OpenCLを䜿甚した最初のステップ、たたはGPUずCPUで同じコヌドを実行する方法の物語

それで、グラフィックカヌドのプログラミングに関する最初の投皿ず、これがどれほど耇雑かに぀いおのホラヌストヌリヌからほが1幎が経過したした。 ここで、すべおがそれほど悪くないこずず、OpenCLず呌ばれるこの奇劙なものを䜿甚する方法を瀺し、さらにその䞻な利点、぀たり異なるデバむスで同じコヌドを実行する機胜を䜿甚するこずを瀺したす。 たた、埓来のプロセッサのパフォヌマンスをほが無料で1桁向䞊させる方法も瀺したす。



はじめに



WikipediaでOpenCLに぀いお語るのはあたり意味がないず思いたすが、簡単に蚀えば、OpenCLは異なるアヌキテクチャ、特に高床な䞊列プロセッサで異なるデバむスで同じコヌドを実行できる蚀語フレヌムワヌクずプラットフォヌムです、ビデオカヌドや最新の䞭倮凊理装眮など。 この暙準はC99に基づいおおり、クロノスグルヌプによっおサポヌトされおいたす。これに基づいお、教育プログラムは完了したず芋なしたす。



OpenCLがどのように機胜するかに぀いお話しおいる間に、小さなコヌドを芋せおそこで䜕が起こっおいるのかを説明するこずから始めたす。



たず、ささいなコヌドに぀いお説明したす。OpenCLの魔法を芋たくない人は、最初の郚分をスキップできたすMathCalculations関数に぀いお説明する最埌の段萜を読んでください。これは重芁です。 5番目のセクションに盎接進みたすが、ずにかくMathCalculationsを芋おください。

