「OpenCLのC++ Bindingsを使ってみる - その1」、「OpenCLのC++ Bindingsを使ってみる - その2」、「OpenCLのC++ Bindingsを使ってみる - その3」、「OpenCLのC++ Bindingsを使ってみる - その4」に引き続き、今回はOpenCLのC++ Bindingsを使って、コマンドキューの生成とキューにカーネルを入れ実際に計算することにする。これまでと同様に前回までのコードに追記していく。
#define __CL_ENABLE_EXCEPTIONS #if defined(__APPLE__) || defined(__MACOSX) #include #else #include #endif #include const int nElements = 9000000; float input1[nElements]; float input2[nElements]; float output[nElements]; int main(int argc, char* argv[]) { cl_int error = CL_SUCCESS; try{ std::vector platforms; cl::Platform::get(&platforms); if(platforms.size() == 0){ std::cout << "Any Platforms is NOT FOUNT." << std::endl; return 1; } cl_context_properties properties[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; cl::Context context(CL_DEVICE_TYPE_GPU, properties); std::vector devices = context.getInfo(); char* addVector = { "__kernel void\n\ addVector(__global const float *input1,\n\ __global const float *input2,\n\ __global float *output)\n\ {\n\ int index = get_global_id(0);\n\ output[index] = sin(input1[index]) * sin(input2[index]);\n\ output[index] = cos(output[index]);\n\ output[index] = pow(output[index], output[index]);\n\ }\n"}; cl::Program::Sources source(1,std::make_pair(addVector, strlen(addVector))); cl::Program program = cl::Program(context, source); program.build(devices); cl::Kernel kernel(program, "addVector", &error); for(int i = 0; i < nElements; i++){ input1[i] = (float)i * 10.0f; input2[i] = (float)i / 20.0f; output[i] = 0.0f; } cl::Buffer memInput1 = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_float) * nElements, input1, &error); cl::Buffer memInput2 = cl::Buffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_float) * nElements, input2, &error); cl::Buffer memOutput = cl::Buffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_float) * nElements, NULL, &error); kernel.setArg(0, memInput1); kernel.setArg(1, memInput2); kernel.setArg(2, memOutput); cl::CommandQueue queue(context, devices.at(0), 0, &error); queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(nElements), cl::NullRange, NULL, NULL); queue.enqueueReadBuffer(memOutput, CL_TRUE, 0, sizeof(cl_float) * nElements, output, NULL, NULL); for(int i = 0; i < 20; i++){ std::cout << "input1[" << i << "], input2[" << i << "], output[" << i << "] : "; std::cout << input1[i] << ", " << input2[i] << ", " << output[i] << std::endl; } }catch(cl::Error err){ std::cerr << "ERROR: " << err.what() << "(" << err.err() << ")" << std::endl; } return 0; }
まず、cl::CommandQueueクラスのインスタンスを生成する。この時の引数はコンテキストとデバイス、キューのプロパティ、エラーコードとなる。キューのプロパティはOpenCL 1.1 Specification [PDF]のTable 5.1に記載されている。queue.enqueueNDRangeKernelでは、生成したコマンドキューにカーネルを入れている。この時の引数はカーネル、オフセット、グローバルデータの次元、ローカルデータの次元、イベントリスト、イベント出力用変数となる。ここで、カーネルが実行される。
次に結果を取得する。queue.enqueueReadBufferで出力用変数outputに結果を読み込む。最後に読み込んだ変数の一部を表示している。
結果このようになる
input1[0], input2[0], output[0] : 0, 0, 1 input1[1], input2[1], output[1] : 10, 0.05, 0.999631 input1[2], input2[2], output[2] : 20, 0.1, 0.995867 input1[3], input2[3], output[3] : 30, 0.15, 0.989237 input1[4], input2[4], output[4] : 40, 0.2, 0.989182 input1[5], input2[5], output[5] : 50, 0.25, 0.997898 input1[6], input2[6], output[6] : 60, 0.3, 0.995962 input1[7], input2[7], output[7] : 70, 0.35, 0.966201 input1[8], input2[8], output[8] : 80, 0.4, 0.93131 input1[9], input2[9], output[9] : 90, 0.45, 0.930719 input1[10], input2[10], output[10] : 100, 0.5, 0.971525 input1[11], input2[11], output[11] : 110, 0.55, 0.999733 input1[12], input2[12], output[12] : 120, 0.6, 0.949504 input1[13], input2[13], output[13] : 130, 0.65, 0.867867 input1[14], input2[14], output[14] : 140, 0.7, 0.841198 input1[15], input2[15], output[15] : 150, 0.75, 0.896425 input1[16], input2[16], output[16] : 160, 0.8, 0.987789 input1[17], input2[17], output[17] : 170, 0.85, 0.967397 input1[18], input2[18], output[18] : 180, 0.9, 0.842729 input1[19], input2[19], output[19] : 190, 0.95, 0.7733
これで、OpenCL C++ Bindingsを使用した一連の計算をすることができた。
コメント