Linuxパフォーマンスの最適化(14)-CPUキャッシュ

一、CPU Cache

1.CPUキャッシュの概要

CPUCacheは、CPUとメモリの間の一時的なストレージです。容量はメモリよりも小さくなりますが、交換速度はメモリよりもはるかに高速です。キャッシュの外観は、主にCPUの動作速度とメモリの読み取りおよび書き込み速度の矛盾を解決するためのものです。CPUの動作速度はメモリの読み取りおよび書き込み速度よりもはるかに高速であるため、CPUはデータの到着またはメモリへのデータの書き込みを待機するのに長い時間を費やします。キャッシュ内のデータはメモリのごく一部ですが、CPUは短時間でアクセスします。CPUが大量のデータを呼び出す場合、メモリを回避してキャッシュから直接呼び出すことができるため、読み取り速度が向上します。キャッシュは、主にCPUのデータ交換シーケンスとCPUとキャッシュ間の帯域幅により、CPUパフォーマンスに大きな影響を与えます。
キャッシュの動作原理は、CPUがデータの一部を読み取りたい場合、最初にキャッシュで検索し、見つかった場合はすぐに読み取り、処理のためにCPUに送信します。見つからない場合は、比較的低速でメモリから読み取り、CPUに送信します。処理すると同時に、データが配置されているデータブロックをキャッシュに転送します。これにより、将来、メモリを呼び出さなくても、データのブロック全体をキャッシュから読み取ることができます。
キャッシュを読み取るCPUのヒット率は非常に高く(ほとんどのCPUは約90%に達する可能性があります)、CPUがメモリを直接読み取る時間を大幅に節約し、基本的にCPUがデータを読み取るときに待機する必要がなくなります。
データの読み取り順序とCPUとの緊密な統合の程度に応じて、CPUキャッシュは、L1キャッシュ、L2キャッシュ、およびL3キャッシュに分割できます。各レベルのキャッシュに格納されているすべてのデータは、次のレベルのキャッシュの一部です。技術的な難易度と製造コストは比較的減少しているため、その容量は比較的増加しています。CPUがデータの一部を読み取りたい場合、最初にL1キャッシュで検索し、見つからない場合はL2キャッシュで検索し、それでも見つからない場合はL3キャッシュまたはメモリで検索します。通常、キャッシュの各レベルのヒット率は約80%です。つまり、総データ量の80%がL1キャッシュにあり、総データ量の20%のみをL2キャッシュ、L3キャッシュ、またはメモリから読み取る必要があります。したがって、L1キャッシュはCPUキャッシュアーキテクチャの最も重要な部分です。

2.CPUキャッシュ構造

Intel CPUの中で、8086および80286時代のCPUにはキャッシュがありませんでした。これは、当時のCPUとメモリの速度に大きな違いがなく、CPUがメモリに直接アクセスしたためです。しかし、80386以降、CPU速度はメモリアクセス速度よりもはるかに高速でした。キャッシュが初めて登場し、最初のキャッシュはCPUモジュールではなく、マザーボードに配置されました。CPUキャッシュの素材であるSRAMは、メモリDRAMよりもはるかに高価であり、サイズはMB単位です。したがって、最近のコンピュータでは、CPUとメモリ間のチャネルとしてCPUとメモリ間に高速キャッシュが導入されています。長期的な開発と進化の後、L1、L2、およびL3の3レベルのキャッシュ構造は徐々に進化し、それらはすべてCPUチップに統合されています。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
L1キャッシュはCPUに最も近く、速度は最速ですが容量は最小です。最新のCPUのL1キャッシュは、データキャッシュと命令キャッシュの2つに分かれています。命令とデータの更新戦略は同じではなく、CISCの可変長命令のため、命令キャッシュは特別に最適化する必要があります。各CPUコアには独自の独立したL1キャッシュとL2キャッシュがありますが、L3キャッシュは通常CPU全体で共有されます。

3.CPUキャッシュを表示する

Linuxカーネル開発者は詳細なCPU情報を表示するようにCPUFreqシステムを定義し、/ sys / devices / system / cpuディレクトリは詳細なCPU情報を保存します。
L1キャッシュビュー
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
L2キャッシュビュー
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
L3キャッシュビュー
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
CPUキャッシュビューコマンドは次のとおりです。

dmidecode -t cache
getconf -a | grep CACHE

