デジタルちんぶろぐ

デジタルな話題

ホーム 連絡をする 同期する ( RSS 2.0 ) Login
投稿数  268  : 記事  0  : コメント  4387  : トラックバック  79

ニュース


技術以外は
ちんぶろぐ

記事カテゴリ

書庫

日記カテゴリ

ネタ元

比較と代入の演算子
比較と代入の演算子

 

    int i = 1;
    int j = 0;

    Function((j = i) == i);

※C#でのvarに相当するautoを実装したコンパイラが手元になかったのとコンパイル結果をわかりやすくするためにCで書きました

こんなソースコードを

gcc -O3

で最適化付きでコンパイルするとこんなコードになります(GCC 4.3.2 on Debian 5.0 AMD64)。

func:
.LFB2:
    movl    $1, %edi
    xorl    %eax, %eax
    jmp    Function

変数なんてありゃしない。比較だってありゃしない。

だってコンパイル時に結果はわかっているんだから。

C#版でリリース版(って最適化かかってるんですよね?)でもceqって比較命令が何故出ていたのか謎。

投稿日時 : 2009年4月21日 1:13

コメント

# re: [C] 比較も代入も無いです。 2009/04/21 8:56 刈歩 菜良 CTP
ま、それは実装の話で仕様とは別件。っと、

# 悩める新人ちゃんのために一応コメントを残しときます。(^_-)

# re: [C] 比較も代入も無いです。 2009/04/21 9:28 あんどちん
> ま、それは実装の話で仕様とは別件。っと、
勿論承知です。
普段C/C++のコンパイラの出力見てるとC#のIL出力が気に入らなかっただけですよ。「もっと最適化せいよ!」と


> 悩める新人ちゃん
ぼくのためにありがとうございます

# re: [C] 比較も代入も無いです。 2009/04/21 12:18 なちゃ
JITの領分の仕事になってるんじゃないですか?

# re: [C] 比較も代入も無いです。 2009/04/21 13:29 あんどちん
> JITの領分の仕事になってるんじゃないですか?
僕も「そうなのかなー」と思うのですが、よくわからないので具体的にJITでどのように比較が消えるのか教えていただけると嬉しいのですが。
この最適化ができるなら
・何でコンパイラにも最適化があるのか
・コンパイラで最適化するところとJITが行うところの境界
が分かりにくいというか。
JITが比較命令そのものを消してしまうほど賢いのであればコンパイラのオプティマイザ並ということですよね。VMが常に比較命令のソースとデスティネーションの実態を常にチェックしているとすると通常の比較のパフォーマンスが落ちてしまいますし。最初にネイティブコードに落ちるときに最適化しているのかな?
同じものの比較であれば真になるというのは単純なパターンマッチングでできるから。

確かにVM(JIT)のある言語ならコンパイラでガリガリに最適化しなくてもホットスポット最適化等を頑張れば良いような気もしますね。


# re: [C] 比較も代入も無いです。 2009/04/21 15:03 渋木宏明(ひどり)
> JITの領分の仕事になってるんじゃないですか?

この場合はリテラレル同士の比較なので、C# コンパイラの段階で比較まで除去しちゃってもいいような気がします。

僕も「JIT の段階で消えてほしい」とは思いますが、JIT って、インスタンスがリテラルかどうかまで知ってるのかなぁ?

現実的には「定数同士の比較なんて、普通しないだろ」ってことで、C# コンパイラがそこまで頑張ってない、ってのがホントのところかなぁ?

const int i = 0;
const int j = 0;

あるいは

readonly int i = 0;
readonly int j = 0;

で結果が異なるようなら「やるじゃん」って感じ?


# re: [C] 比較も代入も無いです。 2009/04/21 22:52 あんどちん
良く知らないんだけど.NETって定数比較でも定数2つをスタックに積んで比較するんですよね?
であれば、JITは2個の定数をスタックに積む場合それらが一致するかしないかを判断し無条件分岐(サブルーチンコール)にするということになりますね。
(条件不成立は次の命令への分岐と解釈し最適化が可能でしょう)
何となくできそうですね。



# re: [C] 比較も代入も無いです。 2009/04/22 2:03 中博俊
x64 C# 実行時コード
int i = 1;
0000002f mov dword ptr [rsp+20h],1
int j = 0;
00000037 mov dword ptr [rsp+24h],0

func((j = i) == i);
0000003f mov eax,dword ptr [rsp+20h]
00000043 mov dword ptr [rsp+24h],eax
00000047 xor ecx,ecx
00000049 mov eax,dword ptr [rsp+20h]
0000004d cmp dword ptr [rsp+20h],eax
00000051 sete cl
00000054 mov dword ptr [rsp+28h],ecx
00000058 movzx ecx,byte ptr [rsp+28h]
0000005d call FFFFFFFFFFEC9300
00000062 nop


