32 ビット CPU モード以降、x86 アーキテクチャで使用できる拡張アドレス オペランドがあります。ベースアドレス、ディスプレースメント、インデックスレジスタ、スケーリングファクタを指定できます。
たとえば、32 ビット整数のリスト (32 バイト長のデータ構造の配列の最初の 2 つ%rdi
をデータ インデックス%rbx
として、ベース ポインターとして) をストライドしたいとします。
addl $8, %rdi # skip eight values: advance index by 8
movl (%rbx, %rdi, 4), %eax # load data: pointer + scaled index
movl 4(%rbx, %rdi, 4), %edx # load data: pointer + scaled index + displacement
私が知っているように、このような複雑なアドレス指定は、1 つの機械語命令に収まります。しかし、そのような操作のコストはいくらで、独立したポインター計算による単純なアドレス指定と比較してどうですか?
addl $32, %rbx # skip eight values: move pointer forward by 32 bytes
movl (%rbx), %eax # load data: pointer
addl $4, %rbx # point next value: move pointer forward by 4 bytes
movl (%rbx), %edx # load data: pointer
後者の例では、1 つの追加の命令と依存関係を導入しました。しかし、整数の加算は非常に高速で、より単純なアドレス オペランドが得られ、乗算はもうありません。一方、許可されている倍率は 2 のべき乗であるため、乗算はビット シフトに帰着します。これも非常に高速な操作です。それでも、2 回の加算とビット シフトを 1 回の加算に置き換えることができます。
これら 2 つのアプローチのパフォーマンスとコード サイズの違いは何ですか? 拡張アドレッシング オペランドを使用するためのベスト プラクティスはありますか?
または、C プログラマーの観点から尋ねると、配列のインデックス付けとポインター演算のどちらが高速ですか?
サイズ/パフォーマンス調整用のアセンブリ エディタはありますか? 各アセンブリ命令のマシン コード サイズ、クロック サイクル単位の実行時間、または依存関係グラフを確認できればと思います。このようなアプリケーションの恩恵を受けるアセンブリ フリークは何千人もいるので、このようなアプリケーションが既に存在していることは間違いありません。