セクションの開始を揃えるのはどういう意味ですか?
例えば:
align 4
a: dw 0
どのようにメモリアクセスを節約しますか?
セクションの開始を揃えるのはどういう意味ですか?
例えば:
align 4
a: dw 0
どのようにメモリアクセスを節約しますか?
私はいつも次のスレッドでの Samael による包括的な説明が好きでした:
ALIGN MASM ディレクティブの説明、このディレクティブはコンパイラによってどのように解釈されますか?
見積もり:
ALIGN X
ALIGN ディレクティブには数字 (X) が付きます。
この数値 (X) は 2 のべき乗でなければなりません。つまり、2、4、8、16 などです...
このディレクティブを使用すると、値 X の倍数であるメモリ アドレスで、ディレクティブの直後に命令またはデータのアライメントを強制できます。
前の命令/データと ALIGN ディレクティブの後の命令/データの間の余分なスペースは、コード セグメントの場合は NULL 命令 (または MOV EAX,EAX などの同等の命令) で埋められ、データ セグメントの場合は NULL で埋められます。
数値 X は、ALIGN ディレクティブが参照されるセグメントのデフォルトのアラインメントよりも大きくすることはできません。これは、セグメントのデフォルトの位置合わせ以下である必要があります。これについて詳しくは...
A. コードの操作
ディレクティブがコードの前にある場合、その理由は最適化 (実行速度を参照) になります。一部の命令は、4 バイト (32 ビット) 境界に配置されている場合、より高速に実行されます。この種の最適化は、通常、大量のデータを常に操作するように設計されたループなど、タイム クリティカルな関数で使用または参照できます。ただし、実行速度の向上以外に、コードでディレクティブを使用する「必要」はありません。
B. データの操作
同じことがデータにも当てはまります。速度最適化の手段として、主に実行速度を向上させるためにディレクティブを使用します。データの不整合がアプリケーションのパフォーマンスに大きな影響を与える場合があります。
しかし、データに関しては、贅沢ではなく、正確な調整が必要な場合があります。これは特に、Itanium プラットフォームと SSE/SSE2 命令セットに当てはまり、128 ビット境界 (X=16) でのミスアライメントが一般保護例外を発生させる可能性があります。
MS C/C++ コンパイラを対象としていますが、データ アライメントに関する興味深く、最も有益な記事は次のとおりです。
IPF、x86、および x64 での Windows データの配置、Kang Su Gatlin、MSDN
A. .386 プロセッサ ディレクティブを使用していて、セグメントのデフォルトのアラインメント値を明示的に宣言していない場合、デフォルトのセグメント アラインメントは DWORD (4 バイト) サイズです。はい、この場合は X = 4 です。次に、ALIGN ディレクティブで次の値を使用できます: (X=2, X= 4)。X はセグメントのアラインメント以下でなければならないことに注意してください。
B. .486 以上のプロセッサ ディレクティブを使用し、セグメントのデフォルトのアラインメント値を明示的に宣言していない場合、デフォルトのセグメント アラインメントは PARAGRAPH (16 バイト) サイズです。この場合、X = 16 です。次に、ALIGN ディレクティブで次の値を使用できます: (X=2、X= 4、X = 8、X = 16)。
C.次の方法で、デフォルト以外の配置でセグメントを宣言できます。
;Here, we create a code segment named "JUNK", which starts aligned on a 256 bytes boundary
JUNK SEGMENT PAGE PUBLIC FLAT 'CODE'
;Your code starts aligned on a PAGE boundary (X=256)
; Possible values that can be used with the ALIGN directive
; within this segment, are all the powers of 2, up to 256.
JUNK ENDS
セグメント アラインメント値のエイリアスは次のとおりです...
Align Type Starting Address
BYTE Next available byte address.
WORD Next available word address (2 bytes per word).
DWORD Next available double word address (4 bytes per double word).
PARA Next available paragraph address (16 bytes per paragraph).
PAGE Next available page address (256 bytes per page).
次の例を考えてみましょう (ALIGN ディレクティブの使用に関するコメントを読んでください)。
.486
.MODEL FLAT,STDCALL
OPTION CASEMAP:NONE
INCLUDE \MASM32\INCLUDE\WINDOWS.INC
.DATA
var1 BYTE 01; This variable is of 1 byte size.
ALIGN 4
; We enforce the next variable to be alingned in the next memory
;address that is multiple of 4.
;This means that the extra space between the first variable
;and this one will be padded with nulls. ( 3 bytes in total)
var2 BYTE 02; This variable is of 1 byte size.
ALIGN 2
; We enforce the next variable to be alingned in the next memory
;address that is multiple of 2.
;This means that the extra space between the second variable
;and this one will be padded with nulls. ( 1 byte in total)
var3 BYTE 03; This variable is of 1 byte size.
.CODE
; Enforce the first instruction to be aligned on a memory address multiple of 4
ALIGN 4
EntryPoint:
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we do not enforce opcode
; alignment in memory...
MOVZX EAX, var1
MOVZX EAX, var2
MOVZX EAX, var3
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we enforce opcode alignment
; for the third instruction, on a memory address multiple of 4.
; Since the second instruction opcodes end on a memory address
; that is not a multiple of 4, some nops would be injected before
; the first opcode of the next instruction, so that the first opcode of it
; will start on a menory address that is a multiple of 4.
MOVZX EAX, var1
MOVZX EAX, var2
ALIGN 4
MOVZX EAX, var3
; The following 3 instructions have 7 byte - opcodes
; of the form 0F B6 05 XX XX XX XX
; In the following block, we enforce opcode alignment
; for all instructions, on a memory address multiple of 4.
;The extra space between each instruction will be padded with NOPs
ALIGN 4
MOVZX EAX, var1
ALIGN 4
MOVZX EAX, var2
ALIGN 4
MOVZX EAX, var3
ALIGN 2
; The following instruction has 1 byte - opcode (CC).
; In the following block, we enforce opcode alignment
; for the instruction, on a memory address multiple of 2.
;The extra space between this instruction ,
;and the previous one, will be padded with NOPs
INT 3
END EntryPoint
プログラムをコンパイルすると、コンパイラは次のように生成します。
.DATA
;------------SNIP-SNIP------------------------------
.data:00402000 var1 db 1
.data:00402001 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402002 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402003 db 0; This NULL was generated to enforce the alignment of the next instruction on an address that is a multiple of 4
.data:00402004 var2 db 2
.data:00402005 db 0; This NULL was generated to enforce the alignment of the next instruction oon an address that is a multiple of 2
.data:00402006 var3 db 3
.data:00402007 db 0; The rest of the NULLs are to fill the memory page in which the segment will be loaded
;------------SNIP-SNIP------------------------------
.CODE
;------------SNIP-SNIP------------------------------
.text:00401000 start:
.text:00401000 movzx eax, var1
.text:00401007 movzx eax, var2
.text:0040100E movzx eax, var3
.text:00401015 movzx eax, var1
.text:0040101C movzx eax, var2
.text:00401023 nop; This NOP was generated to enforce the alignment...
.text:00401024 movzx eax, var3
.text:0040102B nop; This NOP was generated to enforce the alignment...
.text:0040102C movzx eax, var1
.text:00401033 nop; This NOP was generated to enforce the alignment...
.text:00401034 movzx eax, var2
.text:0040103B nop; This NOP was generated to enforce the alignment...
.text:0040103C movzx eax, var3
.text:00401043 nop; This NOP was generated to enforce the alignment...
.text:00401044 int 3 ; Trap to Debugger
.text:00401044; ---------------------------------------------------------------------------
.text:00401045 db 0
.text:00401046 db 0
.text:00401047 db 0
.text:00401048 db 0
;------------SNIP-SNIP------------------------------
ご覧のとおり、アプリケーションのコード/データが終了した後、コンパイラはさらに命令/データを生成します。これは、PE セクションがメモリにロードされると、ページ サイズ (512 バイト) に合わせて配置されるためです。
そのため、コンパイラは、次の 512 バイト境界までの余分なスペースをジャンク バイト (通常は INT 3 命令、コード セグメントの NOP または NULL、およびデータ セグメントの 0FFh、NULL) で埋めて、ロードされたデータのメモリ アラインメントを確保します。 PE画像は正しい...
メモリは固定幅で、今日では 32 ビットまたは通常は 64 ビット幅 (32 ビット システムであっても) です。ここでは、32 ビットのデータ バスを想定します。8、16、または32ビットの読み取りを行うたびに、それは32ビットバスであるため、これらのデータラインには何かが含まれるため、整列されたアドレスに関連する32ビットを配置するだけで意味があります。
したがって、アドレス 0x100 に 32 ビット値 0x12345678 があるとします。そして、32 ビットの読み取りを実行すると、これらのビットはすべてバス上にあることになります。アドレス 0x101 で 8 ビットの読み取りを実行する場合、メモリ コントローラーはアドレス 0x100 の読み取りを実行し、0x12345678 を取得します。そして、これらの 32 ビットから、アドレス 0x101 に関連する 8 ビットである適切な「バイト レーン」を分離します。一部のプロセッサでは、メモリ コントローラは 32 ビットの読み取り以外は認識しない場合があり、プロセッサはバイト レーンの分離を処理します。
x86 のようなアライメントされていないアクセスを許可するプロセッサはどうですか? アドレス 0x100 に 0x12345678 があり、アドレス 0x104 に 0xAABBCCDD があるとします。この 32 ビット データ バス ベースのシステムでアドレス 0x102 で 32 ビットの読み取りを行う場合、2 つのメモリ サイクルが必要です。見つかった。これらの 2 つの読み取りが発生した後、32 ビットをつなぎ合わせて、要求されたプロセッサのより深い部分に提供できます。たとえば、アドレス 0x103 で 16 ビットの読み取りを実行したい場合、同じことが起こり、2 倍のメモリ サイクルがかかり、2 倍の時間がかかります。
ディレクティブがアセンブリ言語で通常行うこと.align
(もちろん、これはディレクティブであり、各アセンブラーはディレクティブに定義したいものを定義できるため、正確なアセンブラーとプロセッサを指定する必要があります) は、出力をパディングして、直後に続くものが.align
まあ、その境界に沿っています。このコードがある場合:
b: .db 0
c: .dw 0
そして、アセンブルして C のアドレスをリンクすると 0x102 であることがわかりますが、32 ビット値として非常に頻繁にアクセスすることがわかっているので、次のようにしてアラインできます。
b: .db 0
.align 4
c: .dw 0
結果としてこれが変化する前に他に何も変化しないと仮定すると、b はアドレス 0x101 のままですが、アセンブラは b と c の間のバイナリにさらに 2 バイトを配置し、c がアドレス 0x104 に変更され、4 バイト境界に整列されます。
「4 バイト境界に整列」とは、単純にアドレス モジュロ 4 がゼロであることを意味します。基本的には 0x0、0x4、0x8、0xc、0x10、0x14、0x18、0x1C などです。(アドレスの下位 2 ビットはゼロです)。8 に揃えるとは、アドレスの 0x0、0x8、0x10、0x18、または下位 3 ビットがゼロであることを意味します。等々。
バスよりも小さいデータに対して読み取り-変更-書き込みを行う必要があるため、書き込みは読み取りよりも悪いです。アドレス 0x101 のバイトを変更したい場合は、アドレス 0x100 の 32 ビット値を読み取り、1 バイトを変更してから、その 32 ビット値を 0x100 に書き戻します。したがって、プログラムを書いていて、より小さな値を使用することで物事を高速化していると考えている場合、そうではありません。したがって、アライメントされていない書き込みとメモリの幅により、読み取り-変更-書き込みのコストがかかります。アラインされていない書き込みは、読み取りの場合と同じように 2 倍のコストがかかります。アラインされていない書き込みは、2 つの読み取り-変更-書き込みになります。ただし、書き込みには読み取りよりも優れたパフォーマンス機能があります。プログラムがメモリから何かを読み取ってその値をすぐに使用する必要がある場合、次の命令はメモリ サイクルが完了するまで待機する必要があります (最近では、数百クロック サイクルになり、ドラムは約 10 年間 133MHz でスタックしています。1333MHz DDR3 メモリは 1333MHz ではありません。バスは 1333MHz/2 であり、その速度でリクエストを送信できますが、応答はしばらく返されません)。基本的に、読み取りではアドレスがありますが、データがかかるまで待つ必要があります。書き込みの場合、アドレスとデータの両方の項目があり、メモリコントローラーにアドレスとデータを与えると「起動して忘れる」ことができ、プログラムは実行を続けることができます。次の命令または一連の命令がメモリへのアクセス、読み取りまたは書き込みを行う必要がある場合、誰もが最初の書き込みが完了するのを待ってから、次のアクセスに移る必要があります。バスは 1333MHz/2 で、その速度でリクエストを送信できますが、応答はしばらく返されません)。基本的に、読み取りではアドレスがありますが、データがかかるまで待つ必要があります。書き込みの場合、アドレスとデータの両方の項目があり、メモリコントローラーにアドレスとデータを与えると「起動して忘れる」ことができ、プログラムは実行を続けることができます。次の命令または一連の命令がメモリへのアクセス、読み取りまたは書き込みを行う必要がある場合、誰もが最初の書き込みが完了するのを待ってから、次のアクセスに移る必要があります。バスは 1333MHz/2 で、その速度でリクエストを送信できますが、応答はしばらく返されません)。基本的に、読み取りではアドレスがありますが、データがかかるまで待つ必要があります。書き込みの場合、アドレスとデータの両方の項目があり、メモリコントローラーにアドレスとデータを与えると「起動して忘れる」ことができ、プログラムは実行を続けることができます。次の命令または一連の命令がメモリへのアクセス、読み取りまたは書き込みを行う必要がある場合、誰もが最初の書き込みが完了するのを待ってから、次のアクセスに移る必要があります。メモリ コントローラにアドレスとデータを与えると、プログラムは実行し続けることができます。次の命令または一連の命令がメモリへのアクセス、読み取りまたは書き込みを行う必要がある場合、誰もが最初の書き込みが完了するのを待ってから、次のアクセスに移る必要があります。メモリ コントローラにアドレスとデータを与えると、プログラムは実行し続けることができます。次の命令または一連の命令がメモリへのアクセス、読み取りまたは書き込みを行う必要がある場合、誰もが最初の書き込みが完了するのを待ってから、次のアクセスに移る必要があります。
上記のすべては非常に単純化されていますが、プロセッサとキャッシュの間、キャッシュの反対側にある固定幅メモリ (キャッシュ内の SRAM の固定幅と、ファーサイドは一致する必要はありません)キャッシュの反対側では、通常、バス幅のサイズの倍数である「キャッシュライン」でアクセスされます。これは、アラインメントに役立ち、害を及ぼします。たとえば、0x100 がキャッシュ ラインの境界であるとします。たとえば、0xFE のワードは 1 つのキャッシュ ラインの末尾であり、0x100 は次のキャッシュ ラインの先頭です。アドレス 0xFE で 32 ビットの読み取りを実行する場合、2 つの 32 ビット メモリ サイクルが発生するだけでなく、2 つのキャッシュ ライン フェッチが発生する必要があります。最悪の場合、フェッチする 2 つの新しいキャッシュ ライン用のスペースを確保するために、2 つのキャッシュ ラインをメモリに削除する必要があります。
あなたの質問ではプロセッサを指定していませんでしたが、質問の性質上、この問題でよく知られている x86 が暗示されています。他のプロセッサ ファミリは非境界整列アクセスを許可しないか、例外フォルトを明確に無効にする必要があります。また、アラインされていないアクセスが x86 に似ていない場合もあります。たとえば、少なくとも 1 つのプロセッサで、アドレス 0x100 に 0x12345678 があり、アドレス 0x104 に 0xAABBCCDD があり、障害を無効にしてアドレス 0x102 で 32 ビットの読み取りを実行した場合、0x56781234 が得られます。下位バイトを正しい位置に配置するためにバイト レーンを回転させた単一の 32 ビット読み取り。いいえ、x86 システムではなく、他のプロセッサについて話しているのです。
align
オペランドにアラインされるまで、アドレスを NOPs/0x90 (NASM) で埋めます (addr modulo operand はゼロです)。
例えば:
db 12h
align 4
db 32h
組み立てられた出力:
0000 12 90 90 90
0004 32
これは、メモリ アクセスが高速であり、x86 CPU (およびおそらく他のアーキテクチャも同様) でいくつかのテーブルをロードするために必要です。特定のケースを挙げることはできませんが、SO と検索エンジンでいくつかの 回答を見つけることができます。