1

構造体/クラスのデータメンバーを整列させることで、特にハードウェアの改善により、以前の利点が得られなくなったのは本当ですか?もしそうなら、それでもアライメントは常にパフォーマンスを向上させ、過去のCPUと比較して非常に小さな顕著な改善が見られますか?

メンバー変数の配置はメンバー関数に拡張されますか?命令キャッシュに最適にロードするために、メンバー関数をさまざまな「ユニット」(つまりソースファイル)に「パック」するためのルールがあることを一度読んだことがあると思います(ウィキブックスの「C ++パフォーマンス」にある可能性があります)。(ここで用語が間違っている場合は、訂正してください)。

4

2 に答える 2

4

プロセッサはRAMが提供できるものよりもはるかに高速であるため、キャッシュが必要です。キャッシュは引き続き固定サイズのキャッシュラインで構成されます。また、メインメモリはページ単位で提供され、ページはトランスレーションルックアサイドバッファを使用してアクセスされます。このバッファにも、固定サイズのキャッシュがあります。

これは、空間的および時間的局所性の両方が非常に重要であることを意味します(つまり、荷物をどのように梱包し、どのようにアクセスするか)。構造を無計画な順序で梱包するのではなく、構造を適切に梱包すると(パディング/位置合わせの要件でソート)、通常、構造のサイズが小さくなります。

大量のデータがある場合、構造体のサイズが小さいということは、次のことを意味します。

  • より多くの構造が1つのキャッシュラインに収まります(キャッシュミス= 50〜200サイクル)
  • 必要なページ数が少なくなります(ページフォールト= 1,000〜2,000万CPUサイクル)
  • 必要なTLBエントリが少なくなり、TLBミスが少なくなります(TLBミス= 50〜500サイクル)

密にパックされた数ギガバイトのSoAデータを直線的に処理することは、レイアウトやパッキングが不適切な単純な方法で同じことを行うよりも3桁速くなります(ページフォールトが関係している場合は8〜10桁速くなります)。

個々の4バイトまたは2バイトの値(たとえば、標準intまたは)を2バイトまたは4バイトに手動で調整するかどうかはshort、最近のIntel CPUではごくわずかな違いになります(ほとんど目立たない)。これまでのところ、それを「最適化」したくなるかもしれませんが、そうしないことを強くお勧めします。
これは通常、最も心配する必要のないものであり、コンパイラに任せて理解します。他の理由がない場合、ゲインはせいぜいわずかであるためですが、他のいくつかのプロセッサアーキテクチャでは、間違った場合に例外が発生します。したがって、賢くなりすぎた場合、他のアーキテクチャでコンパイルすると、突然、説明のつかないクラッシュが発生します。それが起こったとき、あなたは気の毒に思うでしょう。

もちろん、処理するデータが少なくとも数十メガバイトない場合は、まったく気にする必要はありません。

于 2013-02-18T22:37:20.007 に答える
2

プロセッサに合わせてデータを調整しても問題はありませんが、一部のプロセッサには他のプロセッサよりも顕著な欠点があります。この質問に答える最良の方法だと思います。

関数をキャッシュラインユニットに整列させることは、私には少し赤いニシンのように思えます。小さな関数の場合、本当に必要なのは、可能な限りインライン化することです。コードをインライン化できない場合は、とにかくキャッシュラインよりも大きい可能性があります。[もちろん、仮想関数でない限り]。これが大きな要因になったとは思いません。コードは一般的に頻繁に呼び出されるため、通常はキャッシュ内で呼び出されるか、あまり頻繁に呼び出されず、キャッシュ内であまり頻繁に呼び出されません。1つの関数を呼び出すと、func1()もfunc2()をキャッシュにドラッグするコードを思い付く可能性があると確信しています。したがって、常にfunc1()とfunc2()を連続して呼び出すと、次のようになります。いくつかの利点。しかし、それは 密接に呼び出される関数のペアまたはグループを持つ関数が多数ない限り、実際にはそれほど大きなメリットはありません。[ちなみに、ソースファイルにどの順序で配置しても、コンパイラが関数コードを特定の順序で配置することは保証されていないと思います]。

キャッシュアラインメントは少し異なる問題です。なぜなら、キャッシュラインは、正しく実行した場合と間違って実行した場合に、依然として大きな影響を与える可能性があるためです。これは、一般的な「データのロード」よりもマルチスレッドにとって重要です。ここで重要なのは、プロセッサ間で同じキャッシュラインでデータを共有しないようにすることです。私が約10年前に取り組んだプロジェクトでは、ベンチマークには、2つの整数の配列を使用して、各スレッドが実行した反復回数をカウントする関数がありました。それが2つの別々のキャッシュラインに分割されると、ベンチマークは単一のプロセッサで実行される0.6倍から1つのプロセッサの1.98倍に向上しました。最新のCPUでも同じ効果が発生します。たとえそれらがはるかに高速であっても、効果はまったく同じではない可能性がありますが、大幅な速度低下になります(データを共有するプロセッサが多いほど、効果は大きくなります。したがって、クアッドコアシステムはデュアルコアなどよりも劣ります)。これは、プロセッサがキャッシュライン内の何かを更新するたびに、そのキャッシュラインを読み取った他のすべてのプロセッサが、それを更新したプロセッサから[または昔のメモリから]リロードする必要があるためです。

于 2013-02-18T22:37:10.783 に答える