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

When I rebuild my c++ DLL (which I call from c#) into /CLR (originally native) the performance drops in half

当我将 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);
  }
}

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);

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

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

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