アカウント名:
パスワード:
馬鹿なストーリー採用してんなよアホか
「ふーん、ちなみに ++x と x++ の比較と、*++x と *x++ の比較では事情が違うよね?」と振って、相手が反応するかどうかで話を聞く価値があるかどうか値踏みできそうな気がする。でも、組み込みとかデバドラとかのレイヤのコーディングしてる人とか、HPCの人とか以外は、パフォーマンスのボトルネックのありかがよそにあるので、こんな些細な話はそもそもあまり気にしないのではないのかな。
多分、期待されているような差は無いかと。
例えば、以下の2つは同じコードにコンパイルされる。INC1はINC2の形に最適化されてループは消える。
int _X=0;int *x=&X;void INC1(){ *x = 0; for(int i = 0; i < 3; i ++){ (*x)++; }}
void INC2(){ *x = 3;}
いやまて、
void OUTPUT(){ while(true){ std::cout << *x << std::endl; }}
なる関数が別スレッドで走っていたら、0と3以外、1や2も表示されるべきなんだから、いきなり3を代入するんじゃなくて、*xを徐々に0~3まで増やして貰わないと困る、という例だと理解したけど、それでプログラマが困ろうが、C++コンパイラはやっちゃう。0か3しか表示されないコードが出力される。
「たまたまINC1を実行しているスレッドが一切CPUを手放さずにループを終えた後、ようやくOUTPUTが実行された」というのは、あり得る話っすよね?あり得るんだったら、たまたま毎回そうなってるだけっすよ? とすっとぼけるのが、最適化コンパイラの立場。コンピュータに、CPUの1クロック未満で複数処理を終えれる「超高速マジカル計算デバイス」が積んであってもユーザとしては困らないし、そのデバイスが、0+1、1+1、2+1の演算と、*xに1~2が入ってる状態を、観測不能な一瞬で計算してくれても構わない。C++コンパイラは、そう言う謎のデバイスがあるかのような機械語を吐く。
OUTPUT関数のようなものに備えるてこの手の最適化を施さないと、もし、そのようなOUTPUT関数が無かった場合には、INC1のループの実行は無駄になる。無いかも知れないものに備えて念のため遅くなる、という発想はC++には無い。備える必要があって備えて欲しいなら、「備えろ」と明示しないとダメ。
詳しくは、「C++ メモリモデル」辺りでググれば出てくるけど、実情はもっと楽しい。ついでに、C++ほどのスピード狂ではない安全安心設計のJavaですら、この手のメモリの不思議は発生しうる。
> いきなり3を代入するんじゃなくて、*xを徐々に0~3まで増やして貰わないと困る、
困るような使い方をしてる時はvolatile を付けましょう。「volatile int* x」なら大丈夫。INC1は毎回加算後にメモリ書き込みされます。
#組み込み系のコードで、割り込みルーチンと本ルーチンで同じ変数を参照するのはよくある話。#で、「正常動作しないので最適化はオフにしておいてください」とかいう説明してるのもたまに見かけたりして…#ちゃんとvolatile付けろよとツッコミを入れたくなります。
C++だと、volatileだとまだ効きが弱くて、「memory_orderほげほげ<int>」とかが必要だったかと。詳しくは>「メモリモデル」[検索]
もちろん、mutexなどのスレッド同期機構に頼っても実現可能ですが、それよりも、メモリモデルに従ってメモリバリアを張った実装の方が一般に動作は速くなります。
より多くのコメントがこの議論にあるかもしれませんが、JavaScriptが有効ではない環境を使用している場合、クラシックなコメントシステム(D1)に設定を変更する必要があります。
物事のやり方は一つではない -- Perlな人
どのコンパイラではどういうコードに落ちてどのプロセッサではどう実行されるみたいな話がないと無意味 (スコア:-1)
馬鹿なストーリー採用してんなよアホか
Re: (スコア:1)
「ふーん、ちなみに ++x と x++ の比較と、*++x と *x++ の比較では事情が違うよね?」
と振って、相手が反応するかどうかで話を聞く価値があるかどうか値踏みできそうな気がする。
でも、組み込みとかデバドラとかのレイヤのコーディングしてる人とか、HPCの人とか以外は、パフォーマンスのボトルネックのありかがよそにあるので、こんな些細な話はそもそもあまり気にしないのではないのかな。
Re:どのコンパイラではどういうコードに落ちてどのプロセッサではどう実行されるみたいな話がないと無意 (スコア:-1)
多分、期待されているような差は無いかと。
例えば、以下の2つは同じコードにコンパイルされる。INC1はINC2の形に最適化されてループは消える。
int _X=0;
int *x=&X;
void INC1(){
*x = 0;
for(int i = 0; i < 3; i ++){
(*x)++;
}
}
void INC2(){
*x = 3;
}
いやまて、
void OUTPUT(){
while(true){
std::cout << *x << std::endl;
}
}
なる関数が別スレッドで走っていたら、0と3以外、1や2も表示されるべきなんだから、
いきなり3を代入するんじゃなくて、*xを徐々に0~3まで増やして貰わないと困る、
という例だと理解したけど、それでプログラマが困ろうが、C++コンパイラはやっちゃう。
0か3しか表示されないコードが出力される。
「たまたまINC1を実行しているスレッドが一切CPUを手放さずにループを終えた後、ようやくOUTPUTが実行された」というのは、あり得る話っすよね?
あり得るんだったら、たまたま毎回そうなってるだけっすよ? とすっとぼけるのが、最適化コンパイラの立場。
コンピュータに、CPUの1クロック未満で複数処理を終えれる「超高速マジカル計算デバイス」が積んであってもユーザとしては困らないし、
そのデバイスが、0+1、1+1、2+1の演算と、*xに1~2が入ってる状態を、観測不能な一瞬で計算してくれても構わない。
C++コンパイラは、そう言う謎のデバイスがあるかのような機械語を吐く。
OUTPUT関数のようなものに備えるてこの手の最適化を施さないと、
もし、そのようなOUTPUT関数が無かった場合には、INC1のループの実行は無駄になる。
無いかも知れないものに備えて念のため遅くなる、という発想はC++には無い。
備える必要があって備えて欲しいなら、「備えろ」と明示しないとダメ。
詳しくは、「C++ メモリモデル」辺りでググれば出てくるけど、実情はもっと楽しい。
ついでに、C++ほどのスピード狂ではない安全安心設計のJavaですら、この手のメモリの不思議は発生しうる。
Re:どのコンパイラではどういうコードに落ちてどのプロセッサではどう実行されるみたいな話がないと無意 (スコア:1)
> いきなり3を代入するんじゃなくて、*xを徐々に0~3まで増やして貰わないと困る、
困るような使い方をしてる時はvolatile を付けましょう。
「volatile int* x」なら大丈夫。INC1は毎回加算後にメモリ書き込みされます。
#組み込み系のコードで、割り込みルーチンと本ルーチンで同じ変数を参照するのはよくある話。
#で、「正常動作しないので最適化はオフにしておいてください」とかいう説明してるのもたまに見かけたりして…
#ちゃんとvolatile付けろよとツッコミを入れたくなります。
Re: (スコア:0)
C++だと、volatileだとまだ効きが弱くて、「memory_orderほげほげ<int>」とかが必要だったかと。詳しくは>「メモリモデル」[検索]
Re: (スコア:0)
Re: (スコア:0)
もちろん、mutexなどのスレッド同期機構に頼っても実現可能ですが、
それよりも、メモリモデルに従ってメモリバリアを張った実装の方が一般に動作は速くなります。