17

問題

ミューテックスのないステートレスで副作用のない関数を実装するJavaコード(関連する場合はJDK 1.6.0._22)があります。ただし、大量のメモリを使用します(それが関連しているかどうかはわかりません)。

過去に、私は Sun Laboratories を訪れ、標準的な「パフォーマンスとスレッド数」の曲線を集めました。この関数にはミューテックスがないため、スレッドの数が増えるとガベージ コレクションが開始されますが、グラフは良好です。ガベージ コレクションの調整を行った後、この曲線をほぼフラットにすることができました。

現在、Intel ハードウェアで同じ実験を行っています。ハードウェアには、それぞれ 8 コアの 4 つの CPU とハイパースレッディングが搭載されています。これにより、64 個の availableProcessors() が得られます。残念ながら、「パフォーマンス対スレッド数」の曲線は、1、2、3 スレッドで適切にスケーリングされ、3 スレッドでキャップされます。3 つのスレッドの後、タスクに必要な数のスレッドを配置できますが、パフォーマンスは向上しません。

問題を解決する試み

私の最初の考えは、私が愚かで、同期されたコードをどこかに導入したということでした。通常、この問題を解決するには、JConsole または JVisualVM を実行し、スレッドのスタック トレースを調べます。速度 3 で 64 個のスレッドを実行している場合、そのうち 61 個のスレッドがミューテックスに入るのを待っていると予想されます。私はこれを見つけませんでした。代わりに、すべてのスレッドが実行されていることがわかりました。非常にゆっくりです。

2 番目の考えは、おそらくタイミング フレームワークが問題を引き起こしているということでした。関数を、AtomicLong を使用して 10 億までカウントするダミー関数に置き換えました。これはスレッド数に応じて美しくスケーリングされました。1 スレッドよりも 64 スレッドの方が 64 倍速く、10,000 倍までカウントすることができました。

おそらくガベージ コレクションに非常に長い時間がかかっているのではないかと考えたので (必死になって)、ガベージ コレクションのパラメーターを微調整しました。これによりレイテンシーの変動は改善されましたが、スループットには影響しませんでした。3 つ実行すると予想される速度で、まだ 64 個のスレッドが実行されています。

インテル ツール VTunes をダウンロードしましたが、スキルが弱く、複雑なツールであり、まだ理解できていません。私は注文書を持っています: 自分自身への楽しいクリスマスプレゼントですが、それは私の現在の問題を解決するには少し遅すぎます.

質問

  1. 何が起こっているのかについての理解を深めるために、どのようなツール (メンタルまたはソフトウェア) を使用できますか?
  2. ミューテックスまたはガベージ コレクション以外のどのメカニズムがコードの速度を低下させている可能性がありますか?
4

3 に答える 3

11

私はJavaコードを持っています(関連する場合はJDK 1.6.0._22)

それ以来、かなりのパフォーマンスの改善が行われてきました。Java 6 update 37 または Java 7 update 10 を試してみます。

ただし、多くのメモリを使用します

これは、データへのアクセス方法が重要になる可能性があることを意味します。メイン メモリ内のデータへのアクセスは、プライマリ キャッシュ内よりも 20+x 遅くなる可能性があります。これは、データに保守的にアクセスし、アクセスする新しいデータの各部分を最大限に活用する必要があることを意味します。

3 つのスレッドの後、タスクに必要な数のスレッドを配置できますが、パフォーマンスは向上しません。代わりに、すべてのスレッドが実行されていることがわかりました。非常に遅いだけです。

これは、リソースを最大限に使用していることを示しています。使用しているメモリの量を考えると、最大になる可能性が最も高いリソースは、CPU からメイン メモリへのブリッジです。64 スレッドに対して 1 つのブリッジがあると思われます。これは、より多くの CPU を使用する方法を検討する必要がありますが、メモリへのアクセス方法を改善し (ランダム性を減らし、より順次的に)、そのときにボリュームを減らす (可能な場合はよりコンパクトなタイプを使用する) ことを意味します。たとえば、float の代わりに「小数点以下 2 桁の short」型を使用すると、メモリの使用量を半分にすることができます。