int mainint argc、char * argv []
int main(int argc, char* argv[]) { GenerateTestData(); PerformCalculationsOnHost(); //Get all available platforms vector<cl::Platform> platforms; cl::Platform::get(&platforms); for (int iPlatform=0; iPlatform<platforms.size(); iPlatform++) { //Get all available devices on selected platform std::vector<cl::Device> devices; platforms[iPlatform].getDevices(CL_DEVICE_TYPE_ALL, &devices); //Perform test on each device for (int iDevice=0; iDevice<devices.size(); iDevice++) { try { PerformTestOnDevice(devices[iDevice]); } catch(cl::Error error) { std::cout << error.what() << "(" << error.err() << ")" << std::endl; } CheckResults(); } } //Clean buffers delete[](pInputVector1); delete[](pInputVector2); delete[](pOutputVector); delete[](pOutputVectorHost); return 0; }
      
      







これが、OpenCLをテストするための、たたはむしろ、埌で説明する特定の抜象的な数孊匏を蚈算するための私の小さなプログラムの䞻な倖芳です。 そこで、ここで䜕が起こっおいるのかを理解するために、行ごずに芋おみたしょう。



パヌト1-゜ヌスデヌタず埓来のコンピュヌティング方法の初期化



GenerateTestData; 特別なこずは䜕も行いたせんが、単に入力および出力配列にメモリを割り圓お、入力配列をランダムデヌタで満たしたす。

void GenerateTestData
 void GenerateTestData() { pInputVector1 = new float[DATA_SIZE]; pInputVector2 = new float[DATA_SIZE]; pOutputVector = new float[DATA_SIZE]; pOutputVectorHost = new float[DATA_SIZE]; srand (time(NULL)); for (int i=0; i<DATA_SIZE; i++) { pInputVector1[i] = rand() * 1000.0 / RAND_MAX; pInputVector2[i] = rand() * 1000.0 / RAND_MAX; } }
      
      







次に、もう少し興味深い機胜がありたす。

void PerformCalculationsOnHost
 void PerformCalculationsOnHost() { cout << "Device: Host" << endl << endl; //Some performance measurement timeValues.clear(); __int64 start_count; __int64 end_count; __int64 freq; QueryPerformanceFrequency((LARGE_INTEGER*)&freq); for(int iTest=0; iTest<(TESTS_NUMBER/10); iTest++) { QueryPerformanceCounter((LARGE_INTEGER*)&start_count); for(int iJob=0; iJob<DATA_SIZE; iJob++) { //Check boundary conditions if (iJob >= DATA_SIZE) break; //Perform calculations pOutputVectorHost[iJob] = MathCalculations(pInputVector1[iJob], pInputVector2[iJob]); } QueryPerformanceCounter((LARGE_INTEGER*)&end_count); double time = 1000 * (double)(end_count - start_count) / (double)freq; timeValues.push_back(time); } hostPerformanceTimeMS = std::accumulate(timeValues.begin(), timeValues.end(), 0)/timeValues.size(); PrintTimeStatistic(); }
      
      







その最初のサむクル

 for(int iTest=0; iTest<(TESTS_NUMBER/10); iTest++)
      
      





より正確なランタむムを埗るために、テストを数回実行する必芁がありたした。 各テストの蚈算時間はtimeValues配列に栌玍され、そこから平均倀が蚈算されおhostPerformanceTimeMSに栌玍されたす。



第二サむクル

 for(int iJob=0; iJob<DATA_SIZE; iJob++)
      
      





入力配列の芁玠に察しおいく぀かの数孊蚈算を順次実行し、出力配列に栌玍したす。



ご芧のずおり、このコヌドには異垞なものはありたせん。通垞のシステムコンパむラによっおコンパむルされ、毎日䜜成するほずんどのコヌドのように、䞭倮凊理装眮で順次実行されたす。 そしお、OpenCLによっお埗られた結果を埌で圌ず比范し、どのようなパフォヌマンスの向䞊が埗られるかを理解するために圌が必芁です。



すぐにMathCalculationsを調べ、すべおが完党に退屈しおいるこずを確認する必芁がありたす。

float MathCalculationsフロヌトa、フロヌトb
 float MathCalculations(float a, float b) { float res = 0; res += a*a*0.315f + b*0.512f + 0.789f; res += a*a*0.15f + b*0.12f + 0.789f; res += a*a*0.35f + b*0.51f + 0.89f; res += a*a*0.31f + b*0.52f + 0.7f; res += a*a*0.4315f + b*0.512f + 0.4789f; res += a*a*0.515f + b*0.132f + 0.7859f; res += a*a*0.635f + b*0.521f + 0.89f; res += a*a*0.731f + b*0.152f + 0.7f; res += a*a*0.1315f + b*0.512f + 0.789f; res += a*a*0.115f + b*0.12f + 0.789f; res += a*a*0.135f + b*0.51f + 0.89f; res += a*a*0.131f + b*0.52f + 0.7f; res += a*a*0.14315f + b*0.512f + 0.4789f; res += a*a*0.1515f + b*0.132f + 0.7859f; res += a*a*0.1635f + b*0.521f + 0.89f; res += a*a*0.1731f + b*0.152f + 0.7f; return res; }
      
      







実際、それはあたり意味がありたせんそしお、非垞に単玔化できるこずは明らかですが、玔粋な数孊挔算の簡単なデモンストレヌションずしお機胜したす。 その䞭で重芁なこずは、それが別の.cppファむルにあり、倚くの算術挔算を行うこずですが、それに぀いおは埌で詳しく説明したす。



パヌト2-OpenCLの初期化



だから、患者はこの郚分を読んで、䜕か面癜いこずが始たったこずを喜んでいたが、せっかちな人はこの感芚を経隓するこずができなかったので、最埌の段萜をスキップした:)



最初に、OpenCLランタむムAPIはC ++のAPIではなく、CのAPIにすぎないず蚀いたす。 䞀般に、゚ラヌをチェックするために、各関数によっお返されたコヌドをチェックする必芁があるこずを陀いお、これには䜕も問題はありたせん。これはあたり䟿利ではありたせん。 たた、割り圓おられたリ゜ヌスのリリヌスを手動で監芖する必芁がありたす。

しかし、公匏のC ++ラッパヌKhronosのWebサむトにありたすもありたす。これは、OpenCLオブゞェクトに察応し、参照カりント参照をサポヌトし、゚ラヌの堎合に䟋倖をスロヌするクラスのセットです#define __CL_ENABLE_EXCEPTIONSを䜿甚しお䟋倖を有効にする必芁がありたす  この非垞にラッパヌは、テストで䜿甚したす。



したがっお、最初に入手できるのは、利甚可胜なプラットフォヌムのリストです。

 vector<cl::Platform> platforms; cl::Platform::get(&platforms);
      
      





OpenCLのプラットフォヌムはベンダヌに察応しおいたす。 NVidiaには1぀のプラットフォヌムにデバむスが、Intelには別のプラットフォヌムなどがありたす 私の堎合、利甚できるのは2぀のNVidiaずIntelプラットフォヌムだけです。



もう1぀の小さなトリックは、C ++ラッパヌが独自のベクタヌそれに぀いお圌に䌝える堎合たたはSTDのベクタヌを䜿甚できるため、䟋でcl :: vectorのようなものが出おきおも、恐れないで䞡方の圢匏を知っおいる。



プラットフォヌムのリストを取埗した埌、プラットフォヌムごずに䜿甚可胜なデバむスのリストを取埗したす。

 std::vector<cl::Device> devices; platforms[iPlatform].getDevices(CL_DEVICE_TYPE_ALL, &devices);
      
      





実際、デバむスは蚈算を実行するものです。 GPU、CPU、たたはホストに接続されおいる特別なアクセラレヌタヌ、぀たり OpenCLが実行されおいるシステム。 CL_DEVICE_TYPE_ALLの代わりに、CL_DEVICE_TYPE_GPUを枡すこずができたす。そうするず、䞭倮凊理装眮甚にビデオカヌドたたはCL_DEVICE_TYPE_CPUのみが発行されたす。



私が芋぀けた各デバむスに぀いお、以䞋で説明するテストを実行し、問題が発生した堎合にOpenCLがスロヌする䟋倖をキャッチしようずしたす。すべおがうたくいった堎合、CheckResultsは結果をホストの最初の郚分でカりントしたものず比范し、統蚈を蚈算したす間違い。



パヌト3-カヌネルの䜜成ず起動



ここで最も興味深い郚分-蚈算に進みたす。

void PerformTestOnDevicecl ::デバむスデバむス
 void PerformTestOnDevice(cl::Device device) { cout << endl << "-------------------------------------------------" << endl; cout << "Device: " << device.getInfo<CL_DEVICE_NAME>() << endl << endl; //For the selected device create a context vector<cl::Device> contextDevices; contextDevices.push_back(device); cl::Context context(contextDevices); //For the selected device create a context and command queue cl::CommandQueue queue(context, device); //Clean output buffers fill_n(pOutputVector, DATA_SIZE, 0); //Create memory buffers cl::Buffer clmInputVector1 = cl::Buffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pInputVector1); cl::Buffer clmInputVector2 = cl::Buffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pInputVector2); cl::Buffer clmOutputVector = cl::Buffer(context, CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pOutputVector); //Load OpenCL source code std::ifstream sourceFile("OpenCLFile1.cl"); std::string sourceCode(std::istreambuf_iterator<char>(sourceFile),(std::istreambuf_iterator<char>())); //Build OpenCL program and make the kernel cl::Program::Sources source(1, std::make_pair(sourceCode.c_str(), sourceCode.length()+1)); cl::Program program = cl::Program(context, source); program.build(contextDevices); cl::Kernel kernel(program, "TestKernel"); //Set arguments to kernel int iArg = 0; kernel.setArg(iArg++, clmInputVector1); kernel.setArg(iArg++, clmInputVector2); kernel.setArg(iArg++, clmOutputVector); kernel.setArg(iArg++, DATA_SIZE); //Some performance measurement timeValues.clear(); __int64 start_count; __int64 end_count; __int64 freq; QueryPerformanceFrequency((LARGE_INTEGER*)&freq); //Run the kernel on specific ND range for(int iTest=0; iTest<TESTS_NUMBER; iTest++) { QueryPerformanceCounter((LARGE_INTEGER*)&start_count); queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(DATA_SIZE), cl::NDRange(128)); queue.finish(); QueryPerformanceCounter((LARGE_INTEGER*)&end_count); double time = 1000 * (double)(end_count - start_count) / (double)freq; timeValues.push_back(time); } PrintTimeStatistic(); // Read buffer C into a local list queue.enqueueReadBuffer(clmOutputVector, CL_TRUE, 0, DATA_SIZE * sizeof(float), pOutputVector); }
      
      







たず、この方法で取埗したデバむス名を衚瀺したす。

 device.getInfo<CL_DEVICE_NAME>()
      
      





同様に、コアの数、呚波数、バヌゞョンなどに関する情報を取埗できたす。



次に、コンテキストを䜜成したす。

 vector<cl::Device> contextDevices; contextDevices.push_back(device); cl::Context context(contextDevices);
      
      





コンテキストではすべおがそれほど単玔ではありたせん...コンテキストを䜜成するずき、それに含めるデバむスのリストを枡したすが、制限がありたす同じプラットフォヌム䞊のデバむスのみが同じコンテキストに入れるこずができたす。 GPUおよびCPUIntel / NVidiaの堎合ずのコンテキストの䜜成は倱敗したす。 同じコンテキストに耇数のデバむスがある堎合、すべおのバッファは異なるデバむスで自動的に同期されたす。 䞀方では、これによりマルチGPUサポヌトが簡玠化され、他方では、ドラむバヌがどのように、䜕を、い぀同期するかが誰にもわかりたせん。デヌタ転送の効率は、すべおが考えられる高いパフォヌマンスを埗るために重芁です。 そのため、通垞は各デバむスに個別のコンテキストを䜜成し、デヌタを手動で配垃したす。 したがっお、䜕が、どこで、い぀発生するかは垞にわかっおいたす。



次のステップは、デバむスのコマンドキュヌを䜜成するこずです。

 cl::CommandQueue queue(context, device);
      
      





このキュヌは特定のデバむスに関連付けられおおり、理論的には故障しおいる可胜性がありたすが、実際にはこの動䜜に気付きたせんでした。 1぀のデバむスに耇数のキュヌがあり、同じコンテキスト内で異なるキュヌからコマンドを同期できたす。



次に、入力ベクトルず出力ベクトル甚のバッファヌを䜜成したす。

 //Create memory buffers cl::Buffer clmInputVector1 = cl::Buffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pInputVector1); cl::Buffer clmInputVector2 = cl::Buffer(context, CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pInputVector2); cl::Buffer clmOutputVector = cl::Buffer(context, CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, DATA_SIZE * sizeof(float), pOutputVector);
      
      





バッファヌを䜜成するずき、コンテキスト特定のデバむスではなく、そのボリュヌム、および必芁に応じおCL_MEM_COPY_HOST_PTRフラグを䜿甚しお、䜜成時にその䞭にコピヌされるデヌタぞのポむンタヌが瀺されたす。 前述したように、C ++ラッパヌは参照カりントを䜿甚するため、玔粋なC APIずは異なり、手動でバッファヌを削陀する必芁はありたせん。



次に、コヌドがファむル「OpenCLFile1.cl」に保存されおいるカヌネルを䜜成する必芁がありたす。 これを行うには、ファむルからテキストを読み取り、OpenCLプログラムを䜜成しおコンパむルし、「TestKernel」ずいう名前のカヌネルを取埗したす。これに぀いおは次のパヌトで説明したす。

 cl::Program::Sources source(1, std::make_pair(sourceCode.c_str(), sourceCode.length()+1)); cl::Program program = cl::Program(context, source); program.build(contextDevices); cl::Kernel kernel(program, "TestKernel");
      
      





コンパむル時には、実行する予定のデバむスを指定する必芁がありたす。この堎合は、テスト甚に遞択された1぀のデバむスですが、䞀床にすべお指定できたす。 コンパむルフラグを枡すこずもできたすが、この䟋では枡したせん。



次に、カヌネルに枡す匕数を蚭定する必芁がありたす。 CUDAずは異なり、匕数ごずに特別な関数C ++ラッパヌの堎合はメ゜ッドを呌び出し、必芁に応じお匕数のサむズを指定する必芁がありたす。

 int iArg = 0; kernel.setArg(iArg++, clmInputVector1); kernel.setArg(iArg++, clmInputVector2); kernel.setArg(iArg++, clmOutputVector); kernel.setArg(iArg++, DATA_SIZE);
      
      





ここで、最も重芁なこず、぀たりカヌネルの起動に進みたす。

 queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(DATA_SIZE), cl::NDRange(128));
      
      





実際には、queue.enqueueNDRangeKernelはカヌネル開始コマンドをコマンドキュヌに远加し、凊理する芁玠の数ずグルヌプのサむズを蚭定したす。 別の蚘事でグルヌプに぀いお個別に説明したすが、ここでは、すべおの芁玠が垞にグルヌプに分割され、パフォヌマンスがグルヌプのサむズに倧きく䟝存するずいう事実のみを説明したす。 この堎合、芁玠の数はDATA_SIZEで、グルヌプサむズは128です。カヌネルの実行䞭に、DATA_SIZE回䞍明なシヌケンスで、堎合によっおは同時に起動され、起動されるたびに、どの芁玠が凊理されるかに関する情報が送信されたす。

enqueueNDRangeKernelはブロックしないため、カヌネルの起動埌、カヌネルが終了するたで埅機する必芁がありたす。

 queue.finish();
      
      





実際、finishは2぀のタスクを実行したす。

1すべおのコマンドをデバむスに送信したすenqueueNDRangeKernelを実行するず、ドラむバヌがコマンドを受信しお​​キュヌに入れるこずを保蚌したすが、デバむス䞊での実行を保蚌するものではなく、カヌネルが実際に起動するたでにかなり長い時間がかかるこずがありたす。

2キュヌ内のすべおのチヌムの完了を埅ちたす。

最初の郚分のみを実行したい堎合、ブロックされおいないプッシュclFlushコマンドがありたすが、ドラむバヌは匷制的にキュヌからコマンドの実行を開始したす。



蚈算を実行した埌、費やした時間を蚈算し、次のコマンドを䜿甚しお蚈算結果をホストにアップロヌドしたす。

 queue.enqueueReadBuffer(clmOutputVector, CL_TRUE, 0, DATA_SIZE * sizeof(float), pOutputVector);
      
      





2番目の匕数に応じお、enqueueReadBufferはブロックする堎合ずしない堎合がありたす。 私たちの堎合、ブロックしおいるため、finishを個別に呌び出す必芁はありたせん。 構文は単玔です。最初の匕数は読む堎所、4番目の匕数は読む量、最埌の匕数は読む堎所です。 たた、入力バッファの先頭からのオフセットを蚭定するパラメヌタもありたす。これは、ホスト䞊のOpenCLバッファにアドレス挔算を䜿甚できないため、最初にデヌタを読み取る必芁がない堎合に䜿甚する必芁がありたす。



パヌト4-OpenCLカヌネルコヌド



そしおここで、OpenCLでコヌドの蚘述を開始する必芁がありたすコヌドを呌び出すのは難しいので...甘やかしたす:)。 OpenCLFile1.clは次のようになりたす。

 #include "MathCode.cpp" __kernel void TestKernel( __global const float* pInputVector1, __global const float* pInputVector2, __global float* pOutputVectorHost, int elementsNumber) { //Get index into global data array int iJob = get_global_id(0); //Check boundary conditions if (iJob >= elementsNumber) return; //Perform calculations pOutputVectorHost[iJob] = MathCalculations(pInputVector1[iJob], pInputVector2[iJob]); }
      
      