Linuxパフォーマンスの最適化(14)-CPUキャッシュ
CPUにはレベル3のキャッシュしかなく、L4は0、L1キャッシュのデータキャッシュと命令キャッシュは32KB、L2キャッシュは256KB、L3キャッシュは3MB、キャッシュラインは64バイトです。ASSOCは、メインメモリアドレスをキャッシュにマッピングする戦略を表しています。L1とL2は8ウェイのグループアソシアティブであり、L3は12ウェイのグループアソシアティブです。
L1キャッシュのデータキャッシュと命令キャッシュは物理CPUコアで共有されるため、ハイパースレッディングテクノロジによって仮想化された論理CPUコアCPU0とCPU1はL1キャッシュを共有します。

4、キャッシュライン

キャッシュラインはCPUキャッシュ内の最小のキャッシュユニットであり、このレベルのキャッシュがデータを次のレベルにフェッチするときの基本ユニットです。現在の主流のCPUキャッシュキャッシュラインサイズは64バイトです。つまり、プログラムがメモリから1バイトを読み取る必要がある場合、CPUが隣接にアクセスすると、隣接する63バイトがメモリからCPUキャッシュに同時にロードされます。データが保存されるとき、データはメモリから読み取られませんが、CPUキャッシュからデータにアクセスできるため、速度が向上します。
L1、L2、およびL3のキャッシュラインサイズはすべて64バイトであり、次のように表示できます
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
。CPUレジスタ、キャッシュ、およびメモリのパフォーマンスインジケータは、CPU
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
 がアクセスするデータを参照します。キャッシュにはヒットと呼ばれるキャッシュがあり、その逆も同様です。ミス(ミッシング)と呼ばれます。
CPUが特定のアドレスからデータを読み込もうとすると、最初にL1キャッシュからヒットしたかどうかを確認し、ヒットした場合はデータをCPUに返します。L1キャッシュがない場合は、L2キャッシュから検索を続けます。L2キャッシュがヒットすると、データはL1キャッシュとCPUに返されます。L2キャッシュとL3キャッシュも欠落している場合は、メインメモリからデータをロードし、そのデータをL3キャッシュ、L2キャッシュ、L1キャッシュ、およびCPUに返す必要があります。

5.マルチコアキャッシュの一貫性

各CPUコアにはプライベートL1キャッシュがあります。マルチコアCPUの場合、異なるCPUコア間のL1キャッシュは一貫している必要があります。
(1)バススヌーピングプロトコル
CPUコアが独自のプライベートキャッシュを変更すると、ハードウェアはバス上の他のすべてのCPUコアに通知をブロードキャストします。各CPUコアには、ブロードキャストイベントを監視し、同じデータが独自のキャッシュにキャッシュされているかどうかを確認するための特別なハードウェアがあります。
バススヌーピングは、バス上のすべてのアクティビティを常に監視する必要があります。これにより、バスの負荷がある程度増加し、読み取りと書き込みの遅延が増加します。
(2)MESIプロトコル
MESIプロトコルは、ステートマシンメカニズムを使用して帯域幅のプレッシャーを軽減し、マルチコアキャッシュの一貫性を維持するプロトコルです。
MESIプロトコル名は、キャッシュラインのModified、Exclusive、Shared、Invalidの4つの状態の略語に由来します。キャッシュラインの状態がModifiedまたはExclusive状態の場合、そのデータを変更しても他のCPUにメッセージを送信する必要がないため、帯域幅の負荷がある程度軽減されます。
マルチコアキャッシュの整合性はハードウェアによって保証されます。最新のCPUハードウェアで採用されている整合性プロトコルは通常MESIプロトコルの変形です。たとえば、ARM64アーキテクチャはMOESIプロトコルを使用します。

2、TLB

1.MMUの概要

MMU(Memory Management Unit)は、セグメントメカニズムとページメカニズムを介して仮想アドレスを物理アドレスに変換するハードウェア回路であり、通常はCPUチップに統合されています。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
MMUは、ページテーブルを確立することにより、仮想アドレスから物理アドレスへの変換を完了します。メモリ内のデータにアクセスする必要がある場合、ページテーブルはデータの仮想アドレスを介して検索されます。ページテーブルがヒットすると、メモリ内のデータは検出された物理アドレスを介してアドレス指定されます。ページテーブルにヒットがない場合は、データ仮想アドレスから物理アドレスへのマッピングがページテーブルで確立されておらず、データのページテーブルマッピングエントリがページ欠落例外によって確立されていることを意味します。
頻繁に使用する定数データについては、検索データとそれに対応するページテーブルエントリが毎回実行されます。ページテーブル検索を高速化し、不要な重複を減らすために、TLBが導入されています。