あなたが観察したように、各スレッドが独自のプライベート AtomicLong を更新しているとき、線形のスケーラビリティが得られます。これはCPUからメインメモリへのブリッジをまったく使用しません。


@マルコより

ピーター、とにかく、これらのマルチコア アーキテクチャがメモリでどのように機能するか考えていますか?

この問題は Java には表示されないため、私が望むほどではありません。

各コアには独立したチャネルがありますか?

各コアには、プライマリ キャッシュへの独立したチャネルがあります。外部キャッシュの場合、各キャッシュ領域または 2 ~ 6 個のキャッシュ領域にチャネルが存在する可能性がありますが、負荷が高いと多数の衝突が発生します。

メイン メモリへのブリッジには、非常に広いチャネルが 1 つあります。これは長いシーケンシャル アクセスに有利ですが、ランダム アクセスには非常に適していません。単一のスレッドは、ランダム読み取りでこれを最大化できます (外部キャッシュに収まらないほどランダムです)。

または、衝突がない限り、少なくとも独立していますか?

一次キャッシュ (L1 は通常 32 KB) を使い果たすと、完全に衝突します。

それ以外の場合、スケーリングは大きな問題になるためです。

OPが示すように。ほとんどのアプリケーションは、a) IO の待機にかなりの時間を費やします。b) データの小さなバッチに計算を割り当てます。大量のデータに計算を割り当てることは、最悪のシナリオです。

これに対処する方法は、シーケンシャル アクセス用にデータ構造をメモリに配置することです。オフ ヒープ メモリを使用するのは面倒ですが、レイアウトを完全に制御できます。(私のソース データは、持続性のためにメモリ マップされています) シーケンシャル アクセスでデータをストリーミングし、そのデータを最大限に活用しようとします (つまり、繰り返しアクセスを最小限に抑えます) それでも、16 コアでは、それらすべてが使用されると想定するのは困難です常に作業している 40 GB のソース データと約 80 GB の派生データがあるため、効率的です。

注: ハイエンド GPU は、信じられないほど高いメモリ帯域幅を備えているため、この問題に対処しています。トップ エンド プロセッサは 250 GB/秒を達成できますが、一般的な CPU は約 4 ~ 6 GB/秒です。それでも、それらはベクトル化された処理により適しており、引用されたピーク パフォーマンスは、マンデルブロー セットなどのメモリ アクセスがほとんどない可能性があります。

http://www.nvidia.com/object/tesla-servers.html

于 2012-12-20T09:41:12.200 に答える
9

多くの実験の後、JVM が違いを生まないことを発見しましたが、JDump の能力も発見しました。64 個のスレッドのうち 50 個が次の行にありました。

java.lang.Thread.State: RUNNABLE
    at java.util.Random.next(Random.java:189)
    at java.util.Random.nextInt(Random.java:239)
    at sun.misc.Hashing.randomHashSeed(Hashing.java:254)
    at java.util.HashMap.<init>(HashMap.java:255)
    at java.util.HashMap.<init>(HashMap.java:297)

Random.next は次のようになります

 protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
 }

最も興味深いのは、これが明らかなロックではないことです。そのため、ミューテックスを見つけるために使用したツールが機能していませんでした。

そのため、Java ハッシュマップを作成すると、アプリケーションのスケーラビリティが失われるように見えます (誇張していますが、大したことではありません)。私のアプリケーションはハッシュマップを多用するので、ハッシュマップを書き直すか、アプリケーションを書き直すかのどちらかだと思います。

これに対処する方法を確認するために、別の質問をしています。

助けてくれてありがとう

于 2012-12-22T13:47:23.177 に答える
0

割り当ての壁にぶつかっている可能性があります。つまり、プログラムはメモリ帯域幅によって制限されるオブジェクト割り当てよりも速く実行できません。

于 2012-12-23T13:29:31.890 に答える