我有一个C ++方法签名,如下所示:
static extern void ImageProcessing(
[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
int inYSize, int inXSize);
我将函数包含在内部和外部的计时方法中。在内部,该功能运行在0.24s。在外部,该功能运行2.8秒,或慢约12倍。这是怎么回事?编组是否会让我放慢那么多?如果是的话,我该如何解决这个问题呢?我应该去不安全的代码并使用指针或什么?关于额外的时间成本来自何处,我有点沮丧。
答案 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层的最高效方法是
与任何“这将更快”的答案一样,您需要分析这是您自己的代码库。在考虑和分析了几种方法后,我们才得出这个解决方案。
答案 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倍肯定在我老板的可接受范围内。