Performance 最大化OpenCL内存输出

Performance 最大化OpenCL内存输出,performance,memory,opencl,Performance,Memory,Opencl,使用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_dev

使用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";
}
#包括
#包括
#包括
int main(int argc,字符**argv)
{
cl_平台\u id平台;
clGetPlatformIDs(1,&平台,NULL);
cl_设备\u id设备;
CLGetDeviceID(平台,CL\U设备类型\U GPU,1和设备,空);
cl_context context=clCreateContext(NULL,1,&device,NULL,NULL,NULL);
cl_命令_队列队列=clCreateCommandQueue(上下文,设备,0,空);
常量字符*源=
“uu内核void copytest(uu global short*dst){\n”
“_本地短buff[1024];\n”
“对于(int i=0;i<1024;i++){\n”
“对于(int j=0;j<1024;j++)\n”
“buff[j]=j;\n”
(void)异步工作组拷贝(&dst[i*1024],buff,1024,0);\n
“}\n”
“}\n”;
cl_program=clCreateProgramWithSource(上下文,1,&源,NULL,NULL);
clBuildProgram(程序,1和设备,NULL,NULL,NULL);
cl_kernel kernel=clCreateKernel(程序,“copytest”,NULL);
cl_mem buf=clCreateBuffer(上下文,仅cl_mem_WRITE_,1024*1024*2,NULL,NULL);
常量大小\u t全局工作\u大小=1;
clSetKernelArg(kernel,0,sizeof(buf),(void*)&buf);
大整数pcFreq={},pcStart={},pcEnd={};
QueryPerformanceFrequency(&pcFreq);
QueryPerformanceCounter(&pcStart);
clEnqueueNDRangeKernel(队列、内核、1、NULL和全局工作大小、NULL、0、NULL、NULL);
clFinish(队列);
QueryPerformanceCounter(&pcEnd);

问题是你在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);
    }
}
其次,您使用的是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);
    }
}

希望这有助于加速你的应用程序。

< P>确保你正确地分配缓冲区:阅读英伟达OpenCL程序指南(如果你能找到它)看看如何分配固定内存。有一个成熟的例子可以让你达到6GB/s——同样的原则也适用于AMD。特别是,CL_MEM_ALLOC_HOST_PTR标志。

你在这里并不真的需要你的“j”循环。异步工作组复制是双向的;你可以复制到全局和本地空间

//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);
  }
}

是否需要将其复制为1kB的数据块?请尝试至少使用pagesize(4096B)和R600缓存线大小(3-64MB)进行实验。事实证明,更改为2048 shorts(4096B)也没什么不同。如果我将数组声明的大小增加到clBuildProgram之外,则会出现“创建内核失败”的情况。谢谢。我没有尝试并行化数字运算——只是内存传输。你的代码确实将其速度提高了40倍,我怀疑是因为结果在内存中的分布方式,而不是因为数字运算,这是一个很小的过程,现在是分布的。我的数字运算实际上是一个IIR,本质上是串行计算。通过一些代数,我设法将其反序列化了一点,但我没有反序列化内存写入。我现在将尝试反序列化这两个操作,并返回报告。谢谢!即使您无法反序列化计算,您也需要为工作组副本使用多个线程。您可以通过
I绑定其余的计算f(get_local_id(0)==0)
以确保只有一个线程在执行计算。经过更多测试后,问题实际上是您最近建议的if(get_local_id(0)==0)。解决方案与我在阅读(并测试)您的原始答案后建议的一样:写入buff[]必须驻留在不同的工作单元上才能实现加速。因此,当我努力通过代数将我的递归关系映射到两个工作单元上时,我得到了一个很好的加速(25%)。我现在必须更加努力地将其映射到4或8个工作单元上。
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);
//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);
  }
}
//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);
  }
}
//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);
}