読者です 読者をやめる 読者になる 読者になる

ビット単位の操作はアトミック操作でなく、バイト単位の操作はアトミック操作

(えと色々と間違えでした。詳しくは追記を)

Matzにっき(2010-05-10)
これってなぜなんだっけという事でちょっと自分用に整理兼メモ。
もし間違っていたら教えてもらえると嬉しいです > 有識者の方
 
アトミック操作ってことは操作が1命令になるってことなのかなぁと思って、
それで以下のコード書いてみた。
 

#include <stdio.h>
#include <stdlib.h>

void
bit_write(char *flg)
{
    *flg = *flg | 1;
}

void
byte_write(char *flg)
{
    *flg = 1;
}

void
bit_read(char *flg)
{
    if (*flg & 1)
        printf("true\n");
}

void
byte_read(char *flg)
{
    if (*flg == 1)
        printf("true\n");
}

 
アセンブリ言語で見てみる。以下は差異だけを抜き出した物。
 

bit_write:
	movl    (%eax), %eax
	movl    %eax, %edx
	orl     $1, %edx
	movl    8(%ebp), %eax
	movl    %edx, (%eax)

byte_write:
	movl    $1, (%eax)

bit_read:
	andl	$1, %eax
	testb	%al, %al

byte_read:
	cmpl	$1, %eax

あぁ、なんか複数の命令になってしまっててダメなんだろうなと思った。
書き込み時なんかダメの駄目な所としては、orlした直後に別のスレッドでビット書き込みが行われてしまったりすると、そのビット書き込みは反映されないな、とか。
 
あと、バイト単位にすると他の情報が埋め込めるかも? という話がある。
これは「マーク自体にいろんな情報を持たせられるかも」という意味なのだろうなと理解。
ビットの場合は「マーク済み」「未マーク」という情報しかないけど、1バイトもあると他の状態も保存できたりしそう(良い例は思いつかないけど、Mostly Cuncrrent GCでもそんな話があるし)。
RVALUEのflagsみたいにビット単位で操作してうんぬんという話と混乱してしまった(なぜだ)。
 

追記

コメント欄より

アトミック操作というのは1命令になることではありません。
そのメモリ操作が自分自身を含む全てのプロセッサから見た場合に、同時に処
理されているように見えることが保証された操作のことです。
言い換えると中間状態が見えないことが保証された操作のことです。

例えば x86 系の場合、"addl $1, (memory-address)" はメモリの内容を+1する
1命令ですが、非アトミックです。
アトミックにするには "lock; addl $1, (memory-address)" をつける必要があります。
同様に x86 系には BTS/BTR/BTC 命令があるので、1ビットだけの更新であれば 1 命令で完了可能です。
ただしこれも lock を付けない限り非アトミックです。

ロード・ストア命令も不正境界を跨ぐ場合にはアトミック操作にならないものが多いです。
x86-64 はキャッシュラインを跨いだストア操作がアトミックであることは保証されません。

また一部のアーキテクチャにはバイト単位のメモリアクセスがそもそも1命令で書けないものがあります。
今は亡き DEC Alpha の EV5(21164) 以前の CPU は、
1バイトの2バイトのロード・ストア命令がなく、最小書き込み幅が4バイトになります。

そもそも1命令とアトミック操作は関係ないのでした。勘違いしていました。すいません。
よって、追記前に書かれたことは忘れてください(きっと頭の中からGCされるはず)。
 
んで、振り出しに戻っちゃうのですけど、なぜ「バイト単位の操作はアトミック操作」になるんでしょうね…。
都市伝説?