/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "OCLMultiQueue.h" #include #include #include #include #include #include "CL/cl.h" const static char* strKernel = "__kernel void \n" "copyInc(__global uint* dst, __global uint* src) \n" "{ \n" " uint index = get_global_id(0); \n" " \n" " dst[index] = src[index] + 1; \n" "} \n"; static bool useGPU = true; static const cl_uint NumQueues = 8; // must be power of 2 static cl_uint NumElements = 4096; #if EMU_ENV static const cl_uint NumRuns = 16; #else static const cl_uint NumRuns = 16384; #endif static const cl_uint ExecutionsPerQueue = 256; std::stringstream lerror; class MemTransfer { public: MemTransfer(OCLWrapper* wrapper, cl_context context, cl_command_queue queue, cl_uint numElements) : wrapper_(wrapper), context_(context), queue_(queue), numElements_(numElements), count_(0) {} ~MemTransfer() { wrapper_->clReleaseMemObject(dst_); wrapper_->clReleaseMemObject(src_); } bool create() { cl_int err; size_t size = numElements_ * sizeof(cl_uint); cl_uint* data = new cl_uint[numElements_]; memset(data, 0, size); src_ = wrapper_->clCreateBuffer(context_, CL_MEM_COPY_HOST_PTR, size, data, &err); if (src_ == NULL) { lerror << "clReleaseContext failed"; delete[] data; return false; } dst_ = wrapper_->clCreateBuffer(context_, 0, size, NULL, &err); if (dst_ == NULL) { lerror << "clCreateBuffer() failed"; delete[] data; return false; } delete[] data; return true; } bool run(cl_kernel kernel) { size_t global_work_size[1]; size_t local_work_size[1]; size_t size = numElements_ * sizeof(cl_uint); global_work_size[0] = (numElements_ + 63) / 64 * 64; local_work_size[0] = 64; if (CL_SUCCESS != wrapper_->clSetKernelArg(kernel, 0, sizeof(cl_mem), (void*)&dst_)) { return false; } if (CL_SUCCESS != wrapper_->clSetKernelArg(kernel, 1, sizeof(cl_mem), (void*)&src_)) { return false; } if (CL_SUCCESS != wrapper_->clEnqueueNDRangeKernel(queue_, kernel, 1, NULL, (const size_t*)global_work_size, (const size_t*)local_work_size, 0, NULL, NULL)) { lerror << "clEnqueueNDRangeKernel() failed"; return false; } // Copy dst into src if (CL_SUCCESS != wrapper_->clEnqueueCopyBuffer(queue_, dst_, src_, 0, 0, size, 0, 0, NULL)) { lerror << "clEnqueueCopyBuffer() failed"; return false; } count_++; return true; } bool check() { size_t size = numElements_ * sizeof(cl_uint); cl_event event; void* ptr = wrapper_->clEnqueueMapBuffer(queue_, src_, CL_TRUE, CL_MAP_READ, 0, size, 0, NULL, NULL, NULL); cl_uint* data = reinterpret_cast(ptr); for (cl_uint i = 0; i < numElements_; ++i) { if (data[i] != count_) { return false; } } wrapper_->clEnqueueUnmapMemObject(queue_, src_, ptr, 0, NULL, &event); wrapper_->clWaitForEvents(1, &event); wrapper_->clReleaseEvent(event); return true; } void flush() { wrapper_->clFlush(queue_); } private: OCLWrapper* wrapper_; cl_context context_; cl_command_queue queue_; cl_uint numElements_; cl_uint count_; cl_mem dst_; cl_mem src_; }; MemTransfer* work[NumQueues]; bool test(cl_kernel, cl_uint, cl_uint); OCLMultiQueue::OCLMultiQueue() { _numSubTests = 0; for (cl_uint i = 1; i <= NumQueues; i <<= 1, _numSubTests++); failed_ = false; } OCLMultiQueue::~OCLMultiQueue() {} void OCLMultiQueue::open(unsigned int test, char* units, double& conversion, unsigned int 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)) { testDescString = "GPU device is required for this test!\n"; failed_ = true; return; } size_t maxWorkGroupSize = 1; cl_uint computePower = 1; error_ = _wrapper->clGetDeviceInfo(devices_[deviceId], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(maxWorkGroupSize), &maxWorkGroupSize, NULL); computePower *= static_cast(maxWorkGroupSize); cl_uint maxComputeUnits = 1; error_ = _wrapper->clGetDeviceInfo(devices_[deviceId], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(maxComputeUnits), &maxComputeUnits, NULL); computePower *= 32 * maxComputeUnits; NumElements = (NumElements < static_cast(computePower)) ? static_cast(computePower) : NumElements; 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_, "copyInc", &error_); CHECK_RESULT((error_ != CL_SUCCESS), "clCreateKernel() failed"); } void OCLMultiQueue::run(void) { if (failed_) { return; } // Run test cl_uint queues = 1 << test_; if (!test(kernel_, NumRuns / queues, queues)) { lerror << "We failed a test run!"; CHECK_RESULT(true, lerror.str().c_str()); } } unsigned int OCLMultiQueue::close(void) { return OCLTestImp::close(); } bool OCLMultiQueue::test(cl_kernel kernel, cl_uint numRuns, cl_uint numQueues) { cl_command_queue cmd_queue[NumQueues]; CPerfCounter timer; for (cl_uint i = 0; i < numQueues; ++i) { cmd_queue[i] = _wrapper->clCreateCommandQueue(context_, devices_[_deviceId], 0, &error_); if (cmd_queue[i] == (cl_command_queue)0) { _wrapper->clReleaseContext(context_); testDescString = "clCreateCommandQueue() failed"; return false; } work[i] = new MemTransfer(_wrapper, context_, cmd_queue[i], NumElements); if (work[i] == NULL || !work[i]->create()) { testDescString = "Test creation failed"; return false; } } timer.Reset(); timer.Start(); cl_uint dispatchCount = ExecutionsPerQueue / numQueues; for (cl_uint i = 0; i < numRuns; ++i) { for (cl_uint j = 0; j < numQueues; ++j) { if (!work[j]->run(kernel)) { testDescString = "Execution failed"; return false; } // Every queue should have a dispatch after 256 executions, // but the time for dispatch on each queue // will be shifted on dispatchCount if (((i % dispatchCount) == 0) && (((i / dispatchCount) % numQueues) == j)) { work[j]->flush(); } } } for (cl_uint i = 0; i < numQueues; ++i) { _wrapper->clFinish(cmd_queue[i]); } timer.Stop(); for (cl_uint j = 0; j < numQueues; ++j) { if (!work[j]->check()) { testDescString = "Result Check fails!"; return false; } } std::stringstream stream; stream << "Num Queues: " << numQueues << ", Executions Per Queue: "; stream.flags(std::ios::right | std::ios::showbase); stream.width(5); stream << numRuns; stream.precision(3); stream << ", Time: " << (float)(timer.GetElapsedTime()) << " seconds"; for (cl_uint i = 0; i < numQueues; ++i) { delete work[i]; _wrapper->clReleaseCommandQueue(cmd_queue[i]); } testDescString = stream.str(); return true; }