使用OpenCL,我似乎无法将超过7MB /秒的数据从Radeon 7970拉入i5桌面的主内存。
#include <iostream>
#include <Windows.h>
#include <CL/cl.h>
int main(int argc, char ** argv)
{
cl_platform_id platform;
clGetPlatformIDs(1, &platform, NULL);
cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
cl_context context = clCreateContext(NULL, 1, &device, NULL, NULL, NULL);
cl_command_queue queue = clCreateCommandQueue(context, device, 0, NULL);
const char *source =
"__kernel void copytest(__global short* dst) {\n"
" __local short buff[1024];\n"
" for (int i = 0; i < 1024; i++) {\n"
" for (int j = 0; j < 1024; j++)\n"
" buff[j] = j;\n"
" (void)async_work_group_copy(&dst[i*1024], buff, 1024, 0);\n"
" }\n"
"}\n";
cl_program program = clCreateProgramWithSource(context, 1, &source, NULL, NULL);
clBuildProgram( program, 1, &device, NULL, NULL, NULL);
cl_kernel kernel = clCreateKernel( program, "copytest", NULL);
cl_mem buf = clCreateBuffer(context, CL_MEM_WRITE_ONLY, 1024 * 1024 * 2, NULL, NULL);
const size_t global_work_size = 1;
clSetKernelArg(kernel, 0, sizeof(buf), (void*)&buf);
LARGE_INTEGER pcFreq = {}, pcStart = {}, pcEnd = {};
QueryPerformanceFrequency(&pcFreq);
QueryPerformanceCounter(&pcStart);
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_work_size, NULL, 0, NULL, NULL);
clFinish(queue);
QueryPerformanceCounter(&pcEnd);
std::cout << 2.0 * pcFreq.QuadPart / (pcEnd.QuadPart-pcStart.QuadPart) << "MB/sec";
}
正如您所看到的,它只在一个工作单元上运行。我尝试用多个(64个)工作单元之间的循环替换async_work_group_copy(),但这没有帮助。
是否有某种方法可以让Radeon的内存速度超过7MB /秒?我对数百MB /秒感兴趣。 NVidia会更快吗?
答案 0 :(得分:1)
问题是你只在GPU上使用一个线程,使数千个线程空闲。在这种情况下,您可以做两件事来帮助您实现更快的速度。
首先,尝试在工作组中使用更多线程:
__kernel void copytest(__global short* dst) {
__local short buff[1024];
for (int i = 0; i < 1024; i++) {
for (int j = get_local_id(0); j < 1024; j+= get_local_size(0))
buff[j] = j;
barrier(CLK_LOCAL_MEM_FENCE);
(void)async_work_group_copy(&dst[i*1024], buff, 1024, 0);
}
}
然后您可以将工作组的大小增加到256左右。
const size_t local_work_size = 256;
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL);
其次,您正在使用GPU,因此您可能不应该只使用一个工作组。您可以使用更多工作组,如下所示:
__kernel void copytest(__global short* dst) {
__local short buff[1024];
for (int i = get_group_id(0); i < 1024; i += get_num_groups(0)) {
for (int j = get_local_id(0); j < 1024; j+= get_local_size(0))
buff[j] = j;
barrier(CLK_LOCAL_MEM_FENCE);
(void)async_work_group_copy(&dst[i*1024], buff, 1024, 0);
}
}
然后你可以增加工作组的数量:
const size_t local_work_size = 256;
const size_t global_work_size = local_work_size * 32;
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, NULL, NULL);
希望这有助于加快申请速度。
答案 1 :(得分:0)
确保正确分配缓冲区:阅读NVIDIA OpenCL编程指南(如果可以找到)以查看分配固定内存。有一个完全成熟的例子可以让你达到6GB / s - 同样的原则适用于AMD。特别是CL_MEM_ALLOC_HOST_PTR标志。
答案 2 :(得分:0)
这里你真的不需要'j'循环。 async_work_group_copy可以双向工作;您可以复制到全局和本地空间。
//kernel will copy 2MB of short* in memory
__kernel void copytest(__global short* dst) {
__local short buff[1024];
for (int i = get_group_id(0); i < 1024; i += get_num_groups(0)) {
(void)async_work_group_copy(buff, &dst[i*1024], 1024, 0);
(void)async_work_group_copy(&dst[i*1024], buff, 1024, 0);
}
}
opencl 1.0规范说必须有16kb或更多的本地内存可供您使用(32kb +与ocl v1.1或更高版本)。许多设备实际上有32kb。我建议轮询系统并尽可能多地使用。实际上,您仍需要将一些本地内存保存用于其他目的。 see clGetDeviceInfo(CL_DEVICE_LOCAL_MEM_SIZE)
//using 16kb local memory per work group to copy 2MB...
__kernel void copytest(__global short* dst) {
__local short buff[16384];
for (int i = get_group_id(0); i < 64; i += get_num_groups(0)) {
(void)async_work_group_copy(buff, &dst[i*16384], 16384, 0);
(void)async_work_group_copy(&dst[i*16384], buff, 16384, 0);
}
}
如果要进行足够少量的复制,您可以通过使用完成工作所需的工作组数量来摆脱“i”循环。这会导致装配中的分支更少。
//using 32kb and 16 work groups to copy 2MB...
__kernel void copytest(__global short* dst) {
__local short buff[32768];
int i = get_group_id(0);
(void)async_work_group_copy(buff, &dst[i*32768], 32768, 0);
(void)async_work_group_copy(&dst[i*32768], buff, 32768, 0);
}