質問

エグゼクティブサマリー:OpenMP が REAL コアのスレッドのみを使用するようにコード内で指定するにはどうすればよいでしょうか。ハイパースレッディングのものは数えませんか?

詳細な分析:長年にわたり、私は自由時間に SW 専用のオープンソース レンダラー (ラスタライザー/レイトレーサー) をコーディングしてきました。GPL コードと Windows バイナリはここから入手できます。 https://www.thanassis.space/renderer.htmlWindows、Linux、OS/X、BSD 上でコンパイルし、正常に実行されます。

私は先月レイトレーシング モードを導入しましたが、生成された画像の品質は飛躍的に向上しました。残念ながら、レイトレーシングはラスタライズよりも桁違いに遅くなります。速度を上げるために、ラスタライザの場合と同様に、レイトレーサに OpenMP (および TBB) サポートを追加し、追加の CPU コアを簡単に利用できるようにしました。ラスタライズとレイトレーシングは両方とも、スレッド化 (三角形ごとの作業 - ピクセルごとの作業) に簡単に対応できます。

自宅の Core2Duo では、2 番目のコアがすべてのモードに役立ちました。ラスタライズ モードとレイトレーシング モードの両方で 1.85 倍から 1.9 倍の速度向上が得られました。

問題: 当然のことながら、私は最高の CPU パフォーマンスを確認することに興味がありました (私は GPU も「プレイ」していますが、 予備的な CUDA ポート)、比較のためのしっかりとした根拠が必要でした。私はこのコードを、16 コア、1500 ドルの Intel スーパー プロセッサを搭載した「野獣」マシンにアクセスできる友人に渡しました。

彼はそれを「最も重い」モードであるレイトレーサー モードで実行しています...

...そして、私の Core2Duo の 5 分の 1 の速度が得られます (!)

息をのむ - 恐怖。今何があったの?

私たちはさまざまな修正やパッチなどを試し始めました...そして最終的に私たちはそれを理解しました。

OMP_NUM_THREADS 環境変数を使用すると、生成される OpenMP スレッドの数を制御できます。スレッドの数が 1 から 8 に増加するにつれて、速度は増加しました (直線的な増加に近く)。8 を超えた瞬間に速度が低下し始め、16 コアすべてが使用されたときの Core2Duo の速度の 5 分の 1 にまで落ちました。

なぜ 8 なのか?

8がその数字だったので、 本物 コア。残りの8人は…ハイパースレッディングのものです!

その理論: さて、これは私にとってはニュースでした。他のアルゴリズムでハイパースレッディングが非常に (最大 25%) 役立つのを見てきたので、これは予想外でした。どうやら、各ハイパースレッディング コアには独自のレジスタ (および SSE ユニット?) が付属しているにもかかわらず、レイトレーサは追加の処理能力を利用できなかったようです。それは私に考えさせます...

おそらく不足しているのは処理能力ではなく、メモリ帯域幅です。

レイトレーサーは、境界ボリューム階層データ構造を使用して、光線と三角形の交差を高速化します。ハイパースレッド コアが使用されている場合、ペアの各「論理コア」は、そのデータ構造内の異なる場所 (つまり、メモリ内) - そして CPU キャッシュ (ペアごとにローカル) が完全にスラッシングされます。少なくとも、それが私の理論です。どんな提案も大歓迎です。

それで、質問は次のとおりです。 OpenMP は「コア」の数を検出し、それに一致するスレッドを生成します。つまり、計算にハイパースレッド化された「コア」が含まれます。私の場合、これは速度の点で悲惨な結果を招くようです。OpenMP API (可能であれば移植可能) を使用して、ハイパースレッドコアではなく REAL コアのスレッドのみを生成する方法を知っている人はいますか?

追伸コードはオープン (GPL) で、上記のリンクから入手できます。自分のマシンで自由に再現してください。これはすべてのハイパースレッド CPU で起こると思います。

追伸投稿が長くなってしまいましたが、教育的な経験だったと思い、共有したいと思いました。

役に立ちましたか?

解決

基本的に、かなり低レベルのハードウェアの詳細を環境に問い合わせるには、かなり移植性の高い方法が必要です。そして一般に、システム コールだけからそれを行うことはできません (OS は通常、ハードウェア スレッドとコアの違いさえ認識しません)。

多くのプラットフォームをサポートするライブラリの 1 つは次のとおりです。 hwloc - Linux および Windows (およびその他)、Intel および AMD チップをサポートします。Hwloc を使用すると、ハードウェア トポロジに関するすべてを知ることができ、コアとハードウェア スレッド (hwloc 用語では PU (処理ユニット) と呼ばれます) の違いがわかります。したがって、最初にこのライブラリを呼び出し、実際のコアの数を見つけて、omp_set_num_threads() を呼び出します (または、並列セクションの先頭にその変数をディレクティブとして追加するだけです)。

他のヒント

残念ながら、なぜこれが起こっているのかについてのあなたの仮定は、おそらく正しい可能性が高いです。確かに、あなたはプロファイルツールを使用する必要があります - しかし、私は以前にレイトレースでこれを見たことがあるので、それは驚くことではありません。いずれにせよ、現在、OpenMPから一部のプロセッサが「本物」であり、一部はハイパースレッドされていることを判断する方法はありません。これを決定するためにいくつかのコードを記述してから、自分で番号を設定できます。ただし、OpenMPがプロセッサ自体のスレッドをスケジュールしないという問題がまだあります。これにより、OSがそれを行うことができます。

OpenMP ARB言語委員会では、ユーザーが自分の環境を決定して実行する方法を言うための標準的な方法を定義しようとする作業がありました。この時点で、この議論はまだ激怒しています。多くの実装では、実装定義済みの環境変数を使用して、スレッドをプロセッサに「バインド」することができます。ただし、ユーザーはプロセッサの番号付けと、どのプロセッサが「リアル」とハイパースレッドされているかを知る必要があります。

問題は、OMPがHTを使用する方法です。メモリ帯域幅ではありません! 2.6GHz HT PIVで簡単なループを試しました。結果は素晴らしいです...

OMPで:

    $ time ./a.out 
    4500000000
    real    0m28.360s
    user    0m52.727s
    sys 0m0.064s

ompなし:$ time ./a.out 4500000000

    real0   m25.417s
    user    0m25.398s
    sys 0m0.000s

コード:

    #include <stdio.h>
    #define U64 unsigned long long
    int main() {
      U64 i;
      U64 N = 1000000000ULL; 
      U64 k = 0;
      #pragma omp parallel for reduction(+:k)
      for (i = 0; i < N; i++) 
      {
        k += i%10; // last digit
      }
      printf ("%llu\n", k);
      return 0;
    }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top