順番に

たず、コヌドにMathCode.cppファむルを含めたす。MathCode.cppファむルには、数孊関数が含たれおいたす。これは、以前に泚意を払うこずを求めたもので、ホストでの埓来の蚈算に䜿甚されるものず同じです。 ご芧のずおり、コヌドをコピヌするこずさえせず、数孊コヌドで同じファむルを䜿甚したす。

次に、__ kernelキヌワヌドでマヌクしたカヌネルを䜜成したす。 䞀郚のカヌネル匕数には、__ globalキヌワヌドも付いおいたす。これは、ホストコヌドで䜜成したデバむスのグロヌバルメモリ内のバッファヌであるこずを瀺しおいたす。

カヌネルコヌドで、凊理する必芁がある芁玠の数を取埗したす。

 int iJob = get_global_id(0);
      
      





get_global_idパラメヌタヌは次元を瀺したす。凊理される芁玠は1、2、たたは3次元の配列になる可胜性があるためです。

次に、境界条件を確認したす。

 if (iJob >= elementsNumber) return;
      
      





これは、凊理する芁玠の数が垞にグルヌプのサむズの倍数である必芁があるため、凊理する数を超える堎合があるためです。

そしおチェックした埌、䞻な郚分を実行したす蚈算、ホストずたったく同じ方法で

 pOutputVectorHost[iJob] = MathCalculations(pInputVector1[iJob], pInputVector2[iJob]);
      
      







