.NET编组速度

时间:2009-01-19 20:48:51

标签: .net c++ performance pinvoke marshalling

我有一个C ++方法签名,如下所示:

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

我将函数包含在内部和外部的计时方法中。在内部,该功能运行在0.24s。在外部,该功能运行2.8秒,或慢约12倍。这是怎么回事?编组是否会让我放慢那么多?如果是的话,我该如何解决这个问题呢?我应该去不安全的代码并使用指针或什么?关于额外的时间成本来自何处,我有点沮丧。

3 个答案:

答案 0 :(得分:3)

看看this article。虽然它的重点是Compact Framework,但一般原则也适用于桌面。分析部分的相关引用如下:

  

托管调用不直接调用本机方法。相反,它调用JITted存根方法,该方法必须执行一些开销例程,例如确定GC Preemption状态的调用(以确定GC是否挂起,我们需要等待)。一些编组代码也可能会被JIT打入存根。这都需要时间。

编辑:另外值得一读this blog article on perf of JITted code - 再次,CF具体,但仍然相关。还有an article covering call stack depth and its impact on perf,虽然这可能是CF特定的(未在桌面上测试)。

答案 1 :(得分:2)

您是否尝试将两个数组参数切换为IntPtr?当编组签名中的所有类型都是blittable时,PInvoke绝对是最快的。这意味着Pinvoke归结为仅仅是memcpy来获取数据来回。

在我的团队中,我们发现管理PInvoke层的最高效方法是

  1. 保证马歇尔的一切都是幸福的
  2. 通过根据需要操作IntPtr类来支付价格来手动Marshal类型(如数组)。这非常简单,因为我们有许多包装器方法/类。
  3. 与任何“这将更快”的答案一样,您需要分析这是您自己的代码库。在考虑和分析了几种方法后,我们才得出这个解决方案。

答案 2 :(得分:0)

遗憾的是,答案远比这些建议更为平凡,尽管它们确实有所帮助。基本上,我搞砸了我的计时方式。

我使用的时间码是:

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

此代码专用于intel编译器,旨在提供极其精确的时间测量。不幸的是,这种极高的精度意味着每次运行大约需要2.5秒的成本。删除时间码删除了时间限制。

但是运行时间似乎仍有延迟 - 代码会在开启时间码时报告0.24秒,现在报告的时间约为0.35秒,这意味着速度成本约为50%。

将代码更改为:

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

并称之为:

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

将可执行时间减少到0.3秒(三次运行的平均值)。对我的口味来说仍然太慢,但速度提高10倍肯定在我老板的可接受范围内。