モジュロ演算がコストのかかる演算であることを確認したかったので、特定の数値が偶数かどうかをチェックする次のコードをテストしました。
bool is_even(int n) {
return (n & 1) == 0;
}
次に、これ:
bool is_even_bis(int n) {
return (n % 2) == 0;
}
最初は C# を使用していましたが、実際、logical を使用するコード&
は他のコードよりも高速で、3 倍高速になることもあります。ILSpy を使用すると、MSIL にコンパイルしたときに最適化が行われていないことがわかりました。コードはまったく同じです。
ただし、C の私の友人が発見したようにgcc -O3
、コードを使用すると次のようにコンパイルされます。
is_even:
mov eax, DWORD PTR [esp+4] # tmp63, n
and eax, 1 # tmp63,
xor eax, 1 # tmp63,
ret
と:
is_even_bis:
mov eax, DWORD PTR [esp+4] # tmp63, n
and eax, 1 # tmp63,
xor eax, 1 # tmp63,
ret
基本的に厳密には同じことです。最適化を使用しても-O0
、操作は表示されません。
is_even:
push ebp #
mov ebp, esp #,
mov eax, DWORD PTR [ebp+8] # tmp63, n
and eax, 1 # D.1837,
test eax, eax # D.1837
sete al #, D.1838
movzx eax, al # D.1836, D.1838
pop ebp #
ret
言うまでもなく、コンパイルされたコードは と の間でis_even
も同じです。is_even_bis
-O0
もっと面白いのは、私の別の友人が OCaml を使って同じことを試みたことです:
let is_even x = ((x land 1) == 0)
let _ =
let i = ref 100000000 in
while !i > 0 do
ignore (is_even !i);
decr i
done
と:
let is_even_bis x = ((x mod 2) == 0)
let _ =
let i = ref 100000000 in
while !i > 0 do
ignore (is_even_bis !i);
decr i
done
また、モジュロ バージョンは、バイトコードを実行する場合は高速ですが、ネイティブ コードを実行する場合は遅いようです! 多分誰かがこの謎を説明できますか?
次に、なぜ C# でそのように動作しないのか (2 つの関数の間に明らかなパフォーマンスのギャップがある場合)、なぜ JIT コンパイラが と同じ最適化を適用しないのか疑問に思い始めましたgcc
。JITコンパイラの出力をインターセプトする方法があるかどうかはわかりませんが、理解に役立つでしょうか?
おまけの質問: モジュロは除算に基づいていると思いますが、除算は O(n²) 時間 (n は桁数) で行われるため、モジュロは 2 次時間複雑度を持つと言えますか?