既然没人想做一些拆卸工作,我就回答自己的问题。
原因似乎是JITs生成的本机代码,而不是注释中提到的数组边界检查或缓存问题。
RyuJIT为
ClampSimple
方法:
vucomiss xmm1,xmm0
jbe M01_L00
vmovaps xmm0,xmm1
ret
M01_L00:
vucomiss xmm0,xmm2
jbe M01_L01
vmovaps xmm0,xmm2
ret
M01_L01:
ret
它使用CPU的本机
ucomiss
要比较的操作
float
也很快
movaps
移动那些
浮动
在CPU寄存器之间。
扩展方法比较慢,因为它有两个函数调用
System.Single.CompareTo(System.Single)
,这是第一个分支:
lea rcx,[rsp+30h]
vmovss dword ptr [rsp+38h],xmm1
call mscorlib_ni+0xda98f0
test eax,eax
jge M01_L00
vmovss xmm0,dword ptr [rsp+38h]
add rsp,28h
ret
让我们看看Mono为
子痫样
方法:
cvtss2sd xmm0,xmm0
movss xmm1,dword ptr [rsp+8]
cvtss2sd xmm1,xmm1
comisd xmm1,xmm0
jbe M01_L00
movss xmm0,dword ptr [rsp+8]
cvtss2sd xmm0,xmm0
cvtsd2ss xmm0,xmm0
jmp M01_L01
M01_L00:
movss xmm0,dword ptr [rsp]
cvtss2sd xmm0,xmm0
movss xmm1,dword ptr [rsp+10h]
cvtss2sd xmm1,xmm1
comisd xmm1,xmm0
jp M01_L02
jae M01_L02
movss xmm0,dword ptr [rsp+10h]
cvtss2sd xmm0,xmm0
cvtsd2ss xmm0,xmm0
jmp M01_L01
M01_L02:
movss xmm0,dword ptr [rsp]
cvtss2sd xmm0,xmm0
cvtsd2ss xmm0,xmm0
M01_L01:
add rsp,18h
ret
Mono的代码转换
floats
到
double
并使用
comisd
. 此外,还有一些奇怪的“转换翻转”
浮动
â
双重的
â
浮动
准备返回值时。而且在内存和寄存器之间还有更多的移动。这解释了为什么Mono的simple方法的代码比RyuJIT的代码慢。
这个
Extension
方法代码与RyuJIT的代码非常相似,但是同样有奇怪的转换翻转
浮动
â
双重的
â
浮动
:
movss xmm0,dword ptr [rbp-10h]
cvtss2sd xmm0,xmm0
movsd xmm1,xmm0
cvtsd2ss xmm1,xmm1
lea rbp,[rbp]
mov r11,2061520h
call r11
test eax,eax
jge M0_L0
movss xmm0,dword ptr [rbp-10h]
cvtss2sd xmm0,xmm0
cvtsd2ss xmm0,xmm0
ret
似乎RyuJIT可以生成更有效的代码来处理
浮动
单核细胞增多症
浮动
作为
双重的
并每次转换这些值,这也会导致CPU寄存器和内存之间的附加值传输。
请注意,所有这些仅对Windows x64有效。我不知道这个基准在Linux或Mac上会如何运行。