12

私はC、C ++、OpenCLを初めて使用し、現時点でそれらを学ぶために最善を尽くしています。これは、CまたはC++バインディングを使用してOpenCLに移植する方法を理解しようとしている既存のC++関数です。

#include <vector>

using namespace std;

class Test {

private:

    double a;
    vector<double> b;
    vector<long> c;
    vector<vector<double> > d;

public:

    double foo(long x, double y) {
        // mathematical operations
        // using x, y, a, b, c, d
        // and also b.size()
        // to calculate return value
        return 0.0;
    }

};

大まかに言って、私の質問は、この関数がアクセスするすべてのクラスメンバーをバインディングとカーネルに渡す方法です。スカラー値を渡す方法は理解していますが、ベクトル値についてはよくわかりません。上記の各メンバーへのポインターを渡す方法や、OpenCLのメンバーのビューがホストメモリと同期するようにそれらをメモリマップする方法はありますか?私の質問の内訳は以下の通りです。

  1. メンバーbとcが可変サイズである場合、これらをバインディングとカーネルに渡すにはどうすればよいですか?
  2. 2次元である場合、メンバーdを渡すにはどうすればよいですか?
  3. カーネル内からこれらのメンバーにアクセスするにはどうすればよいですか?また、カーネルへの引数でどのタイプとして宣言されますか?配列の添字表記、つまりb [0]を使用するだけでアクセスできますか?
  4. カーネル関数内でb.size()と同等の操作を呼び出すにはどうすればよいですか、または代わりに、バインディングからカーネルにサイズを追加の引数として渡すのでしょうか?変更するとどうなりますか?

回答にCまたはC++バインディングとカーネルコードのサンプルソースコードを入れていただければ幸いです。

どうもありがとう。

4

1 に答える 1

14
  1. OpenCLバッファーを割り当て、CPUデータをそこにコピーする必要があります。OpenCLバッファーのサイズは固定されているため、データサイズが変更された場合は再作成するか、「十分な大きさ」にして、必要なメモリが少ない場合はそのサブセクションのみを使用する必要があります。たとえば、バッファを作成するbと同時に、そのすべてのデータをデバイスにコピーするには、次のようにします。

    cl_mem buffer_b = clCreateBuffer(
        context, // OpenCL context
        CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // Only read access from kernel,
                                                 // copy data from host
        sizeof(cl_double) * b.size(), // Buffer size in bytes
        &b[0], // Pointer to data to copy
        &errorcode); // Return code
    

    ホストメモリ()を直接マップすることも可能ですCL_MEM_USE_HOST_PTRが、これにより、バッファの作成後のアラインメントとホストメモリへのアクセスにいくつかの制限が課せられます。基本的に、現在マッピングしていない場合、ホストメモリにガベージが含まれる可能性があります。

  2. 場合によります。2次元のベクトルのサイズは一貫して等しいですか?次に、OpenCLデバイスにアップロードするときにフラット化します。そうでなければ、それはより複雑になります。

  3. カーネルでは、バッファ引数を__globalポインタとして宣言します。たとえば__global double *b、1で作成されたバッファに適しています。カーネルで配列表記を使用するだけで、バッファ内の個々の要素にアクセスできます。

  4. カーネル内からバッファサイズをクエリすることはできないため、手動で渡す必要があります。これは、たとえば、作業項目の数がのサイズと一致する場合など、暗黙的に発生することもありbます。

計算のためにすべてのデータにアクセスできるカーネルは、次のようになります。

__kernel void foo(long x, double y, double a, __global double* b, int b_size,
                  __global long* c, __global double* d,
                  __global double* result) {
  // Here be dragons
  *result = 0.0;
}

結果のためにメモリも割り当てる必要があることに注意してください。必要に応じて、追加のサイズ引数を渡す必要がある場合があります。次のようにカーネルを呼び出します。

// Create/fill buffers
// ...

// Set arguments
clSetKernelArg(kernel, 0, sizeof(cl_long), &x);
clSetKernelArg(kernel, 1, sizeof(cl_double), &y);
clSetKernelArg(kernel, 2, sizeof(cl_double), &a);
clSetKernelArg(kernel, 3, sizeof(cl_mem), &b_buffer);
cl_int b_size = b.size();
clSetKernelArg(kernel, 4, sizeof(cl_int), &b_size);
clSetKernelArg(kernel, 5, sizeof(cl_mem), &c_buffer);
clSetKernelArg(kernel, 6, sizeof(cl_mem), &d_buffer);
clSetKernelArg(kernel, 7, sizeof(cl_mem), &result_buffer);
// Enqueue kernel
clEnqueueNDRangeKernel(queue, kernel, /* ... depends on your domain */);

// Read back result
cl_double result;
clEnqueueReadBuffer(queue, result_buffer, CL_TRUE, 0, sizeof(cl_double), &result,
                    0, NULL, NULL);
于 2012-09-14T18:40:55.513 に答える