P / Invoke和C ++ Wrappers之间的性能差异

在学习P / Invoke的过程中,我问过上一个问题:

如何在涉及指针时进行P / Invoke

但是,我不太明白在C#中使用P / Invoke而不是在托管C ++中创建包装器的含义。 在C#中使用P / Invoke创建相同的DLL肯定会导致更清晰的接口,因为我可以在嵌入式资源上使用DLLImport,但是我自己编组的本机DLL的托管C ++包装器会有更好的性能吗?

C ++包装器应该更快,看看这个MSDN页面 :

C ++ Interop使用最快的数据封送方法,而P / Invoke使用最强大的方法。 这意味着C ++ Interop(以典型的C ++方式)默认提供最佳性能,程序员负责解决此行为不安全或不合适的情况。

所以基本上主要的原因是P / Invoke确实固定,blitting,错误检查,而C ++ interop只是推送堆栈上的参数并调用函数。

另一点需要记住的是,C ++可以在一次调用中调用多个API,而P / Invoke通过地址传递的每个参数都会在每次调用时被固定和取消固定,复制并复制回来等。

你会获得更好的表现吗? 取决于你正在做什么以及你是如何做的。 一般来说,您的性能影响更可能来自管理/非托管转换,而且您可以更好地削减更多。 理想情况下,您与非托管代码的接口应该是厚实的而不是健谈的。

假设你有一个非托管代码,它包含几千个对象的集合。 您可以将这样的API公开给托管代码:

int GetFooCount(); IntPtr GetFoo(int n); void ReleaseFoo(IntPtr p); 

这一切都很好,直到你开始在C#中使用它,就像这样:

 int total = API.GetFooCount(); IntPtr[] objects = new IntPtr[total]; for (int i=0; i < total; i++) { objects[i] = GetFoo(i); } // and later: foreach (IntPtr p in objects) { ReleaseFoo(p); } 

总计== 1000,将是4002个托管/非托管转换。 如果你有这个:

 int GetFooCount(); void GetFoos(IntPtr[] arr, int start, int count); void ReleaseFoos(IntPtr arr, int start, int count); 

那么你可以用6个过渡做同样的工作。 您认为哪个会表现得更好?

当然,下一个要问的重要问题是“这种性能提升是否值得?” 所以记得先测量一下。

您应该注意的一件事是,当您使用托管C ++时,STL会发生有趣的事情。 我有一些非托管库代码恰好使用STL。 我的经验是,如果我曾经触及过托管C ++中的任何STL类型,那么所有这些类型都成为托管实现。 最终结果是低级代码在迭代列表时进行托管/非托管转换。 让人惊讶。 我通过永远不会将STL类型暴露给托管C ++来解决这个问题。

根据我们的经验,如果你能够做到这一点,那么去C# - >托管C ++包装器 - >静态库要好得多(如果可能的话)。