2.TLBの概要

TLB(Translation Look-aside Buffer)は、ページテーブルエントリをメモリにキャッシュするために特に使用され、通常はMMU内に統合されています。TLBは小さなキャッシュです。TLBエントリの数は比較的少ないです。各TLBエントリには、有効なビット、仮想ページ番号、変更されたビット、物理ページフレーム番号などのページに関する情報が含まれています。プロセッサが仮想アドレスにアクセスする場合、最初にTLBで検索します。TLBテーブルエントリにヒットがない場合は、ページテーブルにアクセスして、対応する物理アドレスを計算する必要があります。TLBエントリにヒットがあった場合、物理アドレスはTLBエントリから直接取得されます。
TLBに格納される基本単位はTLBテーブルエントリです。TLB容量が大きいほど、より多くのTLBテーブルエントリを格納でき、TLBヒット率が高くなります。ただし、TLBの容量には制限があります。現在、Linuxカーネルはデフォルトで4KBの小さなページを使用します。プログラムが512の小さなページ、つまり2MBのサイズを使用する場合、TLBミスが表示されないようにするには、少なくとも512のTLBエントリが必要です。ただし、2MBの大きなページを使用する場合は、TLBミスが表示されないようにするために必要なTLBエントリは1つだけです。GB単位のメモリを消費する大規模なアプリケーションの場合、1GB単位の大きなページを使用してTLBミスを減らすこともできます。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
TLBの本質は、ページテーブルのキャッシュです。TLBは、最近使用されたデータのページテーブルエントリをキャッシュします(仮想アドレスから物理アドレスへのマッピング)。TLBの登場は、メモリデータへのアクセス速度を加速し、ページテーブルの繰り返しルックアップを減らすことです。必須ではありませんが、TLBはメモリデータへのアクセス速度を上げることができます。

3.TLBの原則

CPUが仮想アドレス/線形アドレスにアクセスする場合、CPUは最初に仮想アドレスの上位20ビットに従ってTLBを検索します(X86アーキテクチャ)。テーブルにヒットがない場合は、低速RAMのページテーブルにアクセスして、対応する物理アドレスを計算する必要があります。同時に、物理アドレスはTLBエントリに格納され、同じ線形アドレスへの後続のアクセスは、TLBエントリから物理アドレスを直接取得できます。 
TLBの構造は次のとおりです。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
メモリ内のデータにアクセスする必要がある場合、データの仮想アドレスを指定して、TLBにクエリを実行し、存在することを確認し(ヒット)、物理アドレスを直接取得し、物理アドレスに従ってメモリ内のデータを読み取ります。TLBを見逃していない場合は、ページテーブルからのみ見つけることができます。

4.TLBマッピング方法

TLBに格納されている基本単位は、RAMに格納されているページテーブルエントリに対応するTLBテーブルエントリです。ページテーブルエントリのサイズは固定されているため、TLB容量が大きいほど、格納できるページテーブルエントリが多くなり、TLBヒットが大きくなります。ただし、TLBの容量には制限があるため、RAMページテーブルとTLBテーブルのエントリを1対1で対応させることはできません。
(1)完全に接続さ
れています。TLBキャッシュ内のエントリと線形アドレスの間に関係はありません。TLBエントリは、任意の線形アドレスのページテーブルエントリに関連付けることができます。完全に接続された関連付け方法は、TLBテーブルエントリスペースの使用率を最大化しますが、遅延も大きくなる可能性があります。各CPU要求のため、TLBハードウェアは、TLBがヒットするか、すべてのTLBテーブルエントリが比較されるまで、線形アドレスをTLBテーブルエントリと1つずつ比較します。実施する。CPUキャッシュがますます大きくなるにつれて、多数のTLBエントリが必要になるため、小容量のTLBにのみ適しています。
(2)
各リニアアドレスブロックを直接照合することで、モジュロ演算により一意のTLBエントリに対応できます。1回の比較でTLBの遅延が減少しますが、競合が非常に大きいため、TLBミスが発生し、減少します。ヒット率。
(3)グループ接続
フルコネクション内での低効率と直接マッチングの競合を解消するために、グループ接続を導入しています。グループ連結は、すべてのTLBテーブルエントリを複数のグループに分割し、各線形アドレスブロックは、TLBテーブルエントリではなく、TLBテーブルエントリグループに対応します。CPUは、アドレス変換を実行するときに、最初に線形アドレスブロックが対応するTLBエントリグループを計算し、次にそれをTLBエントリグループ内で順番に比較します。グループの長さに応じて、2ルート、4ルート、8ルートに分けることができます。
長期的なエンジニアリングの実践の後、8ウェイグループ接続がパフォーマンスの境界点であることがわかりました。8ウェイのグループ接続ヒット率は、完全接続ヒット率と同等です。8ウェイを超える場合、グループ内比較遅延によって引き起こされる不利な点は、改善されたヒット率の利点を上回ります。

