当我将 c++ DLL(我从 c# 调用)重建为 /CLR(最初是本机的)时,性能下降了一半

Tom*_*asz 2 c# c++ dll visual-studio-2013

当我将 DLL 构建选项从本机更改为 /CLR 以便我可以跨 c#/c++ 边界进行调试时,我的性能下降了一半。

我开发了一个具有特定高性能算法的本机 C++ DLL 来解决我的计算问题。我需要将该 DLL 插入到业务合作伙伴开发的 C# 应用程序中,并用它来替换他们性能较低的算法。一切都很顺利,我使用静态包装器调用插入了 DLL,DLL 算法运行良好,显示出比原始算法性能提高了 2 倍,但无法跨边界调试。然后,我将 c++ DLL 构建设置从本机切换到 /CLR,以便能够跨 c#/c++ DLL 边界进行调试,但我的性能下降了一半。

无法弄清楚为什么会这样。

C++ DLL 端:

extern "C"
{
  __declspec(dllexport) void* NewCalc()
  {
    return (void*)new CalcCL;
  }
  __declspec(dllexport) double Calc(void* sCalc, int *Buf, int Cnt)
  {
    return ((CalcCl*)sCalc)->Calc(Buf, Cnt);
  }
}
Run Code Online (Sandbox Code Playgroud)

c# 端:

  [DllImport("CalcDLL.dll", CallingConvention = CallingConvention.Cdecl)]
  public static extern int NewCalc();
  [DllImport("CalcDLL.dll", CallingConvention = CallingConvention.Cdecl)]
  public static extern double Calc(int sCalc, int[] Buf, int Cnt);
...
  int sCalc = NewCalc();
...
  double res;
  int[] MyBuf = new int[1000];
  // <Code that fills MyBuf with target data for algorithm>
  res = Calc(sCalc, MyBuf, 1000);
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 5

对于给定的接口,使用 /clr 进行编译根本没有任何好处。通过右键单击主 C# 项目 > 属性 > 调试选项卡 > 勾选“启用本机代码调试”复选框,使本机 C++ 代码可调试。这同时启用了托管和非托管调试引擎。您无法单步执行本机代码,需要在 Calc() 函数上设置断点才能实现调试引擎更改。如果设置断点很尴尬,您可能仍然喜欢用本机 C++ 编写的单元测试。

您需要注意的另一件事是您正在构建启用“调试”设置的 C++ 代码。现在你明白了,这就是为什么代码看起来慢了一半。仅使用发布构建设置进行性能测试,以便确保优化器已启用。最好的办法是将本机 C++ 项目包含在 C# 解决方案中,以便您始终构建正确的风格。并且您必须确保将正确的 DLL 复制到 C# 主项目的构建目录中,通常通过构建后事件完成。对于调试和发布配置,该复制步骤必须不同。

请注意,[DllImport] 声明不正确。NewCalc() 的返回类型必须是 IntPtr,因此它也适用于 64 位代码。对 C++ 代码的 64 位版本进行性能测试是您想要尝试的其他事情,如果 Calc() 函数使用浮点数学,它可以提高性能。实际上,使用 /clr 可以使界面变得更好,但是您必须学习如何编写 C++/CLI 代码并避免使用 /clr 构建本机 C++ 代码。