大量の、互いに独立なビットフラグをチェックし、 それぞれフラグが立っていたら(あるいは立っていなかったら) ある特定の処理を行う、 というコードを書くことはよくある。
「イベントループ」と言われるものの一番コアなループは、 結局このように作られている。 たとえば「シフトキーを押しながらマウスをクリック」 などという状態を表現するには、 シフトキー、マウスのクリックのそれぞれに対してビットを割り当て、 その状態を監視して、 必要な組み合わせに対してコールするべき関数を決定するわけだ。
当然、この場合問題になるのが、 どうやって「どの1ビットが立っているのか」を高速に検出するかである。
例えば 16 種類のイベントがあるなら、 このイベントフラグは全部で 16 bit のはずなので、 大抵の場合どのイベントが成立しているのかを調べる「フラグチェック」は
for ( i = 0; i < 16; i++ ) {
event = flag & ( 1 << i );
switch ( event ) {
case HOGE: .... break;
case HAGE: .... break;
.....
default:break;
}
}
のようなプログラムで実装されることになる。
しかし、これだとイベントがたった1つでも16個のフラグ全部を
丁寧に見て回ることになる。
イベントの数が増え、かつその発生率が低い場合、
これは効率が悪い。
ある整数のレジスター x の最も低位のビットを返す技法がある。
この技法を応用すると、この「フラグチェック」ルーチンは次のようになる。
while ( flag ) {
event = flag & (( ~flag ) + 1 );
flag ^= event;
switch( event ) {
.....
}
}
このコードの場合、
ループ回数は純粋に 1 が立っているフラグの数だけ、
つまりイベントの数だけに制限される。
ちなみに switch 文の中身は、
binary tree を用いる方法である程度高速化は可能だが、
圧倒的に有利な方法は見つかっていない。
ちなみにこの逆、
つまり単純な算術演算と論理演算だけを用いて
MSB に最も近いビットのみを 1 にする方法は見つかっていない。
このためと、switch() 文高速化のために、
「MSB に最も近いビットの bit id を返す命令」
を備えた CPU は存在する(PowerPC もその一つだ)。