待って、待って、待った! Openmp 4.0









新しいOpenMP仕様はそれぞれ、既存の機能に非常に便利で必要な追加を導入しています。 たとえば、バージョン3.0では、予想されるタスクが追加され、アプリケーションを並列化するためのさらに広範なタスクを解決できるようになりました。 3.1では、タスクを処理するための多くの改善と削減が行われました。



しかし、標準4.0が現在提供しているものと比較すると、以前の技術革新はちょっと小さいように思われます。 最新バージョンでは、サポートされる同時実行の種類が拡張されました。これは、これまでに見たことのないものです。



結局のところ、並列処理が非常に異なることは誰もが知っています。 最新のプロセッサアーキテクチャがパイプライン機能とスーパースケーラリティを提供するときに、命令のレベルから始まります。 すでにこのレベルでは、アプリケーションは並列であると言えます。 しかし、これは今日のハードウェアにすでに固有のものであり、開発者に暗黙的に使用されています。 既存のプロセッサには、コアとベクターレジスタ(SSE、AVXなど)の数など、他の「スイート」もあります。



これらは、さらに2つのタイプの並行性を提供します。





実際、ここでは、共有メモリを備えたシステムで使用可能なすべてのタイプがあります。 分散システム(クラスターとMPIにこんにちは)がまだ存在することは注目に値しますが、これは今日の会話の焦点では​​ありません。 私たちはOpenMPについて話しているのですが、共有メモリを備えたシステムでは古典的に「シャープ」になっています。



さらに、最後の瞬間、つまり2013年7月までは、常にタスクの並列性についてでした。 さらに、OpenMPの使用は最も一般的な方法の1つになり、言語サポートの点で普遍的(C / C ++とFortranの両方に適しています)であり、実装の点で簡単です(ディレクティブをステップバイステップで並列化するという考え方)。



ただし、パフォーマンスに関しては、データの同時実行性も同様に重要です。 高性能が本当に重要なアプリケーションで作業している場合、インテル®コンパイラーを使用する場合、ベクトル化は重要なポイントの1つであることに注意することが重要です。 ところで、他のコンパイラも同様です。 したがって、比較的長い間、コードをベクトル化するのに役立つさまざまなディレクティブのセットがあります。つまり、SSE、AVX、AVX2、またはさらに「新鮮」および「長い」レジスタの全長を使用する命令を生成します。 新しいMICアーキテクチャでは、ベクトル化が重要な役割を果たすことを忘れないでください。



これは、OpenMP仕様に取り組んでいる委員会でも理解されていました。 そのため、強力な意思決定により、インテル®Cilk Plusのディレクティブと非常によく似た、データ並列処理を行うためのディレクティブは、インテル®コンパイラーで作業する人々によく知られていました。



それで登場したもの。

これで、以前に存在したディレクティブを介して作業を異なるフローに「分散」できるだけでなく、対応するコードのベクトル化も提供できます。 そして、これらはすべて、OpenMPのフレームワーク内にあります。つまり、標準は遅かれ早かれ、すべてをサポートするよう努めているため、特定のコンパイラを参照しません。



まず、簡単な例でCilk Plusでどのようになったかを示したいと思います。

あるサイクルがあるとしましょう:



void add_floats(float *a, float *b, float *c, float *d, float *e, int n){ int i; for (i=0; i<n; i++){ a[i] = a[i] + b[i] + c[i] + d[i] + e[i]; } }
      
      





したがって、vec-reportキーでコンパイルして、ベクトル化されているかどうかを確認すると、ベクトル化されていないことがわかります。 その理由は、コンパイラが保守的であるためです。ベクトル化が危険であると「感じる」場合、「安全にプレイする」ことを望み、何もしません。 私たちの場合、これは関数に渡される多数のポインターによるものです。 コンパイラは、それらの一部がメモリ内に交差点を持っている可能性があると考えています。 しかし、開発者として、これが事実ではなく、このサイクルをベクトル化しても安全であることがわかっている場合は、インテルCilkPlusのプラグマsimdディレクティブを使用できます。



  #pragma simd for (i=0; i<n; i++){ a[i] = a[i] + b[i] + c[i] + d[i] + e[i];
      
      





再構築して出来上がり-サイクルはベクトル化されます! ただし、これもIntelコンパイラに固有です。 現在、このような機能はOpenMPに登場し、ディレクティブのみがより「なじみのある」形式になっています。



 #pragma omp simd
      
      





さらに、2種類の並列処理を同時に使用するための混合構成があります。



 #pragma omp parallel for simd
      
      





このサイクルのコンパイラチェックが完全に無効になっているため、「裸の」形式でデザインを使用することはあまりお勧めできません。 したがって、他のOMPディレクティブのように、コンパイラを支援する追加のオプション(ベクトルの長さ、メモリのアライメント、アクセスへのアクセスなど)を設定することが可能であり、必要です。 ちなみに、これらは既にインテルコンパイラーにあったものと非常に似ていますが、わずかな変更があります。



別の非常に便利なものが追加されました-いわゆる要素関数。 関数がループで呼び出された場合、一般的な場合、このループがベクトル化されないことは秘密ではありません。 Cilk Plusは、魔法の__declspec(ベクトル)を使用しました。これにより、最も普通の関数(結果のベクトルを「与える」ことができる関数)から基本関数を作成できます。 結果として、サイクルで呼び出すことができ、その後のベクトル化が成功します。 OpenMPでは、次のようになります。



 #pragma omp declare simd
      
      





そして今、Fortranの開発者は喜んでいます。なぜなら、そのようなサポート(C / C ++のみのCilk Plus)が最後までなかったからです。

したがって、1つの仕様のフレームワーク内ですべてのタイプの並列処理を使用する強力なツールが開発者の手に現れました。これは朗報です。 しかし、これが登場したすべてではありません。 現在非常に普及しているアクセラレータを備えた「ハイブリッド」システムのプログラミングモデルは無視されていません。 GPUであろうと、その他の未知の獣であろうと、どのタイプのアクセラレータでも問題ありません。現在、その使用モデルは開発者の観点から一般的であり、新しいOpenMPの一部でもあります。 targetディレクティブを使用して、アクセラレーターで計算を実行し、ホストで結果を取得できるようになりました。



 #pragma omp target map(to(b:count)) map(to(c,d)) map(from(a:count)) { #pragma omp parallel for for (i=0; i<count; i++) a[i] = b[i] * c + d; }
      
      





しかし、これは別の大きなトピックであり、議論する価値があります。 ところで、OpenMPは他の標準、特にOpenCL、OpenACCなどと比較するようになりました。 そして興味深い比較が得られます-OpenMPには多くの「プラス」があります。 しかし、トピックに戻ります。

上記のすべてに加えて、他の「アメニティ」も表示されますが、さまざまな問題を解決するのに非常に役立ちますが、重要度が低いものとして除外します。 エラー処理のための新しいツール、スレッドを特定のカーネルに結び付けること、タスクの新しい機能(タスク拡張)、ユーザー定義の削減のサポート、およびその他の多くの革新について話しています。



完全な説明は OpenMP Webサイトで古典的に見つけることができます。 さて、いくつかの新しい関数をすでに部分的にサポートしている最初のコンパイラの1つがここにあります 。 いつものように、トライアル30日間バージョンも利用できます。 OpenMP 4.0の新機能をぜひお試しください!



All Articles