私はWindowsとLinux(x86-64)の両方でプログラムを実行しています。これは同じコンパイラ(Intel Parallel Studio XE 2017)で同じオプションでコンパイルされており、WindowsのバージョンはLinuxのものより3倍高速です。原因は、std::erfへの呼び出しで、どちらの場合でもIntel数学ライブラリで解決されます(デフォルトではWindowsでは動的に、Linuxでは静的にはLinuxで動的リンクを使用すると同じパフォーマンスが得られます)。intelコンパイラを使用したWindowsとLinuxのパフォーマンスの差異:アセンブリの見方
ここでは、問題を再現するための簡単なプログラムを示します。
#include <cmath>
#include <cstdio>
int main() {
int n = 100000000;
float sum = 1.0f;
for (int k = 0; k < n; k++) {
sum += std::erf(sum);
}
std::printf("%7.2f\n", sum);
}
このプログラムをvTuneでプロファイルすると、WindowsとLinuxバージョンのアセンブリが少し違うことがわかります。ここで呼び出しサイト(ループ)は、Windows
Block 3:
"vmovaps xmm0, xmm6"
call 0x1400023e0 <erff>
Block 4:
inc ebx
"vaddss xmm6, xmm6, xmm0"
"cmp ebx, 0x5f5e100"
jl 0x14000103f <Block 3>
およびLinux上でWindowsの
Block 1:
push rbp
"sub rsp, 0x40"
"lea rbp, ptr [rsp+0x20]"
"lea rcx, ptr [rip-0xa6c81]"
"movd edx, xmm0"
"movups xmmword ptr [rbp+0x10], xmm6"
"movss dword ptr [rbp+0x30], xmm0"
"mov eax, edx"
"and edx, 0x7fffffff"
"and eax, 0x80000000"
"add eax, 0x3f800000"
"mov dword ptr [rbp], eax"
"movss xmm6, dword ptr [rbp]"
"cmp edx, 0x7f800000"
...
で呼び出さERF関数の先頭にある、コードは少し異なっています。呼び出しサイトは次のとおりです。
Block 3
"vmovaps %xmm1, %xmm0"
"vmovssl %xmm1, (%rsp)"
callq 0x400bc0 <erff>
Block 4
inc %r12d
"vmovssl (%rsp), %xmm1"
"vaddss %xmm0, %xmm1, %xmm1" <-------- hotspot here
"cmp $0x5f5e100, %r12d"
jl 0x400b6b <Block 3>
と呼ばれる関数(ERF)の始まりです:私は時間がLinux上で失われた2点を示している
"movd %xmm0, %edx"
"movssl %xmm0, -0x10(%rsp)" <-------- hotspot here
"mov %edx, %eax"
"and $0x7fffffff, %edx"
"and $0x80000000, %eax"
"add $0x3f800000, %eax"
"movl %eax, -0x18(%rsp)"
"movssl -0x18(%rsp), %xmm0"
"cmp $0x7f800000, %edx"
jnl 0x400dac <Block 8>
...
。
誰も私に2つのコードの違いを説明するのに十分なアセンブリを理解していますか?なぜLinuxバージョンが3倍遅くなっているのですか?
ハードウェアは同じですか? – Leon
はい、同じハードウェアです。私はWindowsとLinuxの両方のコアi7 Haswell、WindowsとLinuxの両方のXeon Broadwellでこのケースをテストしました。同じ結果。 コアi7では、macOSでもテストしましたが、速度はWindows版と同じです。 – InsideLoop
Linuxは仮想マシンで動作しますか? – Leon