C ++ / CLI本机类中的函数是否已编译为MSIL或本机x64机器代码?

时间:2019-03-22 09:31:36

标签: c++ optimization c++-cli thunk

此问题与我的另一个问题Calling MASM PROC from C++/CLI in x64 mode yields unexpected performance problems有关。我没有收到任何评论和答案,但是最终我发现自己是由托管函数调用非托管函数时编译器插入的函数重击引起的,反之亦然。我不再赘述,因为今天我将不再关注这种调整机制的另一个结果。

为提供该问题的一些信息,我的问题是为了提高性能,用MASM64文件中的函数替换了非托管C ++ / CLI类中64位至128位无符号整数乘法的C ++函数。 ASM的替换非常简单:

AsmMul1 proc ; ?AsmMul1@@$$FYAX_K0AEA_K1@Z

; ecx  : Factor1
; edx  : Factor2
; [r8] : ProductL
; [r9] : ProductH

mov  rax, rcx            ; rax = Factor1
mul  rdx                 ; rdx:rax = Factor1 * Factor2
mov  qword ptr [r8], rax ; [r8] = ProductL
mov  qword ptr [r9], rdx ; [r9] = ProductH
ret

AsmMul1 endp

我期望通过使用简单的CPU MUL指令将编译的函数替换为四个32到64位乘法来提高性能。最大的惊喜是ASM版本的速度(!)比C ++版本慢四倍。经过大量研究和测试,我发现C ++ / CLI中的某些函数调用涉及thunk,这显然是一件很复杂的事情,比thunk函数本身要花更多的时间。

在详细了解此thunking之后,发现无论何时使用编译器选项/clr,所有函数的调用约定都将默默更改为__clrcall,这意味着它们成为托管功能。例外情况是使用编译器内部函数,内联ASM以及通过dllimport调用其他DLL的函数-正如我的测试所揭示的那样,这似乎包括调用外部ASM函数的函数。

只要所有交互功能都使用__clrcall约定(即受管理),就不涉及任何重击,并且所有操作均会顺利进行。一旦在任意一个方向上越过了可管理/不可管理边界,就会发生重击,并且性能会严重下降。

现在,经过漫长的序幕,让我们进入我的问题的核心。据我了解__clrcall约定和/clr编译器开关,以这种方式在非托管C ++类中标记函数会导致编译器发出MSIL代码。我已经在__clrcall的文档中找到了这句话:

  

将功能标记为__clrcall时,您指示该功能   实现必须是MSIL,并且本机入口点函数   将不会生成。

坦白说,这吓到我了!毕竟,为了获得真正的本机代码,即超快速的x64机器代码,我正在经历编写C ++ / CLI代码的麻烦。但是,这似乎不是mixed assemblies的默认设置。如果我弄错了,请纠正我:如果我使用的是VC2017给出的项目默认值,则我的程序集包含MSIL,它将被JIT编译。是真的吗?

有一个#pragma managed似乎抑制了MSIL的生成,而倾向于基于每个函数使用本机代码。我已经对其进行了测试,并且可以正常工作,但是问题是,只要本机代码调用托管函数,thunking就会再次受阻,反之亦然。在我的C ++ / CLI项目中,我发现没有办法在不影响性能的情况下配置thunk和代码生成。

所以我现在要问自己:首先使用C ++ / CLI有什么意义?当所有内容仍编译为MSIL时,它是否给我带来性能优势?也许最好用纯C ++编写所有内容并使用Pinvoke调用这些函数?我不知道,我有点卡在这里。

也许有人可以阐明这个糟糕透顶的话题...

0 个答案:

没有答案