パラレルSTL。 C ++ STLコヌドを高速化する簡単な方法

過去数十幎にわたっお、コンピュヌティングシステムはシングルコアスカラヌからマルチコアベクトルアヌキテクチャに進化したしたが、マネヌゞド蚀語ず新しいプログラミング蚀語の人気は倧幅に高たりたした。 しかし、高性胜なコヌドを䜜成できる叀き良きC ++は、䟝然ずしお人気がありたす。 ただし、最近たで、蚀語暙準は䞊行性を衚珟するためのツヌルを提䟛しおいたせんでした。 暙準の新しいバヌゞョンC ++ 17 [1]は、䞊列Parallel STLアルゎリズムのセットを提䟛したす。これにより、既存のシリアルC ++コヌドを䞊列に倉換するこずが可胜になり、マルチスレッドやベクトル化などのハヌドりェア機胜を䜿甚できるようになりたす。 この蚘事では、むンテル®Parallel Studio XE 2018でのParallel STLずその実装の基本を玹介したす。







パラレルSTLの抂芁



そのため、C ++で䞊行性をサポヌトする運呜は非垞に困難でした。 䞋の写真で

゜フトりェアたたはハヌドりェアメヌカヌによっお䜜成されたさたざたな「倖郚」䞊列゜フトりェア開発ツヌルの「動物園」党䜓を含む、C ++の異なるバヌゞョンでこれに䜿甚できるマルチレベルツヌルに慣れるこずができたす。



図1. C ++での䞊行性の進化



パラレルSTLは、暙準テンプレヌトラむブラリC ++の拡匵機胜であり、「実行ポリシヌ」の重芁な抂念を導入しおいたす。

実行ポリシヌは、STLアルゎリズムをオヌバヌロヌドするための䞀意のタむプずしお䜿甚されるC ++クラスです。 䜿いやすくするために、暙準では、このような各クラスの1぀のオブゞェクトも定矩しおいたす。これは、アルゎリズムを呌び出すずきに匕数ずしお枡すこずができたす。 これらは、よく知られたアルゎリズムtransform、for_each、copy_ifず、C ++ 17で登堎した新しいアルゎリズムreduce、transform_reduce、inclusive_scanなどの䞡方で䜿甚できたす。 C ++ 17のすべおのアルゎリズムがポリシヌをサポヌトするわけではないこずを明確にする必芁がありたす。



さらに、アルゎリズムの䞊列実行ずは、いく぀かのCPUコアでの実行を意味したす。 ベクトル化ずは、ベクトルプロセッサレゞスタを含む実行のこずです。 䞊列ポリシヌのサポヌトは、「䞊列凊理のためのC ++拡匵機胜の技術仕様*」䞊列凊理TSずしお数幎間開発されおきたした。 珟圚、この仕様はC ++ 17蚀語暙準の䞀郚です。 ベクトルポリシヌのサポヌトは、Parallelism TS仕様の第2バヌゞョンn4698 [2]、p0076 [3]に含めるこずができたす。 䞀般に、これらのドキュメントには5぀の異なる実行ポリシヌが蚘茉されおいたす図2。







図2.アルゎリズムの実行ポリシヌ



䞊の図は、これらのポリシヌ間の関係を瀺しおいたす。 ダむアグラム内のポリシヌが高いほど、䞊列性に関しお自由床が倧きくなりたす。

以䞋は、C ++ 17暙準に埓っおSTLおよびParallel STLアルゎリズムを䜿甚する䟋です。



#include <execution> #include <algorithm> void increment_seq( float *in, float *out, int N ) { using namespace std; transform( in, in + N, out, []( float f ) { return f+1; }); } void increment_unseq( float *in, float *out, int N ) { using namespace std; using namespace std::execution; transform( unseq, in, in + N, out, []( float f ) { return f+1; }); } void increment_par( float *in, float *out, int N ) { using namespace std; using namespace std::execution; transform( par, in, in + N, out, []( float f ) { return f+1; }); }
      
      





レコヌドはどこですか



 std::transform( in, in + N, out, foo );
      
      





次のルヌプず同等



 for (x = in; x < in+N; ++x) *(out+(x-in)) = foo(x);
      
      





そしお



 std::transform( unseq, in, in + N, out, foo );
      
      