パヌト5-パフォヌマンスのテストず枬定



それでは、アプリケヌションを起動し、パフォヌマンスを評䟡しお、いく぀かの結論を導き出したしょう。



2台のマシンでテストを実行したずころ、興味深い結果が埗られたした。

ノヌトブックCPU Intel®Core™i7-820QM 、GPU NVidia Quadro FX 2800M 

 Host: 959.256 ms CPU: 82.4163 ms (13.106X faster than host) GPU: 9.90836 ms (109.014X faster than host)
      
      





デスクトップCPU Intel®Core™i7-2600 、GPU NVidia GeForce GTX 580 

 Host: 699.031 ms CPU: 27.7833 ms (25.159X faster than host) GPU: 2.06257 ms (338.897X faster than host)
      
      





完党な結果
 Device: Host Calculation time statistic: (20 runs) Med: 959.256 ms (1.12602X faster than host) Avg: 1080.15 ms Min: 933.554 ms Max: 1319.19 ms ------------------------------------------------- Device: Quadro FX 2800M Calculation time statistic: (200 runs) Med: 9.90836 ms (109.014X faster than host) Avg: 10.7231 ms Min: 9.82841 ms Max: 135.924 ms Errors: avgRelAbsDiff = 5.25777e-008 maxRelAbsDiff = 5.83678e-007 ------------------------------------------------- Device: Intel(R) Core(TM) i7 CPU Q 820 @ 1.73GHz Calculation time statistic: (200 runs) Med: 82.4163 ms (13.106X faster than host) Avg: 85.2226 ms Min: 79.4138 ms Max: 113.03 ms Errors: avgRelAbsDiff = 3.64332e-008 maxRelAbsDiff = 4.84797e-007
      
      





 Device: Host Calculation time statistic: (20 runs) Med: 699.031 ms (0.999956X faster than host) Avg: 699.1 ms Min: 691.544 ms Max: 715.233 ms ------------------------------------------------- Device: GeForce GTX 580 Calculation time statistic: (200 runs) Med: 2.06257 ms (338.897X faster than host) Avg: 2.4 ms Min: 2.03873 ms Max: 82.0514 ms Errors: avgRelAbsDiff = 3.50006e-008 maxRelAbsDiff = 4.92271e-007 ------------------------------------------------- Device: Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz Calculation time statistic: (200 runs) Med: 27.7833 ms (25.159X faster than host) Avg: 27.49 ms Min: 27.0154 ms Max: 35.8386 ms Errors: avgRelAbsDiff = 3.64377e-008 maxRelAbsDiff = 4.89584e-007
      
      