3.ハイパースレッディングテクノロジー

1.ハイパースレッディングテクノロジーの概要

ハイパースレッディング(ハイパースレッディング)テクノロジーは、1つの物理コア上で2つの論理コアをシミュレートします.2つの論理コアには独自の独立したレジスタ(eax、ebx、ecx、msrなど)とAPICがありますが、これらは物理コアの実行リソースを共有します。実行エンジン、L1 / L2キャッシュ、TLB、システムバスなど。

2.ハイパースレッドがパフォーマンスに与える影響

ハイパースレッディングテクノロジは、1つの物理コアで2つの物理タスク記述子のみを使用し、物理的な計算能力は向上していません。アプリケーションは複数のワーカーで設計されています。ハイパースレッドの助けを借りて、異なるハイパースレッドで同じコアにスケジュールされた2つのワーカーは、キャッシュとTLBを共有できるため、タスク切り替えのオーバーヘッドが大幅に削減されます。ワーカーがビジーでない場合、ハイパースレッドを使用すると、他のワーカーが物理コンピューティングリソースを使用できるようになり、物理コアの全体的なスループットが向上します。ただし、物理コア実行リソースをめぐるハイパースレッドの競合により、ビジネスの実行遅延はそれに応じて増加します。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
ハイパースレッディングを有効にした後、物理コアの総計算能力が向上するかどうか、および改善の程度はビジネスモデルに関連します。平均増加率は約20%〜30%です。
ハイパースレッディングが互いに競合する場合、ハイパースレッディングの計算能力は、ハイパースレッディングが有効になっていない場合と比較されます。物理的なコアは約30%低下します

3.ハイパースレッドアプリケーション

ハイパースレッディングは、アプリケーションモデルに応じてオンまたはオフになります。遅延の影響を受けやすいタスクの場合、ノードの負荷が高すぎるとハイパースレッディングの競合が発生し、タスクの実行時間が大幅に増加します。ハイパースレッディングはオフにする必要があります。バックグラウンドコンピューティングタスクの場合は、ハイパースレッディングをオンにすることをお勧めします。スレッドはノード全体のスループットを向上させます。

4、NUMAテクノロジー

1.NUMAの紹介

NUMAアーキテクチャが登場する前は、高周波方向のCPUの開発は物理的な限界に直面し、複数のコアの方向に向けられていました。すべてのCPUコアはノースブリッジを共有してメモリを読み取るため、CPUコアの数が増えると、応答時間におけるノースブリッジのパフォーマンスのボトルネックがますます明らかになります。したがって、ハードウェア設計者は、メモリコントローラをノースブリッジから分割し、各CPUコア、つまりNUMAアーキテクチャで均等に分割します。
NUMA(Non-Uniform Memory Access)は、AMD Opteronマイクロアーキテクチャに端を発し、同時にIntelNehalemに採用されました。
NUMAアーキテクチャは、サーバー全体をいくつかのノードに分割し、それぞれに個別のCPUとメモリを備えています。CPUが自身のノードのメモリに対応する物理アドレスにアクセスする場合にのみ、応答時間が短くなります(後でローカルアクセスと呼ばれます)。他のノードのメモリ内のデータにアクセスする必要がある場合は、相互接続チャネルを介してデータにアクセスする必要があり、応答時間が以前よりも遅くなります(以下、リモートアクセスと呼びます)。したがって、NUMA(Non-Uniform Memory Access)は
ノード内で名前が付けられます。そのアーキテクチャはSMPに似ています。異なるコア間の通信にIMCバスを使用し、異なるノードはQPI(クイックパス相互接続)を介して通信します。
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
ソケット(メモリスロット)はノードに対応し、QPIレイテンシはIMCバスのレイテンシよりも高く、メモリへのCPUアクセスは遠く(リモート/ローカル)です。