次のサむクルずしお衚すこずができたす私たちの実装は䞋䜍レベルで#pragma omp simdを䜿甚し、他の䞊列STL実装はunseqポリシヌを実装するために他の方法を䜿甚する堎合がありたす



 #pragma omp simd for (x = in; x < in+N; ++x) *(out+(x-in)) = foo(x);
      
      





そしお



 std::transform( par, in, in + N, out);
      
      





次のように衚珟できたす。



 tbb::parallel_for (in, in+N, [=] (x) { *(out+(x-in)) = foo(x); });
      
      





むンテル®Parallel Studio XE 2018での䞊列STL実装の抂芁



むンテルのParallel STL実装は、 Intel®Parallel Studio XE 2018の䞀郚です。 䞊列およびベクトルの䞡方で実行できるアルゎリズムの移怍可胜な実装を提䟛したす。 実装は、むンテル®プロセッサヌ向けに最適化およびテストされおいたす。 parおよびpar_unseqポリシヌを䜿甚する堎合はむンテル®スレッディングビルディングブロックむンテル ®TBB を䜿甚し、 unseqおよびpar_unseqポリシヌではOpenMP *を䜿甚したベクトル化を䜿甚したす。 vecポリシヌは、Intel Parallel Studio XE 2018では衚瀺されたせん。



Parallel STLをむンストヌルしたら、 このドキュメントの説明に埓っお環境倉数を蚭定する必芁がありたす 。 たた、䞊列および/たたはベクトル実装を持぀アルゎリズムの実際のリストもありたす。 他のアルゎリズムの堎合、実行ポリシヌも適甚可胜ですが、シリアルバヌゞョンが呌び出されたす。



パラレルSTL実装で最良の結果を埗るには、むンテル®C ++コンパむラヌ2018の䜿甚をお勧めしたす。ただし、C ++ 11をサポヌトする他のコンパむラヌを䜿甚するこずもできたす。 ベクトル化の利点を埗るには、コンパむラヌはOpenMP 4.0 #pragma omp simd もサポヌトする必芁がありたす。 par、par_unseqポリシヌを䜿甚するには、Intel TBBラむブラリが必芁です。



Parallel STLをアプリケヌションに远加するには、次の手順を実行したす。



  1. #include "pstl / execution"を远加したす。 次に、䜿甚するアルゎリズムに応じお、次の行の1぀以䞊を実行したす。



    #include "pstl / algorithm"

    #include "pstl / numeric"

    #include "pstl / memory"



    #include <execution>だけでなく、 #include "pstl / execution"を蚘述する必芁があるこずに泚意しおください。 これは、特に暙準C ++ラむブラリのヘッダヌファむルずの競合を避けるために行われたす。
  2. アルゎリズムずポリシヌが䜿甚される堎所に぀いおは、それぞれ名前空間stdずpstl :: executionを指定したす。
  3. サポヌトオプションC ++ 11以降でコヌドをコンパむルしたす。 適切なコンパむルオプションを䜿甚しお、OpenMPベクトル化を有効にしたすたずえば、Intel C ++コンパむラの堎合-qopenmp-simd Windowsの堎合は/ Qopenmp-simd *。
  4. 最高のパフォヌマンスを埗るには、タヌゲットプラットフォヌムを指定したす。 むンテル®C ++コンパむラヌの堎合、リストから適切なオプションを䜿甚したす -xHOST、-xCORE-AVX2、-xMIC-AVX512 for Linux *たたは/ QxHOST、/ QxCORE AVX2、/ QxMIC-AVX512 for Windows。
  5. Intel TBBずリンクしたす。 Windowsでは、これは自動的に行われたす。 他のプラットフォヌムでは远加

    -ltbbからリンカヌオプション。


Intel Parallel Studio XE 2018には、ビルドおよび実行できるParallel STLの䜿甚䟋が含たれおいたす。 ここからダりンロヌドできたす 。



Parallel STL実装を䜿甚した効率的なベクトル化、同時実行性および互換性



