ゼロと比較する際の int 演算子 != および == について
質問
ゼロか非ゼロかを調べるのに、!=と==は最速の方法ではないことがわかりました。
bool nonZero1 = integer != 0;
xor eax, eax
test ecx, ecx
setne al
bool nonZero2 = integer < 0 || integer > 0;
test ecx, ecx
setne al
bool zero1 = integer == 0;
xor eax, eax
test ecx, ecx
sete al
bool zero2 = !(integer < 0 || integer > 0);
test ecx, ecx
sete al
コンパイラ VC++ 11 最適化フラグ。/O2 /GL /LTCG
これは x86-32 のアセンブリ出力です。x86-32 と x86-64 の両方で、両方の比較の 2 番目のバージョンは ~12% 速かったです。ただし、x86-64 では、命令は同じでしたが (最初のバージョンは 2 番目のバージョンとまったく同じに見えました)、2 番目のバージョンの方がまだ高速でした。
- なぜコンパイラーは x86-32 上でより高速なバージョンを生成しないのでしょうか。
- アセンブリの出力が同じなのに、なぜ x86-64 では 2 番目のバージョンがまだ高速なのですか?
EDIT: ベンチマークコードを追加しました。ZERO:1544ms, 1358ms NON_ZERO:1544ms, 1358ms http://pastebin.com/m7ZSUrcP または http://anonymouse.org/cgi-bin/anon-www.cgi/http://pastebin.com/m7ZSUrcP
注:単一のソースファイルでコンパイルした場合、main.asmがかなり大きくなるので、これらの関数の場所を特定するのは不便でしょう。私は、zero1, zero2, nonZero1, nonZero2 を別のソースファイルにしていました。
EDIT2: VC++11 と VC++2010 の両方をインストールしたどなたか、ベンチマーク コードを実行し、タイミングを投稿していただけませんか。それは確かに VC++11 のバグかもしれません。
どのように解決するのですか?
<ブロッククオートEDIT: OPのアセンブリリストを見て、私のコードを見てみました。私は、これが VS2011 の一般的なバグであるかどうか とは思えません。これは、単にOPのコードのための特別なケースのバグかもしれません。OP のコードをそのまま clang 3.2, gcc 4.6.2, VS2010 で実行したところ、すべてのケースで 最大差分 は ~1% でした。
ソースをコンパイルし、適切な修正を加えて、私の
ne.c
ファイルおよび
/O2
と
/GL
のフラグを設定します。以下はそのソースです。
int ne1(int n) {
return n != 0;
}
int ne2(int n) {
return n < 0 || n > 0;
}
int ne3(int n) {
return !(n == 0);
}
int main() { int p = ne1(rand()), q = ne2(rand()), r = ne3(rand());}
と対応するアセンブリを
; Listing generated by Microsoft (R) Optimizing Compiler Version 16.00.30319.01
TITLE D:\llvm_workspace\tests\ne.c
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB OLDNAMES
EXTRN @__security_check_cookie@4:PROC
EXTRN _rand:PROC
PUBLIC _ne3
; Function compile flags: /Ogtpy
; COMDAT _ne3
_TEXT SEGMENT
_n$ = 8 ; size = 4
_ne3 PROC ; COMDAT
; File d:\llvm_workspace\tests\ne.c
; Line 11
xor eax, eax
cmp DWORD PTR _n$[esp-4], eax
setne al
; Line 12
ret 0
_ne3 ENDP
_TEXT ENDS
PUBLIC _ne2
; Function compile flags: /Ogtpy
; COMDAT _ne2
_TEXT SEGMENT
_n$ = 8 ; size = 4
_ne2 PROC ; COMDAT
; Line 7
xor eax, eax
cmp eax, DWORD PTR _n$[esp-4]
sbb eax, eax
neg eax
; Line 8
ret 0
_ne2 ENDP
_TEXT ENDS
PUBLIC _ne1
; Function compile flags: /Ogtpy
; COMDAT _ne1
_TEXT SEGMENT
_n$ = 8 ; size = 4
_ne1 PROC ; COMDAT
; Line 3
xor eax, eax
cmp DWORD PTR _n$[esp-4], eax
setne al
; Line 4
ret 0
_ne1 ENDP
_TEXT ENDS
PUBLIC _main
; Function compile flags: /Ogtpy
; COMDAT _main
_TEXT SEGMENT
_main PROC ; COMDAT
; Line 14
call _rand
call _rand
call _rand
xor eax, eax
ret 0
_main ENDP
_TEXT ENDS
END
ne2()
を使用した
<
,
>
と
||
演算子は
明らかに
の方がより高価です。
ne1()
と
ne3()
を使用するもので
==
と
!=
という演算子は、それぞれ逆で等価です。
Visual Studio 2011 は
ベータ版で
. 私はこれをバグと見なします。他の 2 つのコンパイラを使った私のテストでは、すなわち
gcc 4.6.2
と
clang 3.2
というように
O2
最適化スイッチによって、私の Windows 7 マシンで行った 3 つのテストすべてにおいて、まったく同じアセンブリが得られました。以下はその要約です。
$ cat ne.c
#include <stdbool.h>
bool ne1(int n) {
return n != 0;
}
bool ne2(int n) {
return n < 0 || n > 0;
}
bool ne3(int n) {
return !(n != 0);
}
int main() {}
はgccで収まる。
_ne1:
LFB0:
.cfi_startproc
movl 4(%esp), %eax
testl %eax, %eax
setne %al
ret
.cfi_endproc
LFE0:
.p2align 2,,3
.globl _ne2
.def _ne2; .scl 2; .type 32; .endef
_ne2:
LFB1:
.cfi_startproc
movl 4(%esp), %edx
testl %edx, %edx
setne %al
ret
.cfi_endproc
LFE1:
.p2align 2,,3
.globl _ne3
.def _ne3; .scl 2; .type 32; .endef
_ne3:
LFB2:
.cfi_startproc
movl 4(%esp), %ecx
testl %ecx, %ecx
sete %al
ret
.cfi_endproc
LFE2:
.def ___main; .scl 2; .type 32; .endef
.section .text.startup,"x"
.p2align 2,,3
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB3:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
call ___main
xorl %eax, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE3:
で、clangで
.def _ne1;
.scl 2;
.type 32;
.endef
.text
.globl _ne1
.align 16, 0x90
_ne1:
cmpl $0, 4(%esp)
setne %al
movzbl %al, %eax
ret
.def _ne2;
.scl 2;
.type 32;
.endef
.globl _ne2
.align 16, 0x90
_ne2:
cmpl $0, 4(%esp)
setne %al
movzbl %al, %eax
ret
.def _ne3;
.scl 2;
.type 32;
.endef
.globl _ne3
.align 16, 0x90
_ne3:
cmpl $0, 4(%esp)
sete %al
movzbl %al, %eax
ret
.def _main;
.scl 2;
.type 32;
.endef
.globl _main
.align 16, 0x90
_main:
pushl %ebp
movl %esp, %ebp
calll ___main
xorl %eax, %eax
popl %ebp
ret
私の提案は、これをバグとして マイクロソフト コネクト .
注:対応するC++コンパイラを使用しても、ここでは大きな変化がないと思うので、Cソースとしてコンパイルしました。
関連
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】変数やフィールドがvoid宣言されている
-
[解決済み] callとapplyの違いは何ですか?
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] static_cast, dynamic_cast, const_cast, reinterpret_cast はいつ使うべきですか?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] C++でintをstringに変換する最も簡単な方法
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] Swift Betaのパフォーマンス:配列のソート
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】C++のGetlineの問題(オーバーロードされた関数 "getline "のインスタンスがない
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み】変数やフィールドがvoid宣言されている