私は何も最適化したくありません、私は誓います、私は好奇心からこの質問をしたいだけです. ほとんどのハードウェアには、単一のコマンドであるビット シフトのアセンブリ コマンド (例: shl
、 ) があることを知っています。shr
しかし、シフトするビット数 (ナノ秒単位または CPU タクト単位) は重要ですか。つまり、次のいずれかがどの CPU でも高速ですか?
x << 1;
と
x << 10;
そして、この質問で私を嫌いにならないでください。:)
私は何も最適化したくありません、私は誓います、私は好奇心からこの質問をしたいだけです. ほとんどのハードウェアには、単一のコマンドであるビット シフトのアセンブリ コマンド (例: shl
、 ) があることを知っています。shr
しかし、シフトするビット数 (ナノ秒単位または CPU タクト単位) は重要ですか。つまり、次のいずれかがどの CPU でも高速ですか?
x << 1;
と
x << 10;
そして、この質問で私を嫌いにならないでください。:)
CPUに依存する可能性があります。
ただし、最新のすべての CPU (x86、ARM) は「バレル シフター」を使用します。これは、一定時間内に任意のシフトを実行するように特別に設計されたハードウェア モジュールです。
要するに...いいえ。変わりはない。
一部の組み込みプロセッサには、「1 シフト」命令しかありません。そのようなプロセッサでは、コンパイラx << 3
は((x << 1) << 1) << 1
.
モトローラ MC68HCxx は、この制限のある最も人気のあるファミリの 1 つだったと思います。幸いなことに、そのようなアーキテクチャは現在非常にまれであり、ほとんどの場合、可変シフト サイズのバレル シフターが含まれています。
多くの最新の派生製品を持つ Intel 8051 も、任意の数のビットをシフトすることはできません。
これには多くのケースがあります。
多くの高速 MPU には、一定時間内に任意のシフトを行う、マルチプレクサのような電子回路であるバレル シフタがあります。
MPUに1ビットシフトしかない場合x << 10
、通常は遅くなります。これは、ほとんどが10シフトまたは2シフトでのバイトコピーで行われるためです。
しかし、よりもさらに高速になる既知の一般的なケースがありx << 10
ます。x が 16 ビットの場合、x の下位 6 ビットのみが対象となるため (他のすべてはシフトアウトされます)、MPU は下位バイトのみをロードする必要があるため、8 ビット メモリへのアクセス サイクルは 1 回だけで、アクセス サイクルは 2 回必要です。アクセスサイクルがシフト(および下位バイトのクリア)よりも遅い場合、より高速になります。これは、低速の外部データ RAM にアクセスしながら、高速のオンボード プログラム ROM を備えたマイクロコントローラーに適用される場合があります。x << 1
x << 10
x << 10
x << 10
ケース 3 に加えて、コンパイラは、16x16 の乗算を 16x8 の 1 に置き換える (下位バイトは常にゼロであるため) など、重要なビット数を考慮し、さらなる操作をより狭い幅のものに最適化する場合があります。
一部のマイクロコントローラには左シフト命令がまったくなく、add x,x
代わりに使用されることに注意してください。
これが私のお気に入りの CPUで、x<<2
2 倍の時間がかかりますx<<1
:)
ARM では、これは別の命令の副作用として実行できます。そのため、潜在的には、どちらにもレイテンシはまったくありません.
8 ビット プロセッサでx<<1
は、実際には 16 ビット値の場合よりもはるかに遅くなる可能性があります。x<<10
たとえば、 の合理的な翻訳は次のx<<1
ようになります。
byte1 = (byte1 << 1) | (byte2 >> 7)
byte2 = (byte2 << 1)
一方、x<<10
より単純になります:
byte1 = (byte2 << 2)
byte2 = 0
x<<1
が よりも頻繁に、さらに遠くまで移動することに注目してくださいx<<10
。さらに、結果はx<<10
byte1 の内容に依存しません。これにより、操作がさらに高速化される可能性があります。
これは、CPU とコンパイラの両方に依存します。基礎となる CPU がバレル シフターで任意のビット シフトを行っている場合でも、これはコンパイラーがそのリソースを利用する場合にのみ発生します。
C および C++ では、データの幅 (ビット単位) を超えるものをシフトすることは「未定義の動作」であることに注意してください。符号付きデータの右シフトも「実装定義」です。速度を気にしすぎるのではなく、さまざまな実装で同じ答えが得られることに注意してください。
ANSI C セクション 3.3.7 からの引用:
3.3.7 ビットシフト演算子
構文
shift-expression: additive-expression shift-expression << additive-expression shift-expression >> additive-expression
制約
各オペランドは整数型でなければなりません。
セマンティクス
整数昇格は各オペランドで実行されます。結果の型は、プロモートされた左オペランドの型です。右オペランドの値が負の値であるか、プロモートされた左オペランドのビット幅以上である場合、動作は未定義です。
E1 << E2 の結果は、E1 左シフトされた E2 ビット位置です。空いたビットはゼロで埋められます。E1 が unsigned 型の場合、結果の値は E1 に量を掛けたもので、2 を E2 で累乗し、E1 が unsigned long 型の場合は ULONG_MAX+1 を法として減じたものであり、それ以外の場合は UINT_MAX+1 です。(定数 ULONG_MAX および UINT_MAX はヘッダーで定義されます。)
E1 >> E2 の結果は、E1 を右シフトした E2 ビット位置です。E1 が unsigned 型の場合、または E1 が signed 型で非負の値の場合、結果の値は、 E1 の商を量で割った商の整数部 (2 の E2 乗) になります。E1 に符号付きの型と負の値がある場合、結果の値は実装定義です。
そう:
x = y << z;
"<<": y × 2 z (オーバーフローが発生した場合は未定義);
x = y >> z;
">>":符号付きの実装定義(ほとんどの場合、算術シフトの結果: y / 2 z )。
一部の世代の Intel CPU (P2 または P3 ? AMD ではありませんが、私の記憶が正しければ) では、ビットシフト操作がとてつもなく遅くなります。ただし、加算を使用できるため、1ビットのビットシフトは常に高速である必要があります。考慮すべきもう 1 つの問題は、一定数のビットによるビットシフトが可変長シフトよりも高速かどうかです。オペコードが同じ速度であっても、x86 では、ビットシフトの非定数の右側のオペランドが CL レジスタを占有する必要があります。これにより、レジスタの割り当てに追加の制約が課され、プログラムの速度も低下する可能性があります。