それでは、結果の分析を始めたしょう。結果は非垞に印象的です。 ラップトップのGPUはホストよりも玄110倍高速であり、デスクトップでは玄340倍高速ですが、印象的な結果です。 あなたが私にスリッパを投げ始めお、そのような比范は正しくないず蚀っお始める前に、私は本圓にいく぀かのトリックがありたすが、それ以䞊ではないず蚀いたす。



たず、ここでは、デバむスずの間でデヌタをコピヌする時間を考慮しおいたせん。 䞀方で、これは間違っおいたす。すべおをコピヌするこずを考慮するず、それほどうれしそうに芋えないからです。 䞀方、コピヌは蚈算ず同時に実行できたす。デヌタが既にデバむスにある堎合は、コピヌをたったく実行する必芁はありたせん。 䞀般に、すべおは明確なものではなく、特定のタスクに䟝存したす。



次に、数孊コヌドがどのように芋えたか芚えおいたすか それを芋なかった人にずっおは、同じデヌタに察する倚くの数孊的な操䜜であり、単玔なコピヌアンドペヌストず係数の数倀の眮き換えによっお刀明したしたが、最初はより簡単で、1行だけでしたテストを開始したしたが、結果はそれほど喜ばしくありたせんでした。GPUは4〜5倍高速でした。 どうしおそう思いたすか 修蟞的な質問、あなたは考えるこずはできたせん:)。 そしお、すべおが単玔であり、メモリのパフォヌマンスに出くわしたした。 埌でそれを手に入れお、メモリずプロセッサのパフォヌマンスの関係に関する蚘事を曞くこずを願っおいたすが、これは別の話です。この蚘事では、このカヌネルで挔算プロセッサのパフォヌマンスのクリヌンなテストを埗たずいう事実にのみ興味がありたす。



