J'ai effectué quelques opérations bitmap non sécurisées et j'ai découvert que le fait d'augmenter le temps passé en moins de temps peut conduire à de grandes améliorations de performances. Je ne suis pas sûr pourquoi est-ce le cas, même si vous faites beaucoup plus d'opérations au niveau du bit dans la boucle, il est toujours préférable de faire moins d'itérations sur le pointeur. Ainsi, par exemple, au lieu d'itérer sur des pixels de 32 bits avec un UInt32, itérez sur deux pixels avec UInt64 et faites deux fois les opérations dans un cycle. Ce qui suit le fait en lisant deux pixels et en les modifiant (bien sûr, il échouera avec des images avec une largeur impaire, mais c'est juste pour tester).itération de pointeur non sécurisée et bitmap - pourquoi UInt64 est-il plus rapide?
private void removeBlueWithTwoPixelIteration()
{
// think of a big image with data
Bitmap bmp = new Bitmap(15000, 15000, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
TimeSpan startTime, endTime;
unsafe {
UInt64 doublePixel;
UInt32 pixel1;
UInt32 pixel2;
const int readSize = sizeof(UInt64);
const UInt64 rightHalf = UInt32.MaxValue;
PerformanceCounter pf = new PerformanceCounter("System", "System Up Time"); pf.NextValue();
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* image = (byte*)bd.Scan0.ToPointer();
startTime = TimeSpan.FromSeconds(pf.NextValue());
for (byte* line = image; line < image + bd.Stride * bd.Height; line += bd.Stride)
{
for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
doublePixel = *((UInt64*)pointer);
pixel1 = (UInt32)(doublePixel >> (readSize * 8/2)) >> 8; // loose last 8 bits (Blue color)
pixel2 = (UInt32)(doublePixel & rightHalf) >> 8; // loose last 8 bits (Blue color)
*((UInt32*)pointer) = pixel1 << 8; // putback but shift so A R G get back to original positions
*((UInt32*)pointer + 1) = pixel2 << 8; // putback but shift so A R G get back to original positions
}
}
endTime = TimeSpan.FromSeconds(pf.NextValue());
bmp.UnlockBits(bd);
bmp.Dispose();
}
MessageBox.Show((endTime - startTime).TotalMilliseconds.ToString());
}
Le code suivant qu'il fait pixel par pixel et est environ 70% plus lent que la précédente:
private void removeBlueWithSinglePixelIteration()
{
// think of a big image with data
Bitmap bmp = new Bitmap(15000, 15000, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
TimeSpan startTime, endTime;
unsafe
{
UInt32 singlePixel;
const int readSize = sizeof(UInt32);
PerformanceCounter pf = new PerformanceCounter("System", "System Up Time"); pf.NextValue();
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* image = (byte*)bd.Scan0.ToPointer();
startTime = TimeSpan.FromSeconds(pf.NextValue());
for (byte* line = image; line < image + bd.Stride * bd.Height; line += bd.Stride)
{
for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
singlePixel = *((UInt32*)pointer) >> 8; // loose B
*((UInt32*)pointer) = singlePixel << 8; // adjust A R G back
}
}
endTime = TimeSpan.FromSeconds(pf.NextValue());
bmp.UnlockBits(bd);
bmp.Dispose();
}
MessageBox.Show((endTime - startTime).TotalMilliseconds.ToString());
}
Quelqu'un pourrait-il expliquer pourquoi incrémente le pointeur d'une opération plus coûteuse que de faire quelques opérations au niveau du bit? J'utilise framework .NET 4.
Est-ce que quelque chose comme ceci pourrait être vrai pour C++?
NB. 32 bits vs 64 bits le ratio des deux méthodes est égal, mais les deux façons sont comme 20% plus lent sur 64 vs 32 bits?
EDIT: Comme suggéré par Porges et arul, cela pourrait être dû à une diminution du nombre de lectures en mémoire et à un surdébit de branchement.
EDIT2:
Après quelques essais, il semble que la lecture de la mémoire moins de temps est la réponse:
Avec ce code en supposant que la largeur de l'image est divisible par 5, vous obtenez 400% plus rapide:
[StructLayout(LayoutKind.Sequential,Pack = 1)]
struct PixelContainer {
public UInt32 pixel1;
public UInt32 pixel2;
public UInt32 pixel3;
public UInt32 pixel4;
public UInt32 pixel5;
}
Ensuite, utilisez ceci:
int readSize = sizeof(PixelContainer);
// .....
for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
multiPixel = *((PixelContainer*)pointer);
multiPixel.pixel1 &= 0xFFFFFF00u;
multiPixel.pixel2 &= 0xFFFFFF00u;
multiPixel.pixel3 &= 0xFFFFFF00u;
multiPixel.pixel4 &= 0xFFFFFF00u;
multiPixel.pixel5 &= 0xFFFFFF00u;
*((PixelContainer*)pointer) = multiPixel;
}
Est-ce sur une machine/processus 32 bits ou 64 bits? – Justin
C'est sur un Windows 7 64 bits, mais j'ai essayé avec 32 bits binaires et 64 bits, l'augmentation des performances est la même avec la première méthode ... En fait, le faire avec un processus 64 bits est plus lent dans les deux cas. –