3

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

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

我已经将该函数包装在内部和外部的计时方法中。在内部,该函数以 0.24s 运行。在外部,该函数运行时间为 2.8 秒,或慢了大约 12 倍。这是怎么回事?编组让我慢了这么多吗?如果是,我该如何解决?我应该去不安全的代码并使用指针还是什么?我对额外的时间成本从何而来感到困惑。

4

3 回答 3

4

看看这篇文章。虽然它的重点是紧凑框架,但一般原则也适用于桌面。分析部分的相关引用如下:

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

编辑:同样值得一读的是这篇关于 JITted 代码性能的博客文章——同样,CF 特定,但仍然相关。还有一篇文章介绍了调用堆栈深度及其对 perf 的影响,尽管这篇文章可能是特定于 CF 的(未在桌面上测试过)。

于 2009-01-19T20:54:47.137 回答
3

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

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

  1. 保证 Marshall'd 的所有内容都是 blittable
  2. 通过根据需要操作 IntPtr 类来为手动编组类型(例如数组)付出代价。这非常简单,因为我们有许多包装方法/类。

与任何“这会更快”的答案一样,您需要分析这是您自己的代码库。我们只是在考虑和分析了几种方法后才得出这个解决方案。

于 2009-01-19T21:06:01.483 回答
1

可悲的是,答案比这些建议要平凡得多,尽管它们确实有帮助。基本上,我搞砸了我的计时方式。

我使用的计时代码是这样的:

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;

此代码特定于英特尔编译器,旨在提供极其精确的时间测量。不幸的是,这种极高的精度意味着每次运行的成本约为 2.5 秒。删除时序代码消除了该时间限制。

不过,运行时间似乎仍然存在延迟——代码将报告 0.24 s 与该计时代码,现在报告大约 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 倍的速度提升肯定在我老板可以接受的范围内。

于 2009-01-19T22:55:26.487 回答