P/Invoke与C++/CLI性能对比

在.NET应用程序中,与非托管代码交互是一个常见的需求。P/Invoke(Platform Invocation Services)是实现这一需求的方式之一,它允许.NET代码调用Windows API或其他非托管代码。然而,P/Invoke调用时会有一定的性能开销。为了减少这种开销,另一种方法是使用C++/CLI创建一个托管的包装器。本文将探讨P/Invoke与C++/CLI在性能上的差异。

作者正在开发一个名为SharpGL的OpenGL包装器和场景图库。OpenGL是一个调用频繁的API,每秒可能被调用数千次。在开发过程中,作者想知道是直接使用P/Invoke调用OpenGL函数更快,还是通过C++/CLI类库暴露OpenGL函数更快。为了验证这一点,作者创建了一个名为“TraditionalAPI”的小型API,该API暴露了三个基本函数,并使用不同的方法调用这些函数。

第一部分:传统API

传统API暴露了三个基本函数:

  • 测试函数1:IncrementCounter
  • 测试函数2:Square Root
  • 测试函数3:Dot Product

这些函数分别用于测试P/Invoke调用的开销、计算平方根以及计算两个三元组的点积。在测试函数3中,需要对托管数组进行内存固定,以便在非托管API中直接访问。

第二部分:C++/CLI包装器

解决方案的第二部分是一个C++/CLI包装器,它包装了每个函数。通过这种方式,可以从另一个.NET应用程序中调用这些函数。以下是每个测试函数的包装器示例:

void IncrementCounter() { // 调用非托管函数 ::TA_IncrementCounter(); } double CalculateSquareRoot(double value) { // 调用非托管函数 return ::TA_CalculateSquareRoot(value); } double DotProduct(array^ threeTuple1, array^ threeTuple2) { // 固定数组 pin_ptr p1(&threeTuple1[0]); pin_ptr p2(&threeTuple2[0]); // 调用非托管函数 return TA_DotProduct(p1, p2); }

在这种情况下,需要对托管数组进行固定,以便在非托管API中直接访问它们。

第三部分:C#测试应用程序

解决方案的最后部分是一个C# WPF应用程序,用于运行测试。TraditionalAPI DLL可以单独运行每个测试函数,也可以多次运行每个测试函数。通过这种方式,可以比较以下内容:

  • 在TraditionalAPI中直接运行x次测试所需的时间
  • 通过C++/CLI接口运行x次测试所需的时间
  • 通过P/Invoke运行x次测试所需的时间

结果

以下是运行每个测试10000次和100000次的结果:

根据研究结果,C++/CLI接口的性能并没有比P/Invoke快一个数量级,这与作者的预期不符。即使在百万次函数调用的情况下,C++/CLI接口的性能也仅略高于使用P/Invoke。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485