2.NUMAの欠陥

NUMAアーキテクチャのメモリ割り当て戦略は、プロセスまたはスレッドにとって不公平です。RHEL Linuxでは、localallocがデフォルトのNUMAメモリ割り当て戦略です。つまり、現在のノードからメモリ割り当てを要求すると、リソース専用プログラムが特定のノードのメモリを簡単に使い果たしてしまいます。ノードのメモリが使い果たされると、LinuxはCPUノードを大量のメモリを消費するプロセス(またはスレッド)に割り当てるだけで、この時点でスワップが生成されます。

3.NUMAアプリケーション

(1)BIOS閉じるNUMA
BIOSメモリ設定メニューでノードインターリーブ項目を検索し、無効に設定するとNUMAが有効になり、不均一アクセスモードがデフォルト設定になります。有効に設定するとNUMAを閉じ、SMPを使用してメモリインターリーブモードを有効にします。
(2)
numactl –interleave = allを使用して、ソフトウェアレベルでNUMAを閉じてアプリケーション起動する前に、NUMA戦略を変更します。
アプリケーションが大量のメモリを占有する場合は、NUMAノード制限をオフにする(またはハードウェアからNUMAをオフにする)ことを選択する必要があります。アプリケーションが大きなメモリを占有しないが、プログラムの実行時間を短縮する必要がある場合は、このNUMAノードメソッドへのアクセスを制限することを選択する必要があります。プロセスへ。
BIOSでNUMAが有効になっているかどうかを確認します。
grep -i numa /var/log/dmesg
現在のシステムのNUMAノード
numactl --hardware
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
lscpuを
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
確認しますメモリデータ
numastatを確認します。numa_miss
Linuxパフォーマンスの最適化(14)-CPUキャッシュ
の値が比較的高いことわかった場合、指定したCPUにプロセスをバインドしてメモリヒット率を向上させるなど、割り当て戦略を調整する必要があることを示します。
NUMAサポートが有効になっているLinuxでは、カーネルはタスクメモリをあるNUMAノードから別のNUMAノードに移行しません。
プロセスが開始されると、それが配置されているNUMAノードは移行されません。パフォーマンスを可能な限り最適化するために、通常のスケジューリングでは、CPUのコアは、プロセスのライフサイクル全体で可能な限りローカルにアクセスできるローカルコアも使用します。 NUMAノードは変更されません。
NUMAノードの負荷が別のノードのしきい値(デフォルトでは25%)を超えると、このノードの負荷を減らす必要があると見なされます。さまざまなNUMA構造とさまざまな負荷条件に対して、システムは遅延タスクの移行を確認します。リーキーカップアルゴリズムに似ています。この場合、メモリへのリモートアクセスが発生します。
NUMAノード間には異なるトポロジ構造があり、異なるノード間のアクセスには距離の概念があり、numactl-Hで確認できます。

4、NUMA Node绑定

numactl [--interleave nodes] [--preferred node] [--membind nodes] [--cpunodebind nodes] [--physcpubind cpus] [--localalloc] command
--interleave = nodes、-iノードは、メモリインターリーブ割り当てモードを設定するために使用されます。システムが複数のノードにメモリスペースを割り当てる場合、ポーリング分散方式で複数のノードに割り当てられます。現在多数のインターリーブされた割り当てメモリノードの中のターゲットノードがメモリスペースを正しく割り当てることができない場合、メモリスペースは他のノードによって割り当てられます。
--membind = nodes、-mノードは指定されたノードからメモリスペースを割り当てます。ノードに十分なメモリスペースがない場合、割り当て操作は失敗します。
--cpunodebind = nodes、-NノードはプロセスをCPUノードにバインドするために使用されます。
--physcpubind、-C cpusは、プロセスをCPUコアにバインドするために使用されます。
--localalloc、-lプロセスを開始し、現在のCPUノードにメモリを割り当てます。
--preferred = nodeは、メモリスペースを優先的に割り当てるノードを指定するために使用されます。ノードにスペースを割り当てることができない場合は、他のノードから割り当てられます。
numactl --cpubind=0 --membind=0 python param
numactl --show
現在のNUMAメカニズム
numactl --hardware
は、現在のシステムで使用可能なノードの数を示します。
numactl [--huge] [--offset offset] [--shmmode shmmode] [--length length] [--strict]
共有メモリセグメントを作成する

おすすめ

転載: blog.51cto.com/9291927/2594323