12

在学习P/Invoke的过程中,我问了这个之前的问题:

涉及指针时如何 P/Invoke

但是,我不太了解在 C# 中使用 P/Invoke 对在托管 C++ 中创建包装器的影响。在 C# 中使用 P/Invoke 创建相同的 DLL 肯定会产生更清晰的界面,因为我可以在嵌入式资源上使用 DLLImport,但是我自己进行封送处理的本地 DLL 的托管 C++ 包装器会有更好的性能吗?

4

2 回答 2

14

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

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

所以基本上主要原因是 P/Invoke 进行固定、blitting、错误检查,而 C++ 互操作只是将参数压入堆栈并调用函数。

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

于 2009-09-16T14:48:29.123 回答
6

你会得到更好的表现吗?取决于你在做什么以及你是如何做的。一般来说,您的性能影响更有可能来自于托管/非托管转换,并且您可以剪掉的转换越多越好。理想情况下,您与非托管代码的接口应该是厚实的而不是喋喋不休的。

假设您有一个包含几千个对象的非托管代码。您可以向托管代码公开这样的 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#->managed C++ wrapper->static library。

于 2009-09-16T14:53:31.767 回答