確かにわかりません。あなたが1バイトの長さのメモリワードを持つメモリを持っているとしましょう。アライメントされたアドレスの場合のように、アライメントされていないアドレス(つまり、4で割り切れない)の単一メモリアクセスで4バイトの変数にアクセスできないのはなぜですか?
確かにわかりません。あなたが1バイトの長さのメモリワードを持つメモリを持っているとしましょう。アライメントされたアドレスの場合のように、アライメントされていないアドレス(つまり、4で割り切れない)の単一メモリアクセスで4バイトの変数にアクセスできないのはなぜですか?
回答:
最新のプロセッサのメモリサブシステムは、そのワードサイズの細分性と配置でのメモリへのアクセスに制限されています。これにはいくつかの理由があります。
最近のプロセッサには、データをプルスルーする必要がある複数レベルのキャッシュメモリがあります。シングルバイトの読み取りをサポートすると、メモリサブシステムのスループットが実行ユニットのスループットにしっかりとバインドされます(別名CPUバインド)。これは、ハードドライブの同じ理由の多くが原因で、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ビットのメモリ。アドレスから2つのLSBを削除すると、4バイトのアライメントが得られます。4バイトのストライドとも呼ばれます。アドレスがインクリメントされるたびに、実質的にビット0ではなくビット2がインクリメントされます00
。つまり、最後の2ビットは常に継続します。
これは、システムの物理設計にも影響を与える可能性があります。アドレスバスに必要なビットが2つ少ない場合、CPUのピンが2つ少なくなり、回路基板のトレースが2つ少なくなります。
CPUはメモリの整列されたワードをアトミックに操作できます。つまり、他の命令がその操作を中断することはできません。これは、多くのロックフリーデータ構造およびその他の同時実行パラダイムの正しい操作に不可欠です。
プロセッサのメモリシステムは、ここで説明するよりもかなり複雑で複雑です。x86プロセッサが実際にメモリをアドレス指定する方法についての議論が役立つ場合があります(多くのプロセッサは同様に動作します)。
このIBMの記事で読むことができる、メモリー調整を順守することには、さらに多くの利点があります。
コンピューターの主な用途は、データの変換です。現代のメモリアーキテクチャとテクノロジーは数十年にわたって最適化され、信頼性の高い方法で、より高速な実行ユニット間で、より多くのデータを出し入れできます。
私が以前に言及したもう1つのパフォーマンス調整は、(たとえば、一部のCPUで)64Bであるキャッシュラインの調整です。
キャッシュを活用することでどの程度のパフォーマンスが得られるかについて詳しくは、Gallery of Processor Cache Effectsをご覧ください。キャッシュラインサイズに関するこの質問から
キャッシュラインの理解は、特定の種類のプログラム最適化にとって重要です。たとえば、データの整列により、操作が1つまたは2つのキャッシュラインに触れるかどうかが決まります。上記の例で見たように、これは簡単に調整不良の場合、操作が2倍遅くなることを意味します。
一部のプロセッサでは可能ですが(nehalemではこれが可能です)、以前はすべてのメモリアクセスが64ビット(または32ビット)のラインで調整されていました。バスが64ビット幅であるため、一度に64ビットをフェッチする必要がありました。 、これらを64ビットの整列された「チャンク」でフェッチする方がはるかに簡単でした。
したがって、1バイトを取得したい場合は、64ビットのチャンクをフェッチしてから、不要なビットをマスクします。バイトが右端にある場合は簡単かつ高速ですが、64ビットチャンクの中央にある場合は、不要なビットをマスクしてから、データを正しい場所にシフトする必要があります。さらに悪いことに、2バイトの変数が必要だが、2つのチャンクに分割されている場合、必要なメモリアクセスは2倍必要でした。
したがって、メモリは安価であると誰もが思うように、メモリを浪費してコードをより高速かつ効率的に実行できるように、コンパイラのデータをプロセッサのチャンクサイズに合わせます。
基本的に、その理由は、メモリバスにはメモリサイズよりもはるかに小さい、特定の長さがあるためです。
そのため、CPUはオンチップのL1キャッシュから読み取ります。これは、最近の多くの場合32KBです。ただし、L1キャッシュをCPUに接続するメモリバスは、キャッシュラインサイズの幅が非常に小さくなります。これは、128 ビット程度です。
そう:
262,144 bits - size of memory
128 bits - size of bus
誤って調整されたアクセスは、2つのキャッシュラインをオーバーラップすることがあり、データを取得するためにまったく新しいキャッシュの読み取りが必要になります。それは、DRAMまでのすべての行方を見逃す可能性さえあります。
さらに、CPUの一部は、それぞれがデータの一部を持っているこれらの2つの異なるキャッシュラインから単一のオブジェクトを組み立てるために、頭の上に立つ必要があります。一方の行では、非常に高次のビットになり、もう一方の行では、非常に低次のビットになります。
パイプラインに完全に統合されたCPUデータバスの必要なビットへのオブジェクトの移動を処理する専用ハードウェアがありますが、正しく最適化された速度で高速化するためにこれらのトランジスタを使用する方がおそらく理にかなっているため、このようなハードウェアはミスアライメントされたオブジェクトには欠けている可能性がありますプログラム。
いずれの場合も、必要な2番目のメモリ読み取りは、ミスアライメントされたメモリ操作のパッチの適用に専用のハードウェアが(仮説的に、愚かに)どれだけ専用であっても、パイプラインを遅くします。
@joshperryがこの質問に対して優れた回答を示しました。彼の回答に加えて、説明された効果、特に2X増幅をグラフで示すいくつかの数値があります。これは、さまざまな単語の配置の効果がどのように見えるかを示すGoogleスプレッドシートへのリンクです。さらに、ここにテスト用のコードを含むGithub gistへのリンクがあります。テストコードは、 @ joshperryが参照したJonathan Rentzschが書いた記事を基にしています。テストは、クアッドコア2.8 GHz Intel Core i7 64ビットプロセッサと16GBのRAMを搭載したMacbook Proで実行されました。
x
してy
座標の意味は?
バイトアドレス指定可能なメモリを備えたシステムに32ビット幅のメモリバスがある場合、同じアドレスを読み書きするためにすべて配線されている事実上4つのバイト幅のメモリシステムがあることを意味します。整列された32ビットの読み取りでは、4つのメモリシステムすべてで同じアドレスに情報を格納する必要があるため、すべてのシステムが同時にデータを提供できます。アラインされていない32ビットの読み取りでは、一部のメモリシステムが1つのアドレスからデータを返す必要があり、一部は次に高いアドレスからデータを返す必要があります。そのような要求を満たすことができるように最適化されたメモリシステムがいくつかありますが(それらのアドレスに加えて、指定よりも1つ高いアドレスを使用させる「プラス1」信号を効果的に持っています)、このような機能はかなりのコストを追加しますとメモリシステムの複雑さ。
32ビットデータバスを使用している場合、メモリに接続されているアドレスバスアドレスラインはA 2から始まるため、1つのバスサイクルでアクセスできるのは32ビットアライメントのアドレスのみです。
したがって、ワードがアドレスアラインメント境界にまたがる場合(16/32ビットデータのA 0または32ビットデータのA 1がゼロでない場合)、データを取得するには2バスサイクルが必要です。
一部のアーキテクチャ/命令セットは、非境界整列アクセスをサポートせず、そのような試みで例外を生成します。そのため、コンパイラが生成する非境界整列アクセスコードは、追加のバスサイクルだけでなく、追加の命令も必要とし、効率をさらに低下させます。
PowerPCでは、奇数アドレスから整数を問題なくロードできます。
SparcとI86および(私はそう思います)Itatniumは、これを試行するとハードウェア例外を発生させます。
1つの32ビットロードと4つの8ビットロードでは、ほとんどの最新のプロセッサで大きな違いはありません。データがすでにキャッシュにあるかどうかは、はるかに大きな影響を及ぼします。