/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "OCLPerfDoubleDMASeq.h" #include #include #include #include #include #include #include "CL/cl.h" #include "CL/cl_ext.h" #ifdef _WIN32 const size_t blockX = 128; const size_t blockY = 128; const size_t blockZ = 256; #else const size_t blockX = 256; const size_t blockY = 256; const size_t blockZ = 512; #endif const size_t chunk = 16; const size_t size_S = blockX * blockY * blockZ * sizeof(cl_float4); const size_t size_s = blockX * blockY * chunk * sizeof(cl_float4); static const int WindowWidth = 80; const size_t MaxQueues = 3; static const char* strKernel = "__kernel void dummy(__global float4* out) \n" "{ \n" " uint id = get_global_id(0); \n" " float4 value = (float4)(1.0f, 2.0f, 3.0f, 4.0f); \n" " uint factorial = 1; \n" " for (uint i = 1; i < (id / 0x400); ++i)\n" " { \n" " factorial *= i; \n" " } \n" " out[id] = value * factorial; \n" "} \n"; OCLPerfDoubleDMASeq::OCLPerfDoubleDMASeq() { _numSubTests = MaxQueues * 2; failed_ = false; } OCLPerfDoubleDMASeq::~OCLPerfDoubleDMASeq() {} void OCLPerfDoubleDMASeq::open(unsigned int test, char* units, double& conversion, unsigned int deviceId) { _deviceId = deviceId; OCLTestImp::open(test, units, conversion, deviceId); CHECK_RESULT((error_ != CL_SUCCESS), "Error opening test"); test_ = test; cl_device_type deviceType; error_ = _wrapper->clGetDeviceInfo(devices_[deviceId], CL_DEVICE_TYPE, sizeof(deviceType), &deviceType, NULL); CHECK_RESULT((error_ != CL_SUCCESS), "CL_DEVICE_TYPE failed"); if (!(deviceType & CL_DEVICE_TYPE_GPU)) { printf("GPU device is required for this test!\n"); failed_ = true; return; } program_ = _wrapper->clCreateProgramWithSource(context_, 1, &strKernel, NULL, &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateProgramWithSource() failed"); error_ = _wrapper->clBuildProgram(program_, 1, &devices_[deviceId], NULL, NULL, NULL); if (error_ != CL_SUCCESS) { char programLog[1024]; _wrapper->clGetProgramBuildInfo(program_, devices_[deviceId], CL_PROGRAM_BUILD_LOG, 1024, programLog, 0); printf("\n%s\n", programLog); fflush(stdout); } CHECK_RESULT((error_ != CL_SUCCESS), "clBuildProgram() failed"); kernel_ = _wrapper->clCreateKernel(program_, "dummy", &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateKernel() failed"); size_t bufSize = size_s; cl_mem buffer; test_ %= MaxQueues; events_ = ((test / MaxQueues) == 0) ? false : true; size_t numBufs = (test_ % MaxQueues) + 1; for (size_t b = 0; b < numBufs; ++b) { buffer = _wrapper->clCreateBuffer(context_, CL_MEM_READ_WRITE, bufSize, NULL, &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateBuffer() failed"); buffers_.push_back(buffer); } buffer = _wrapper->clCreateBuffer(context_, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, size_S, NULL, &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateBuffer() failed"); buffers_.push_back(buffer); } static void CL_CALLBACK notify_callback(const char* errinfo, const void* private_info, size_t cb, void* user_data) {} void OCLPerfDoubleDMASeq::run(void) { if (failed_) { return; } CPerfCounter timer; const int numQueues = (test_ % MaxQueues) + 1; const int numBufs = numQueues; std::vector cmdQueues(numQueues); int q; cl_command_queue_properties qProp = 0; for (q = 0; q < numQueues; ++q) { cl_command_queue cmdQueue = _wrapper->clCreateCommandQueue(context_, devices_[_deviceId], qProp, &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateCommandQueue() failed"); cmdQueues[q] = cmdQueue; } CHECK_RESULT_NO_RETURN((error_ != CL_SUCCESS), "Execution failed"); float* Data_s = (float*)_wrapper->clEnqueueMapBuffer(cmdQueues[0], buffers_[numBufs], CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, 0, size_S, 0, NULL, NULL, &error_); size_t gws[1] = {size_s / (4 * sizeof(float))}; size_t lws[1] = {256}; // Warm-up for (q = 0; q < numQueues; ++q) { error_ |= _wrapper->clEnqueueWriteBuffer(cmdQueues[q], buffers_[q], CL_FALSE, 0, size_s, (char*)Data_s, 0, NULL, NULL); error_ |= _wrapper->clSetKernelArg(kernel_, 0, sizeof(cl_mem), (void*)&buffers_[q]); error_ |= _wrapper->clEnqueueNDRangeKernel(cmdQueues[q], kernel_, 1, NULL, gws, lws, 0, NULL, NULL); error_ |= _wrapper->clEnqueueReadBuffer(cmdQueues[q], buffers_[q], CL_FALSE, 0, size_s, (char*)Data_s, 0, NULL, NULL); error_ |= _wrapper->clFinish(cmdQueues[q]); } CHECK_RESULT_NO_RETURN((error_ != CL_SUCCESS), "Execution failed"); size_t s_done = 0; cl_event x[MaxQueues] = {0}; /*---------- pass2: copy Data_s to and from GPU Buffers ----------*/ s_done = 0; timer.Reset(); timer.Start(); int idx = numBufs - 1; // Start from the last so read/write won't go to the same DMA when kernel is // executed q = numQueues - 1; size_t iter = 0; if (events_) { while (1) { error_ |= _wrapper->clEnqueueWriteBuffer(cmdQueues[q], buffers_[idx], CL_FALSE, 0, size_s, (char*)Data_s + s_done, 0, NULL, NULL); // Implicit flush of DMA engine on kernel start, because memory dependency error_ |= _wrapper->clSetKernelArg(kernel_, 0, sizeof(cl_mem), (void*)&buffers_[idx]); int prevQ; if (q == 0) { prevQ = numQueues - 1; } else { prevQ = q - 1; } if ((x[prevQ] != NULL) && (numQueues != 1)) { error_ |= _wrapper->clEnqueueNDRangeKernel(cmdQueues[q], kernel_, 1, NULL, gws, lws, 1, &x[prevQ], &x[q]); error_ |= _wrapper->clReleaseEvent(x[prevQ]); x[prevQ] = NULL; } else { error_ |= _wrapper->clEnqueueNDRangeKernel(cmdQueues[q], kernel_, 1, NULL, gws, lws, 0, NULL, &x[q]); if (numQueues == 1) { error_ |= _wrapper->clReleaseEvent(x[q]); x[q] = NULL; } } error_ |= _wrapper->clFlush(cmdQueues[q]); // Change the queue error_ |= _wrapper->clEnqueueReadBuffer(cmdQueues[q], buffers_[idx], CL_FALSE, 0, size_s, (char*)Data_s + s_done, 0, NULL, NULL); if ((s_done += size_s) >= size_S) { break; } error_ |= _wrapper->clFlush(cmdQueues[q]); ++iter; ++idx %= numBufs; ++q %= numQueues; } for (q = 0; q < numQueues; ++q) { if (x[q] != NULL) { error_ |= _wrapper->clReleaseEvent(x[q]); } } } else { while (1) { error_ |= _wrapper->clEnqueueWriteBuffer(cmdQueues[q], buffers_[idx], CL_FALSE, 0, size_s, (char*)Data_s + s_done, 0, NULL, NULL); // Implicit flush of DMA engine on kernel start, because memory dependency error_ |= _wrapper->clSetKernelArg(kernel_, 0, sizeof(cl_mem), (void*)&buffers_[idx]); error_ |= _wrapper->clEnqueueNDRangeKernel(cmdQueues[q], kernel_, 1, NULL, gws, lws, 0, NULL, NULL); // Change the queue error_ |= _wrapper->clEnqueueReadBuffer(cmdQueues[q], buffers_[idx], CL_FALSE, 0, size_s, (char*)Data_s + s_done, 0, NULL, NULL); if ((s_done += size_s) >= size_S) { break; } error_ |= _wrapper->clFlush(cmdQueues[q]); ++iter; ++idx %= numBufs; ++q %= numQueues; } } for (q = 0; q < numQueues; ++q) { error_ |= _wrapper->clFinish(cmdQueues[q]); } timer.Stop(); error_ |= _wrapper->clEnqueueUnmapMemObject(cmdQueues[0], buffers_[numBufs], Data_s, 0, NULL, NULL); error_ |= _wrapper->clFinish(cmdQueues[0]); CHECK_RESULT_NO_RETURN((error_ != CL_SUCCESS), "Execution failed"); for (q = 0; q < numQueues; ++q) { error_ = _wrapper->clReleaseCommandQueue(cmdQueues[q]); CHECK_RESULT_NO_RETURN((error_ != CL_SUCCESS), "clReleaseCommandQueue() failed"); } double GBytes = (double)(2 * size_S) / (double)(1000 * 1000 * 1000); _perfInfo = static_cast(GBytes / timer.GetElapsedTime()); std::stringstream stream; stream << "Write/Kernel/Read operation "; stream << numQueues << " queues "; if (events_) { stream << " (use events) "; } stream << " [GB/s]"; stream.flags(std::ios::right | std::ios::showbase); testDescString = stream.str(); } unsigned int OCLPerfDoubleDMASeq::close(void) { return OCLTestImp::close(); }