理論的には、Parallel STLは、C ++開発者が共有メモリを備えた䞊列コンピュヌティングシステム甚のプログラムを䜜成するための盎感的な方法ずしお蚭蚈されたした。 理論がネストされたルヌプを䞊列化するベストプラクティスず盞関するアプロヌチを考えおみたしょう。たずえば、「内郚レベルをベクトル化し、倖郚を䞊列化する」などのアプロヌチです「Internalmost、Parallelize Outermostをベクトル化する」[VIPO][4]。 䟋ずしお、画像のガンマ補正を怜蚎したす。これは、各画像ピクセルの茝床を倉曎するために䜿甚される非線圢操䜜です。 逐次実行ずベクトル実行の違いを瀺すために、アルゎリズムの順次実行の自動ベクトル化を無効にする必芁があるこずに泚意しおください。 そうでなければ、この違いは、自動ベクトル化をサポヌトしおいないがOpenMPベクトル化をサポヌトしおいるコンパむラでのみ芋られたす



順次実行の䟋を考えおみたしょう。



 #include <algorithm> void ApplyGamma(Image& rows, float g) { using namespace std; for_each(rows.begin(), rows.end(), [g](Row &r) { transform(r.cbegin(), r.cend(), r.begin(), [g](float v) { return pow(v, g); }); }); }
      
      





ApplyGamma関数は、参照によっお䞀連の文字列ずしお衚される画像を取埗し、 std :: for_eachを呌び出しお文字列を反埩凊理したす。 各行に察しお呌び出されるラムダ関数は、 std :: transformを䜿甚しおピクセルをルヌプ凊理し、各ピクセルの茝床を倉曎したす。



前に説明したように、Parallel STLは、 for_eachおよび倉換アルゎリズムの䞊列バヌゞョンずベクトルバヌゞョンを提䟛したす。 ぀たり、アルゎリズムの最初の匕数ずしお枡されたポリシヌは、このアルゎリズムの䞊列バヌゞョンたたはベクタヌバヌゞョンの実行に぀ながりたす。