x86 C#
int i = 1;
0000003a mov dword ptr [ebp-40h],1
int j = 0;
00000041 xor edx,edx
00000043 mov dword ptr [ebp-44h],edx

func((j = i) == i);
00000046 mov eax,dword ptr [ebp-40h]
00000049 mov dword ptr [ebp-44h],eax
0000004c mov eax,dword ptr [ebp-44h]
0000004f cmp eax,dword ptr [ebp-40h]
00000052 sete cl
00000055 movzx ecx,cl
00000058 call FFE2B008
0000005d nop



# re: [C] 比較も代入も無いです。 2009/04/22 2:06 中博俊
x64 リリース

int i = 1;
00000000 mov qword ptr [rsp+8],rcx
00000005 sub rsp,38h
00000009 mov dword ptr [rsp+20h],0
00000011 mov rax,7FF001E1F28h
0000001b mov eax,dword ptr [rax]
0000001d test eax,eax
0000001f je 0000000000000026
00000021 call FFFFFFFFF237ED30
00000026 mov dword ptr [rsp+20h],1
int j = 0;

func((j = i) == i);
0000002e xor ecx,ecx
00000030 mov eax,dword ptr [rsp+20h]
00000034 cmp dword ptr [rsp+20h],eax
00000038 sete cl
0000003b mov dword ptr [rsp+24h],ecx
0000003f movzx ecx,byte ptr [rsp+24h]
00000044 call FFFFFFFFFFEC9300



x86 リリース

int i = 1;
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,8
00000006 mov dword ptr [ebp-4],ecx
00000009 cmp dword ptr ds:[00279200h],0
00000010 je 00000017
00000012 call 5ED39201
00000017 xor edx,edx
00000019 mov dword ptr [ebp-8],edx
0000001c mov dword ptr [ebp-8],1
int j = 0;

func((j = i) == i);
00000023 mov eax,dword ptr [ebp-8]
00000026 cmp eax,dword ptr [ebp-8]
00000029 sete cl
0000002c movzx ecx,cl
0000002f call dword ptr ds:[00279BD8h]

}


# re: [C] 比較も代入も無いです。 2009/04/22 7:18 Azulean
x86リリース。Visual Studioからデバッグなしで実行。
Debugger.Break()で止まったところをアタッチして逆アセンブル結果を表示。
(もしくはオプションから「モジュールの読み込み中に JIT 最適化を抑制する」のチェックをOFFにしておく)

static void Main(string[] args)
{
int i = 1;
00000000 push ebp
00000001 mov ebp,esp
int j = 0;

Console.WriteLine((j = i) == i);
00000003 call 7841D280
00000008 mov ecx,eax
0000000a mov edx,1
0000000f mov eax,dword ptr [ecx]
00000011 call dword ptr [eax+000000B8h]

Debugger.Break();
00000017 call 7891CF04
0000001c pop ebp
}
0000001d ret

# re: [C] 比較も代入も無いです。 2009/04/22 7:31 Azulean
参考

JIT コンパイル時にどのように最適化が施されるのか
http://social.msdn.microsoft.com/forums/ja-JP/netfxgeneralja/thread/3e3b7cbd-16b5-4bcb-95a2-8ab4a5f7c139/



# re: [C] 比較も代入も無いです。 2009/04/22 23:46 あんどちん
中さんとAzuleanさんの結果が違うのが不思議。そこ見たら書いてあるのかな?
今度読んでみよう。


# re: [C] 比較も代入も無いです。 2009/04/23 0:11 Azulean
Visual Studioのデフォルトの設定で、デバッグ実行すると、JITの最適化が走らないんですよ。
ですので、先に書いた私のコードでも、デバッグ実行だと下記のような結果になります。

x86リリース + VSでデバッグ実行(=JIT最適化抑制状態)
static void Main(string[] args)
{
int i = 1;
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,8
00000006 mov dword ptr [ebp-4],ecx
00000009 cmp dword ptr ds:[00B98C9Ch],0
00000010 je 00000017
00000012 call 763191F1
00000017 xor edx,edx
00000019 mov dword ptr [ebp-8],edx
0000001c mov dword ptr [ebp-8],1
int j = 0;
Console.WriteLine((j = i) == i);
00000023 mov eax,dword ptr [ebp-8]
00000026 cmp eax,dword ptr [ebp-8]
00000029 sete cl
0000002c movzx ecx,cl
0000002f call 759E2ED4

Debugger.Break();
00000034 call 75A3BA1C
}
00000039 nop
0000003a mov esp,ebp
0000003c pop ebp
0000003d ret

# re: [C] 比較も代入も無いです。 2009/04/24 0:45 あんどちん
> Visual Studioのデフォルトの設定で、デバッグ実行すると、JITの最適化が走らないんですよ。

成程。納得しました。


Post Feedback

タイトル
名前
Url:
コメント: