コンパイラがメンバーを整列させるためにデータ構造にパディングを追加する理由について、誰かが私に短くてもっともらしい説明を教えてもらえますか?CPUがより効率的にデータにアクセスできるようにするために行われていることは知っていますが、なぜそうなのかわかりません。
そして、これがCPUにのみ関連している場合、なぜLinuxではダブル4バイトが整列され、Windowsでは8バイトが整列されるのでしょうか。
コンパイラがメンバーを整列させるためにデータ構造にパディングを追加する理由について、誰かが私に短くてもっともらしい説明を教えてもらえますか?CPUがより効率的にデータにアクセスできるようにするために行われていることは知っていますが、なぜそうなのかわかりません。
そして、これがCPUにのみ関連している場合、なぜLinuxではダブル4バイトが整列され、Windowsでは8バイトが整列されるのでしょうか。
アライメントは、CPU が効率的な方法でメモリからデータをフェッチするのに役立ちます: キャッシュ ミス/フラッシュの減少、バス トランザクションの減少など。
一部のメモリ タイプ (例: RDRAM、DRAM など) は、効率的な結果を得るために、構造化された方法でアクセスする必要があります (整列された「ワード」および「バースト トランザクション」、つまり一度に多くのワードでアクセスする必要があります)。これは、次のような多くの理由によるものです。
「パディング」は、転送効率を最適化するためにデータ構造のアラインメントを修正するために使用されます。
つまり、「ミスアライン」構造にアクセスすると、全体的なパフォーマンスが低下します。このような落とし穴の良い例: データ構造が正しく配置されておらず、その構造を取得するために CPU/メモリ コントローラーが (1 回ではなく) 2 回のバス トランザクションを実行する必要があるとします。その結果、パフォーマンスが低下します。
CPU は 4 バイトのグループでメモリからデータをフェッチします (実際には、ハードウェアの種類によっては 8 またはその他の値に依存しますが、単純にするために 4 のままにしておきます)、データがアドレスで始まる場合はすべて問題ありません。 4 で割り切れる場合、CPU はメモリ アドレスに移動し、データをロードします。
ここで、データが 4 で割り切れないアドレスで始まると仮定すると、アドレス 1 で簡単にするために、CPU はアドレス 0 からデータを取得し、何らかのアルゴリズムを適用してアドレス 0 でバイトをダンプし、実際のアドレスにアクセスする必要があります。これには時間がかかるため、パフォーマンスが低下します。そのため、すべてのデータ アドレスを整列させる方がはるかに効率的です。
キャッシュ ラインは、キャッシュの基本単位です。通常は 16 ~ 64 バイト以上です。
Pentium IV: 64 バイト。Pentium Pro/II: 32 バイト。Pentium I: 32 バイト。486: 16 バイト。
myrandomreader:
; ...
; ten instructions to generate next pseudo-random
; address in ESI from previous address
; ...
MOV EAX, DS:[ESI] ; X
LOOP myrandomreader
2 つのキャッシュラインにまたがるメモリ読み取りの場合:
(L1 キャッシュ ミスの場合) プロセッサは、2 番目のキャッシュ ラインを要求する前に、キャッシュ ライン 1 全体が L2->L1 からプロセッサに読み込まれるまで待機する必要があり、短い実行停止が発生します。
(L2 キャッシュ ミスの場合) プロセッサは、L3 キャッシュ (存在する場合) またはメイン メモリからのバースト読み取りが 1 回ではなく 2 回完了するまで待機する必要があります。
プロセッサのストール
ランダムな 4 バイト読み取りは、64 バイト キャッシュラインの場合は約 5%、32 バイト キャッシュラインの場合は 10%、16 バイト キャッシュラインの場合は 20% の時間でキャッシュライン境界をまたがります。
位置合わせが正しくないデータがキャッシュライン内にある場合でも、一部の命令では追加の実行オーバーヘッドが発生する場合があります。これは、一部の SSE 命令について Intel の Web サイトで説明されています。
構造体を自分で定義している場合は、すべての <32 ビット データ フィールドを 1 つのリストにまとめstruct
てパディング オーバーヘッドを削減するか、または特定の構造体に対してパッキングをオンまたはオフにする方がよいかどうかを確認することが理にかなっている場合があります。
MIPS や他の多くのプラットフォームでは選択肢がなく、調整する必要があります - そうしないとカーネル例外が発生します!!
バス上で I/O を実行している場合、またはアトミック インクリメント/デクリメントなどのアトミック操作を使用している場合、またはコードを Intel 以外に移植できるようにしたい場合は、アライメントが特に重要になる場合があります。
Intel のみ (!) のコードでは、ネットワークとディスク用に 1 セットのパック構造を定義し、インメモリ用に別のパディング セットを定義し、これらのフォーマット間でデータを変換するルーチンを用意するのが一般的な方法です (「エンディアン」も考慮してください)。ディスクおよびネットワーク形式)。
jldupontの回答に加えて、一部のアーキテクチャには、ワードアライメント境界でのみ動作するロードおよびストア命令(メモリへの読み書きに使用されるもの)があります。したがって、メモリからアライメントされていないワードをロードするには、2つのロード命令が必要になりますシフト命令、次にマスク命令 - 効率が大幅に低下します!