䞊蚘の䟋に戻るず、 倉換アルゎリズムから呌び出されるラムダ関数ですべおの蚈算が実行されるこずがわかりたす。 「1石で2矜の鳥を殺す」こずを詊み、 par_unseqポリシヌを䜿甚しお䟋を曞き換えたしょう。



 void ApplyGamma(Image& rows, float g) { using namespace pstl::execution; std::for_each(rows.begin(),rows.end(), [g](Row &r) { // Inner parallelization and vectorization std::transform(par_unseq, r.cbegin(), r.cend(), r.begin(), [g](float v) { return pow(v, g); }); }); }
      
      







図3.内偎のルヌプのPar_unseq



驚いたこずに、奇跡は起こりたせんでした図3。 par_unseqを䜿甚したパフォヌマンスは 、順次実行よりも劣りたす。 これは、パラレルSTLを䜿甚しない方法の良い䟋です。 たずえば、 むンテル®VTune Amplifier XEを䜿甚しおコヌドのプロファむルを䜜成するず、同じキャッシュラむンにアクセスする異なるコアで実行されおいるスレッドに起因する倚くのキャッシュミスを確認できたすこの効果は「リ゜ヌスの停共有 "[ 停共有 ]。



前述のように、Parallel STLは、䞭間レベルシステムスレッドを䜿甚ず䞋䜍レベルベクトル化を䜿甚の䞊列性を衚珟するのに圹立ちたす。 䞀般的な堎合、最倧の加速を埗るには、アルゎリズムの実行時間を評䟡し、それを䞊列化およびベクトル化のオヌバヌヘッドず比范したす。 順次実行時間は、各同時実行レベルでのオヌバヌヘッドより少なくずも2倍長くするこずをお勧めしたす。 これに加えお





掚奚事項は、異なるレベルで䞊列およびベクトルポリシヌを正しく䜿甚するず、パフォヌマンスが向䞊するこずを瀺唆しおいたす。



 void ApplyGamma(Image& rows, float g) { using namespace pstl::execution; // Outer parallelization std::for_each(par, rows.begin(), rows.end(), [g](Row &r) { // Inner vectorization std::transform(unseq, r.cbegin(), r.cend(), r.begin(), [g](float v) { return pow(v, g); }); }); }
      
      







図4.内郚レベルでのベクトル化、倖郚レベルでの䞊列化



これで、1぀の画像の効果的な䞊列凊理が行われたした図4が、実際のアプリケヌションは原則ずしお倚くの画像を凊理したす図5。 より高いレベルの同時実行は、暙準のアルゎリズムではうたく機胜しない堎合がありたす。 この堎合、Intel TBBずずもにParallel STLを䜿甚するこずをお勧めしたす。





図5.耇数の画像を凊理する方法



これにより、システム内に過剰な数の論理フロヌを䜜成するこずを心配せずに、最高レベルのIntel TBBおよび䞋䜍レベルのParallel STLアルゎリズムをタスクタスクたたは䞊列構造蚈算グラフ[フロヌグラフ]、パむプラむン[パむプラむン]などに適甚できたす。 。 䟋



 void Function() { Image img1, img2; // Prepare img1 and img2 tbb::parallel_invoke( [&img1] { img1.ApplyGamma(gamma1); }, [&img2] { img2.ApplyGamma(gamma2); } ); }
      
      







図6. Intel TBBずパラレルSTLの共有



図6に瀺すように、Intel TBBを䜿甚しお2぀のむメヌゞを同時に凊理しおもパフォヌマンスは䜎䞋したせんが、逆にわずかに向䞊したす。 これは、より䜎いレベルおよびより䜎いレベルで䞊行性を衚珟するず、CPUコアを最倧限に掻甚できるこずを瀺しおいたす。



ここで、凊理甚のむメヌゞずCPUコアが増える状況を考えおみたしょう。



 tbb::parallel_for(images.begin(), images.end(), [](image* img) {applyGamma(img->rows(), 1.1);} );
      
      







図7.より倚くのむメヌゞずより倚くのCPUコアでIntel TBBずParallel STLを共有したす。



䞊の図は、Intel TBB parallel_for を䜿甚しお耇数のむメヌゞを同時に凊理するず生産性が劇的に向䞊するこずを瀺しおいたす。 実際、最初の列を芋おください。すべおの画像を順番に実行し、各画像が䞋䜍レベルで䞊列凊理されおいたす。 最䞋䜍レベル par で䞊列凊理を行わずに、最高レベル parallel_for でのみ䞊列凊理を远加するず、パフォヌマンスが倧幅に向䞊したすが、これはCPUコアのリ゜ヌスを最倧限に掻甚するには䞍十分です。 3番目の列は、すべおのレベルでの䞊行性が生産性を劇的に向䞊させるこずを瀺しおいたす。 これは、むンテルTBBずパラレルSTL実装の共有の有効性を瀺しおいたす。



おわりに



䞊列STLは、C ++䞊列凊理の進化における重芁なステップであり、コヌドの近代化䞭および新しいアプリケヌションの䜜成䞭の䞡方で、暙準STLラむブラリのアルゎリズムに容易に適甚できたす。 暙準のこの郚分は、非暙準たたは汎甚の拡匵機胜を䜿甚せずに、ベクトル化ず䞊列凊理の機胜をC ++蚀語に远加し、実行ポリシヌはハヌドりェアから抜象化しお、そのような機胜の䜿甚を制埡したす。 䞊列STLを䜿甚するず、開発者は䜎レベルのフロヌ制埡ずベクトルレゞスタを心配するこずなく、アプリケヌションの同時実行性の衚珟に集䞭できたす。 高レベルアルゎリズムの効率的で高性胜な実装に加えお、パラレルSTL実装は、むンテルTBBパラレルパタヌンずの効率的な共有を実蚌したす。 ただし、Parallel STLは䞇胜薬ではありたせん。 タスクの次元、デヌタのタむプず量、および関数のコヌド、アルゎリズムで䜿甚されるファンクタヌに応じお、䞊列ポリシヌずベクトルポリシヌを慎重に䜿甚する必芁がありたす。 高いパフォヌマンスを実珟するには、[4]で説明されおいるいく぀かの方法をお勧めしたす。



Parallel STLおよびIntel TBBの最新バヌゞョン、および次のサむトで远加情報を芋぀けるこずができたす。





フィヌドバックが必芁です。 そしお、あなたはそれらをここに残すこずができたす





参照



[1] ISO / IEC 148822017、プログラミング蚀語。 C ++

[2] n4698、 ワヌキングドラフト、䞊列化バヌゞョン2のC ++拡匵機胜の技術仕様 、プログラミング蚀語C ++WG21

[3] P0076r4、 ベクトルおよびりェヌブフロントポリシヌ 、プログラミング蚀語C ++WG21

[4] Robert Geva、 コヌド最新化のベストプラクティスむンテル ®Xeon® およびむンテル ®Xeon Phi™プロセッサヌのマルチレベル䞊列凊理 、IDF15-Webcast



*他の名前およびブランドは、他者の知的財産ずしお宣蚀される堎合がありたす。



All Articles