確かにわかりません。長さが1バイトのメモリワードを持つメモリがあるとします。アラインされたアドレスの場合のように、アラインされていないアドレス(つまり、4で割り切れない)の単一メモリアクセスで4バイト長の変数にアクセスできないのはなぜですか?
これは、多くの基礎となるプロセッサの制限です。通常は、1つの効率的なWordフェッチではなく、4つの非効率的なシングルバイトフェッチを実行することで回避できますが、多くの言語指定者は、それらを禁止してすべてを強制的に整列させる方が簡単だと判断しました。
このリンク には、OPが発見したより多くの情報があります。
最新のプロセッサ上のメモリサブシステムは、Wordサイズの粒度とアライメントでメモリにアクセスすることに制限されています。これにはいくつかの理由があります。
最新のプロセッサには、データをプルスルーする必要がある複数レベルのキャッシュメモリがあります。シングルバイトの読み取りをサポートすると、メモリサブシステムのスループットが実行ユニットのスループットに厳密にバインドされます(別名CPUバインド)。これはすべて、ハードドライブの多くの同じ理由で DMAがPIOモードを上回った を連想させます。
CPUalwaysはそのワードサイズ(32ビットプロセッサでは4バイト)で読み取るため、アラインされていないアドレスアクセスを行う場合-プロセッサ上でそれをサポートします—プロセッサは複数の単語を読みます。 CPUは、要求されたアドレスがまたがるメモリの各ワードを読み取ります。これにより、要求されたデータにアクセスするために必要なメモリトランザクション数が最大2倍になります。
このため、4バイトよりも2バイトの読み取りが非常に簡単に遅くなる可能性があります。たとえば、次のような構造体がメモリにあるとします。
struct mystruct {
char c; // one byte
int i; // four bytes
short s; // two bytes
}
32ビットプロセッサでは、おそらく次のように配置されます。
プロセッサは、これらの各メンバーを1つのトランザクションで読み取ることができます。
たとえば、伝送効率のためにパックされたネットワークから、パックのパックバージョンがあったとします。次のようになります。
最初のバイトの読み取りは同じになります。
プロセッサに0x0005から16ビットを提供するように要求する場合、0x0004からWordを読み取り、1ビット左にシフトして16ビットレジスタに配置する必要があります。多少の余分な作業がありますが、ほとんどは1サイクルで処理できます。
0x0001から32ビットを要求すると、2倍の増幅が得られます。プロセッサは、0x0000から結果レジスタに読み取り、左に1バイトシフトし、次に0x0004から一時レジスタに再度読み取り、右に3バイトシフトしてから、結果レジスタでOR
it読み取ります。
任意のアドレス空間で、アーキテクチャが2つのLSBが常に0(32ビットマシンなど)であると想定できる場合、4倍のメモリ(保存された2つのビットは4つの異なる状態を表す)または同じ量にアクセスできますフラグのようなもののための2ビットのメモリの。アドレスから2つのLSBを削除すると、4バイトのアライメントが得られます。 4バイトの stride とも呼ばれます。アドレスがインクリメントされるたびに、ビット0ではなくビット2が効果的にインクリメントされます。つまり、最後の2ビットは常に00
。
これは、システムの物理設計にも影響を与える可能性があります。アドレスバスに必要なビットが2つ少ない場合、CPUのピンが2つ少なくなり、回路基板のトレースが2つ少なくなります。
CPUは整列されたメモリワードでアトミックに動作できます。つまり、他の命令がその動作を中断することはできません。これは、多くの ロックフリーデータ構造 およびその他の 並行性 パラダイムの正しい操作にとって重要です。
プロセッサのメモリシステムは、ここで説明するよりもかなり複雑で複雑です。 x86プロセッサが実際にメモリをアドレス指定する方法 に関する議論が役立ちます(多くのプロセッサが同様に動作します)。
このIBM記事 で読むことができる、メモリーのアライメントを順守することには、さらに多くの利点があります。
コンピューターの主な用途は、データの変換です。最新のメモリアーキテクチャとテクノロジーは、信頼性の高い方法で、より多くのデータを出し入れし、より高速な実行ユニット間でやり取りできるように、数十年にわたって最適化されてきました。
以前に言及した別のパフォーマンスのアライメントは、64Bのキャッシュライン(たとえば、一部のCPU)のアライメントです。
キャッシュを活用することで得られるパフォーマンスの詳細については、 Gallery of Processor Cache Effects ;をご覧ください。これから キャッシュラインのサイズに関する質問
特定の種類のプログラム最適化では、キャッシュラインを理解することが重要です。たとえば、データのアラインメントにより、操作が1つまたは2つのキャッシュラインに触れるかどうかが決まります。上記の例で見たように、これは簡単に、ミスアライメントの場合、操作が2倍遅くなることを意味します。
一部のプロセッサでは使用できます( nehalemはこれを実行できます )が、以前はすべてのメモリアクセスが64ビット(または32ビット)行で調整されていました。バスは64ビット幅なので、一度に64ビットをフェッチするため、64ビットの整列された「チャンク」でこれらをフェッチする方がはるかに簡単でした。
したがって、1バイトを取得したい場合は、64ビットチャンクをフェッチしてから、不要なビットをマスクします。バイトが右端にある場合は簡単で高速ですが、その64ビットチャンクの中間にある場合は、不要なビットをマスクしてからデータを適切な場所にシフトする必要があります。さらに悪いことに、2バイトの変数が必要で、それが2つのチャンクに分割されている場合、必要なメモリアクセスが2倍になります。
したがって、誰もがメモリは安価であると考えているので、コンパイラはプロセッサのチャンクサイズにデータを揃えて、コードをより速く、より効率的に実行し、メモリを浪費します。
基本的に、その理由は、メモリバスの特定の長さがメモリサイズよりもはるかに小さいためです。
そのため、CPUはオンチップL1キャッシュから読み取ります。これは最近では32 KBであることがよくあります。ただし、L1キャッシュをCPUに接続するメモリバスは、キャッシュラインサイズの幅が非常に小さくなります。これは、128 bitsのオーダーになります。
そう:
262,144 bits - size of memory
128 bits - size of bus
不整列のアクセスは2つのキャッシュラインと重複する場合があり、これにはデータを取得するためにまったく新しいキャッシュ読み取りが必要になります。 DRAMに到達することさえできないかもしれません。
さらに、CPUの一部が頭の上に立って、それぞれがデータの一部を持っているこれら2つの異なるキャッシュラインから単一のオブジェクトをまとめる必要があります。一方の行では、非常に高位のビットになり、もう一方の行では、非常に低位のビットになります。
CPUデータバスの必要なビットへの整列オブジェクトの移動を処理するパイプラインに完全に統合された専用ハードウェアがありますが、適切に最適化を高速化するためにこれらのトランジスタを使用する方が理にかなっているため、整列されていないオブジェクトにはこのようなハードウェアが欠けている可能性がありますプログラム。
いずれにせよ、特殊な目的のハードウェアがミスアライメントのメモリ操作にパッチを当てるのに(仮に愚かに)どれだけ専念していても、必要な2回目のメモリ読み取りはパイプラインを遅くします。
@joshperryは、この質問に対する優れた回答を提供しました。彼の答えに加えて、記述された効果、特に2X増幅をグラフィカルに示すいくつかの数値があります。 Googleスプレッドシート へのリンクは、Wordのさまざまな配置の効果を示しています。さらに、テスト用のコードを含む Github Gist へのリンクがあります。テストコードは、@-joshperryが参照したJonathan Rentzschによって書かれた 記事 から改造されています。テストは、クアッドコア2.8 GHz Intel Core i7 64ビットプロセッサと16 GBのRAMを搭載したMacbook Proで実行されました。
バイトアドレス可能なメモリを備えたシステムに32ビット幅のメモリバスがある場合、事実上4つのバイト幅のメモリシステムがあり、すべて同じアドレスを読み書きするように配線されています。アライメントされた32ビットの読み取りには、4つのメモリシステムすべての同じアドレスに情報を保存する必要があるため、すべてのシステムが同時にデータを提供できます。位置合わせされていない32ビットの読み取りでは、一部のメモリシステムが1つのアドレスからデータを返し、一部のメモリシステムが次に高いアドレスからデータを返す必要があります。そのような要求を満たすことができるように最適化されたメモリシステムがいくつかありますが(アドレスに加えて、指定されたアドレスよりも1つ高いアドレスを使用させる「プラス1」信号を効果的に持っています)メモリシステムの複雑さ。ほとんどのコモディティメモリシステムは、異なる32ビットワードの一部を同時に返すことはできません。
32ビットのデータバスがある場合、メモリに接続されているアドレスバスのアドレスラインはAから始まります2、したがって、単一のバスサイクルでアクセスできるのは32ビットでアラインされたアドレスのみです。
したがって、Wordがアドレスアライメントの境界にまたがる場合-A 16/32ビットデータまたはA1 32ビットのデータはゼロではないため、データを取得するには2バスサイクルが必要です。
一部のアーキテクチャ/命令セットは非境界整列アクセスをサポートせず、そのような試行で例外を生成します。そのため、コンパイラ生成の非境界整列アクセスコードは追加のバスサイクルだけでなく追加の命令を必要とし、さらに効率が低下します。
PowerPCでは、奇数アドレスから整数を問題なくロードできます。
SparcとI86および(私が思うに)Itatniumはこれを試みるとハードウェア例外を発生させます。
1つの32ビットロードと4つの8ビットロードは、ほとんどの最新のプロセッサで大きな違いをもたらすことはありません。データが既にキャッシュにあるかどうかは、はるかに大きな効果があります。