bitの初期化は「0代入」と「^(XOR)演算」どちらが速い?

投稿者: Anonymous

bit演算について色々調べてみると、Wikipediaでこのような文を発見しました。

多くのアーキテクチャでは、ゼロという値をロードしてレジスタに格納するよりもXORを行う方がCPUクロックサイクルを消費せず、また命令長も短いためメモリを節約できる。

同じレジスタを指定したXOR命令を実行して同じレジスタに戻すことでその内容をゼロにすることができる。

例えば、

unsigned int hogeBit;

という変数があるものとし、色々bitを弄った後であるとすると、

hogebit = 0;

よりも

hogeBit ^= hogeBit;

のほうがbitの0化のメモリの負担が少ないと述べていると認識しています。
ここでいう「多くのアーキテクチャ」というのがいつまでの時代のものを指すのかいまいち把握できてはいませんが、これは現在でも有効な手法なのでしょうか。

「有効」というのは「差が多かれ少なかれ負担が少ない」という解釈でお願いいたします。

よろしくお願いいたします。

解決

CPUがZ80やi8086などが主流だったころ、同じレジスタどうしをXORするとゼロになるというテクニックはよく利用されていました。

x86でeaxレジスタをゼロクリアする例です。

B8 00 00 00 00   mov         eax,0
33 C0            xor         eax,eax

xorの方がバイナリが小さいですし、実行に必要なCPUサイクルも少なかったと思います。

次のようなCのコードを試してみました。

volatile short x;
x = 0;
printf("%dn", x);

変数にゼロを代入しています。このコンパイル結果は以下の通りです。(結果はコンパイラによって異なります)

33 C0            xor         eax,eax 
66 89 04 24      mov         word ptr [esp],ax 

ゼロを代入するコードを書いたのに、コンパイラが自動的にxorを行うバイナリを生成してしまいました。

あと、直接は関係ありませんが、代入先の変数は16ビット(short)なのにレジスタは32ビット(eax)が使用されています。メモリに書き込む時点で16ビット修飾(word ptr)されています。なかなか興味深いですが、このやり方が最も効率的だと、コンパイラが判断したのでしょう。

以上のように、機械語レベルでどんな命令が採用されるかは、コンパイラが決定します。Cのソースがゼロ代入でも、コンパイラがXORに変更してしまう、といった具合です。ずっと昔に有効だった細々したテクニックは、今のコンパイラには実装済みなのです。

今でも、組み込み向けマイコン(マイクロコントローラ)では、もしかしたら有効なテクニックかもしれません。それでも、アセンブリ言語に限った話です。現代のコンパイラを使用したプログラミングであれば、人間にとって分かりやすい書き方をすれば、自動的に最良のバイナリが生成されます。無理して古い時代の裏技を使おうとすると、かえって悪い結果を招く恐れがあります。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *