18

エグゼクティブサマリー:OpenMPがREALコアのスレッドのみを使用する、つまりハイパースレッディングコアをカウントしないようにコードで指定するにはどうすればよいですか?

詳細な分析:何年にもわたって、私は空き時間にSWのみのオープンソースレンダラー(ラスタライザー/レイトレーサー)をコーディングしてきました。GPLコードとWindowsバイナリは、次の場所から入手できます 。https ://www.thanassis.space/renderer.html Windows、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コアのスレッドのみを生成し、ハイパースレッドのスレッドは生成しない方法を知っている人はいますか?

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

PPS投稿の長さすみません、それは教育的な経験だと思い、共有したいと思いました。

4

3 に答える 3

6

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

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

于 2011-01-27T22:50:41.973 に答える
3

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

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

于 2011-01-27T16:07:12.070 に答える
1

問題は、OMPがHTをどのように使用するかです。メモリ帯域幅ではありません!2.6GHzHTPIVで単純なループを試しました。結果は素晴らしいです...

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;
    }
于 2011-06-04T10:17:16.790 に答える