6

タイトルが示すとおり、2つのOpenCLバッファーを効果的に交換する方法を探しています。私のカーネルは2つのgloablバッファーを使用します。1つは入力として、もう1つは出力として使用します。ただし、前の出力バッファーが次の反復の入力バッファーシードになるため、カーネル引数を設定し、カーネルをエンキューし、バッファーを交換するたびに、同じNDRangeを使用してforループでカーネルを呼び出します。

これらの2つのバッファを交換するための適切な方法は何ですか?バッファをホストにコピーして、すでにmallocされた配列の1つに戻し、それを使用して次の入力バッファにコピーするのclEnqueueWriteBuffer()clEnqueueReadBuffer()非効率的な方法だと思います。それ以外の場合は、一時cl_mem変数を使用してスワッピングを実行しています。

4

2 に答える 2

11

必要はありません。カーネルをもう一度キューに入れるclSetKernelArg前に、適切なカーネル引数を設定するだけです ( を使用clEnqueueNDRangeKernel)。バッファはデバイスに残り、ホストには何もコピーされません。

CL_MEM_READ_WRITEもちろん、この場合はバッファを作成する必要があります。

于 2012-06-14T21:01:00.227 に答える
4

前の回答:いいえ、バッファーをまったくスワップする必要はありません。

ただし、提案された回答には同意しません。関数clSetKernelArg()スレッド セーフではなく、操作ループで呼び出されるように設計されていません。

適切な解決策は、同じプログラムとソースで作成された 2 つのカーネルを作成することです。このアプローチは、「1 つのタスクに 1 つのカーネル」という OpenCL プログラミングの哲学に沿ったものです。同じコードで引数が異なる多くのカーネルを使用するのが最善の方法です。

最初のカーネルには次のものがあります。

kernel1 = clCreateKernel(program, "mykernel", NULL);
clSetKernelArg(kernel1, 0, &buff1);
clSetKernelArg(kernel1, 1, &buff2);

もう1つは次のようになります。

kernel2 = clCreateKernel(program, "mykernel", NULL);
clSetKernelArg(kernel2, 0, &buff2);
clSetKernelArg(kernel2, 1, &buff1);

このように、反復ごとに実行を停止する必要はありません。あなたは単に実行することができます:

for(int it=0; it<iter; it++){
    clEnqueueNDRangeKernel(it%2 ? kernel1 : kernel2, ....);
}
clFinish(command);

このアプローチは、カーネル引数を変更するよりも確実に優れており、より効率的で、API 呼び出しが少なくなります。さらに、一部のシステムでclSetKernelArgs()は、API の実装が不十分なため、呼び出しがブロックされる場合があります。したがって、これらはできるだけ避けたほうがよいでしょう。

于 2014-02-24T09:47:27.940 に答える