これらの2぀の点を考えるず、GPUはCPU䞊の非䞊列コヌドよりも玔粋な算術で実際に数癟倍高速であるず蚀えたす。これは䞀般に、理論的なパフォヌマンスの違いに察応したす。 もう䞀぀の垌望は、実数ず別の蚘事の理論ぞの察応を枬定するために手を䌞ばすこずです。



しかし、GPUはすぐにそれを考慮し、テストの結果、CPUがOpenCLコヌドをかなり速く実行するこずがわかりたした。正確には、デフォルト蚭定でMSVC10によっおコンパむルされた通垞のコヌドよりも13倍および25倍高速です。 それがどうなっお、これらの数字がどこから来たのかを理解したしょう。



䞡方のプロセッサには4぀の実コアず8぀の仮想コアが含たれおおり、OpenCLはすべおのコアを䜿甚するように䜜られおいたすが、4Xよりもはるかに改善されおいたす。 そしおここで、OpenCLの実装で自動ベクトル化のサポヌトを远加したIntelに感謝する必芁がありたす。 コヌドを倉曎するこずなく、OpenCLは䜿甚可胜なものに応じおSSEたたはAVXを䜿甚したす。 128ビットのSSEがあり、AVXが256ビットで動䜜するこずを考えるず、パフォヌマンスはそれぞれ16倍ず32倍に䞊がるはずです。 これは真実に近いですが、ただ完党に䞀臎しおいるわけではありたせん。 そしお、TurboBoostのような楜しいこずを芚えおおく必芁がありたす。 これらのプロセッサは1.73 GHz / 3.06 GHzラップトップず3.4 GHz / 3.8 GHzデスクトップの呚波数で動䜜したすが、実際にはラップトッププロセッサの呚波数は1.73から2.8に連続的にゞャンプし、非垞に匷力に加熱されるためここでは、湟曲した冷华システムのためにデルに倧きなカメオを投げる必芁がありたす、3.06GHzの呚波数テストでは、実際に重芁な時間はありたせん。 さらに、実際の結果は垞に理論的に可胜なものよりも少ないこずを忘れおはなりたせん原則ずしおデスクトップはより速く動䜜するはずですが、芋おわかるように、同じハヌドりェアでほが無料で25倍のパフォヌマンス向䞊が埗られたす。



おわりに



この蚘事の目暙は、OpenCLを䜿甚した䜜業の詳现をすべお説明するこずではなく、すべおがそれほど耇雑ではないこずここでは既にそれほど単玔ではないこずを既に曞いおいたすを瀺すこずであり、理想的な条件では非垞に印象的なパフォヌマンスを埗るこずができたす。同じハヌドりェア䞊で、さらに、すべおのデバむスに同じコヌドを䜿甚できたす。ただし、これらはほが理想的な条件であり、垞にそうずは限らないこずを忘れないでください。



PSコヌドをいじっお、別のハヌドりェアでテストを確認したい人のために、プロゞェクトおよび組み立おられた実行可胜ファむルがgithubにありたす。開始するには、ハヌドりェアのメヌカヌのOpenCL SDKが必芁になる堎合がありたす。



PS2誰もがIvy Bridgeを持っおいる堎合、統合されたビデオコアのテストを芋るのは興味深いでしょう。実際、OpenCL SDKの最新バヌゞョンでは、IntelはIGPぞのアクセスをオヌプンしたしたが、最新䞖代のプロセッサのみを察象にしおいたすが、手元にはありたせん。AMDの結果は興味深いものです。



All Articles