简体   繁体   English

C# 不安全性能与非托管 PInvoke 调用

[英]C# unsafe performance vs unmanaged PInvoke call

I am running an application which is working with bitmap images.我正在运行一个使用 bitmap 图像的应用程序。 Now I am looking for a fast way to swap the "Red" and "Blue" values of a "Format24bppRgb" bitmap image.现在我正在寻找一种快速交换“Format24bppRgb”bitmap 图像的“红色”和“蓝色”值的方法。 In my C# code my first try was to use an unsafe code fragment:在我的 C# 代码中,我的第一次尝试是使用不安全的代码片段:

var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
    ImageLockMode.ReadWrite, bmp.PixelFormat);
unsafe
{
    byte* array = (byte*)bmpData.Scan0.ToPointer();
    byte temp;
    for (int x = 0; x < bmp.Width * bmp.Height * 3; x = x + 3) {
        temp = *(array + x + 2);
        *(array + x + 2) = *(array + x);
        *(array + x) = temp;
    }
}

for my used bitmap sizes this takes around 50-70ms.对于我使用的 bitmap 尺寸,这大约需要 50-70 毫秒。 Now I tried to do the work in an external library (based on C++) with a pinvoke call:现在我尝试使用 pinvoke 调用在外部库(基于 C++)中完成工作:

[DllImport("ByteSwitch.dll")] 
public static extern IntPtr ChangeRB(IntPtr data, int width, int height);

data = ChangeRB(bmpData.Scan0, bmp.Width, bmp.Height);

Which is defined as follows:其定义如下:

extern "C" __declspec(dllexport) void* ChangeRB(void* xArray, int xHeight, int xWidth);

void* ChangeRB(void* array, int height, int width)
{
    unsigned char* _array = (unsigned char*)array;
    char temp;
    for (int x = 0; x < height * width * 3; x = x + 3)
    {
        temp = _array[x + 2];
        _array[x + 2] = _array[x];
        _array[x] = temp;
    }
    return _array;
}

and this call takes around 1ms?这个电话大约需要1毫秒? So I cannot explain the huge performance difference here - or is it really the case that the unmanaged pinvoke is so much faster than the "unsafe" code fragment?所以我无法在这里解释巨大的性能差异 - 或者说非托管 pinvoke 真的比“不安全”代码片段快得多吗?

The performance problem doesn't come from interop nor from C#, it comes from the fact you use bitmap's Width and Height in the loop.性能问题不是来自互操作,也不是来自 C#,它来自您在循环中使用位图的WidthHeight这一事实。 Both internally call a GDI Plus API :两者都在内部调用 GDI Plus API

public int Width {
    get {
        int width; 
 
        int status = SafeNativeMethods.Gdip.GdipGetImageWidth(new HandleRef(this, nativeImage), out width);
 
        if (status != SafeNativeMethods.Gdip.Ok)
            throw SafeNativeMethods.Gdip.StatusException(status);
 
        return width;
    }
}

And note you don't do this in the C/C++ case... you pass precomputed height and width.请注意,您不会在 C/C++ 案例中执行此操作……您传递了预先计算的高度和宽度。 So, if you change the C# version for this:因此,如果您为此更改 C# 版本:

unsafe
{
    byte* array = (byte*)bmpData.Scan0.ToPointer();
    byte temp;
    var max = bmp.Width * bmp.Height * 3;
    for (int x = 0; x < max; x = x + 3) {
        temp = *(array + x + 2);
        *(array + x + 2) = *(array + x);
        *(array + x) = temp;
    }
}

It may run globally even faster.它可能在全球范围内运行得更快。 You can also use a safe version like this:您还可以使用这样的安全版本:

private static void ChangeSafe(Bitmap bmp, BitmapData bmpData)
{
    var array = bmpData.Scan0;
    byte temp;
    var max = bmp.Width * bmp.Height * 3;
    for (var x = 0; x < max; x = x + 3)
    {
        temp = Marshal.ReadByte(array + x + 2);
        Marshal.WriteByte(array + x + 2, Marshal.ReadByte(array + x));
        Marshal.WriteByte(array + x, temp);
    }
}

It's marginally slower but avoids the need for unsafe code.它稍微慢一些,但避免了对不安全代码的需要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM