Question

Je suis en train de faire des opérations bitmap dangereuses et ont découvert que l'augmentation du pointeur moins temps peut conduire à de grandes améliorations de performance. Je ne sais pas pourquoi est-ce donc, même si vous faites des opérations beaucoup plus 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 de plus de 32 pixels itérer bits avec un UInt32 itérer sur deux pixels avec UInt64 et faire deux fois les opérations dans un cycle.

Ce qui suit, il fait en lisant deux pixels et de les modifier (bien sûr, il échouera avec des images avec une largeur étrange, mais son juste pour tester).

    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 lente que le précédent:

    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 binaires?

Je suis en utilisant le framework .NET 4.

pourrait quelque chose comme ceci est vrai pour C ++?

NB. 32 bit 64 bit vs le rapport entre les deux méthodes est égal, cependant dans les deux sens sont comme 20% plus lente sur 64 vs 32 bits?

EDIT: Comme le suggère Porges et Arul cela pourrait être à cause de diminution du nombre de lectures et la mémoire de branchement en tête

.

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;
            }

Autres conseils

Il est pas incrémenter le pointeur qui est plus lente, mais la lecture de la mémoire. vous faites deux fois plus de lectures avec des unités 32 bits,.

Vous devriez le trouver plus rapidement à nouveau si vous écrivez une fois au lieu de deux fois dans la version 64 bits.

scroll top