確かにわかりません。1バイトの長さのメモリワードを持つメモリがあるとします。アラインされたアドレスの場合のように、アラインされていないアドレス(つまり、4で割り切れない)の単一メモリアクセスで4バイト長の変数にアクセスできないのはなぜですか?
8 に答える
最新のプロセッサのメモリサブシステムは、ワードサイズの粒度と配置でメモリにアクセスするように制限されています。これはいくつかの理由で当てはまります。
スピード
最新のプロセッサには、データをプルスルーする必要のある複数レベルのキャッシュメモリがあります。シングルバイトの読み取りをサポートすると、メモリサブシステムのスループットが実行ユニットのスループットに厳密にバインドされます(別名cpu-bound)。これはすべて、ハードドライブでの同じ理由の多くでPIOモードがDMAによってどのように超えられたかを彷彿とさせます。
CPUは常にワードサイズ(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からワードを読み取り、左に1バイトシフトして16ビットレジスタに配置する必要があります。余分な作業がいくつかありますが、ほとんどの場合、1サイクルでそれを処理できます。
0x0001から32ビットを要求すると、2倍の増幅が得られます。プロセッサは0x0000から結果レジスタに読み取り、左に1バイトシフトし、次に0x0004から一時レジスタに再度読み取り、右に3バイトシフトOR
し、次に結果レジスタを使用します。
範囲
任意のアドレス空間について、アーキテクチャが2つのLSBが常に0であると想定できる場合(たとえば、32ビットマシン)、4倍のメモリ(2つの保存されたビットは4つの異なる状態を表すことができます)または同じ量にアクセスできます。フラグのようなもののための2ビットのメモリの。アドレスから2LSBを削除すると、4バイトのアライメントが得られます。4バイトのストライドとも呼ばれます。アドレスがインクリメントされるたびに、ビット0ではなくビット2が効果的にインクリメントされます。つまり、最後の2ビットは常に。になります00
。
これは、システムの物理的な設計にも影響を与える可能性があります。アドレスバスに必要なビット数が2つ少ない場合、CPUのピン数が2つ少なくなり、回路基板のトレース数が2つ少なくなる可能性があります。
アトミシティ
CPUは、整列されたメモリワードをアトミックに操作できます。つまり、他の命令がその操作を中断することはできません。これは、多くのロックフリーデータ構造やその他の同時実行パラダイムを正しく動作させるために重要です。
結論
プロセッサのメモリシステムは、ここで説明するよりもかなり複雑で複雑です。x86プロセッサが実際にメモリをアドレス指定する方法についての説明が役立ちます(多くのプロセッサは同様に機能します)。
このIBMの記事で読むことができる、メモリー・アラインメントを順守することには、さらに多くの利点があります。
コンピューターの主な用途は、データを変換することです。最新のメモリアーキテクチャとテクノロジーは、信頼性の高い方法で、より多くのデータの取得、入力、出力、およびより多くのより高速な実行ユニット間の取得を容易にするために、数十年にわたって最適化されてきました。
ボーナス:キャッシュ
私が以前にほのめかしたもう1つのパフォーマンスの調整は、(たとえば、一部のCPUでは)64Bであるキャッシュラインでの調整です。
キャッシュを活用することで得られるパフォーマンスの詳細については、Gallery of ProcessorCacheEffectsを参照してください。キャッシュラインサイズに関するこの質問から
キャッシュラインの理解は、特定のタイプのプログラム最適化にとって重要な場合があります。たとえば、データの配置によって、操作が1つまたは2つのキャッシュラインにアクセスするかどうかが決まります。上記の例で見たように、これは簡単に、位置がずれている場合、操作が2倍遅くなることを意味します。
これは、多くの基盤となるプロセッサの制限です。通常、1つの効率的なワードフェッチではなく、4つの非効率的なシングルバイトフェッチを実行することで回避できますが、多くの言語指定子は、それらを非合法化し、すべてを強制的に整列させる方が簡単であると判断しました。
OPが発見したこのリンクには、さらに多くの情報があります。
一部のプロセッサでは可能です ( nehalem はこれを実行できます) が、以前はすべてのメモリ アクセスが 64 ビット (または 32 ビット) ラインに整列していました。バスは 64 ビット幅であるため、一度に 64 ビットをフェッチする必要がありました。 、これらを 64 ビットの整列された「チャンク」でフェッチする方がはるかに簡単でした。
したがって、1 バイトを取得したい場合は、64 ビットのチャンクを取得してから、不要なビットをマスクします。バイトが右端にある場合は簡単で高速ですが、その 64 ビット チャンクの中央にある場合は、不要なビットをマスクしてから、データを適切な場所にシフトする必要があります。さらに悪いことに、2 バイトの変数が必要であるが、それが 2 つのチャンクに分割されている場合、必要なメモリ アクセスが 2 倍必要になります。
誰もがメモリは安いと考えているので、コンパイラがデータをプロセッサのチャンク サイズに揃えるようにしただけなので、無駄なメモリを犠牲にしてコードをより高速かつ効率的に実行できます。
Fundamentally, the reason is because the memory bus has some specific length that is much, much smaller than the memory size.
So, the CPU reads out of the on-chip L1 cache, which is often 32KB these days. But the memory bus that connects the L1 cache to the CPU will have the vastly smaller width of the cache line size. This will be on the order of 128 bits.
So:
262,144 bits - size of memory
128 bits - size of bus
Misaligned accesses will occasionally overlap two cache lines, and this will require an entirely new cache read in order to obtain the data. It might even miss all the way out to the DRAM.
Furthermore, some part of the CPU will have to stand on its head to put together a single object out of these two different cache lines which each have a piece of the data. On one line, it will be in the very high order bits, in the other, the very low order bits.
There will be dedicated hardware fully integrated into the pipeline that handles moving aligned objects onto the necessary bits of the CPU data bus, but such hardware may be lacking for misaligned objects, because it probably makes more sense to use those transistors for speeding up correctly optimized programs.
In any case, the second memory read that is sometimes necessary would slow down the pipeline no matter how much special-purpose hardware was (hypothetically and foolishly) dedicated to patching up misaligned memory operations.
@joshperry は、この質問に対して優れた回答を提供しています。彼の答えに加えて、説明された効果、特に 2 倍の増幅をグラフで示すいくつかの数値があります。これは、さまざまな単語の配置の効果がどのように見えるかを示すGoogle スプレッドシートへのリンクです。さらに、テスト用のコードを含むGithub gistへのリンクを次に示します。テスト コードは、 @joshperry が参照した Jonathan Rentzschの記事を基にしています。テストは、クアッドコア 2.8 GHz Intel Core i7 64 ビット プロセッサと 16 GB の RAM を搭載した Macbook Pro で実行されました。
If you have a 32bit data bus, the address bus address lines connected to the memory will start from A2, so only 32bit aligned addresses can be accessed in a single bus cycle.
So if a word spans an address alignment boundary - i.e. A0 for 16/32 bit data or A1 for 32 bit data are not zero, two bus cycles are required to obtain the data.
Some architectures/instruction sets do not support unaligned access and will generate an exception on such attempts, so compiler generated unaligned access code requires not just additional bus cycles, but additional instructions, making it even less efficient.
バイト アドレス指定可能なメモリを備えたシステムに 32 ビット幅のメモリ バスがある場合、事実上 4 つのバイト幅のメモリ システムがあり、すべてが同じアドレスを読み書きするように配線されています。アライメントされた 32 ビット読み取りでは、4 つのメモリ システムすべての同じアドレスに格納された情報が必要になるため、すべてのシステムが同時にデータを供給できます。アライメントされていない 32 ビット読み取りでは、一部のメモリ システムが 1 つのアドレスからデータを返す必要があり、一部のメモリ システムは次に高いアドレスからデータを返す必要があります。そのような要求を満たすことができるように最適化されたメモリ システムがいくつかありますが (それらのアドレスに加えて、指定されたよりも 1 つ高いアドレスを使用する「プラス 1」信号を効果的に持っています)、そのような機能はかなりのコストを追加します。メモリシステムの複雑さ。
PowerPC では、奇数アドレスから問題なく整数をロードできます。
これを試すと、Sparc と I86 と (私が思うに) Itatnium でハードウェア例外が発生します。
1 つの 32 ビット ロードと 4 つの 8 ビット ロードは、ほとんどの最新のプロセッサで大きな違いはありません。データが既にキャッシュにあるかどうかは、はるかに大きな効果があります。