【问题标题】:What to expect from cuda perfomance对 cuda 性能有什么期望
【发布时间】:2017-01-31 20:12:23
【问题描述】:

我最近使用 cuda 编写了一些简单的代码,但虽然我听说它可以加快速度,但我没有看到太多。 例如,我用 cuda 编写了 2 个代码(不使用任何库,如 NPP 和 ...)。

首先是一个模糊滤镜(没有纹理记忆)。

第二个基于模糊的分割(包括一些每个元素的乘法、加法、除法和一些求和,我使用了来自点积的示例代码和一些模糊过滤器)。

但在第一种情况下,我的代码有点慢!!!与类似的 OpenCV 代码相比,第二个代码只给了我 2 倍的速度。

我的系统(英特尔酷睿 i7 4700HQ 2.4GHz 和 Geforce GT 750m)(N46JV 华硕笔记本电脑) 我在 Windows 10 中使用 Visual Studio 2015、Cuda 8、OpenCV 3.1。

我的代码给了我结果并且逻辑上是正确的。

我的问题是我应该对我的系统有更多期望吗?

我也使用了一些 NPP 代码,但它并没有改变任何东西,它们甚至比我自己的代码慢一点(为了减少)

我之前问过一些人,这是我最后的希望!

例如求和码:

__global__ void GF3_kernel(double* oldU_temp,
int width,
int height,
double* d_partial_sum_t) {
__shared__ double L[nthreadsGF3];

//Initialize shared memory:
L[threadIdx.x] = 0;
__syncthreads();


//calculate pixel coordinates
const int tid_in = blockIdx.x * blockDim.x + threadIdx.x;
int tid_test = tid_in;

const int cacheXIndex = threadIdx.x;

//temperory sum
double temp = 0;


tid_test = tid_in;
//power random pixels of thread
while (tid_test < width * height) {
    temp += oldU_temp[tid_test];
    tid_test += gridDim.x * blockDim.x;
}
L[cacheXIndex] = temp;
__syncthreads();

int p_idx = blockDim.x / 2;
while (p_idx != 0) {
    if (cacheXIndex < p_idx) {
        L[cacheXIndex] += L[cacheXIndex + p_idx];
    }
    __syncthreads();
    p_idx /= 2;
}
if (cacheXIndex == 0) {
    d_partial_sum_t[blockIdx.x] = L[0];
}
}

内核调用将是:

    /////////////////////////////////////////////////////////// sum /////////////////////////////////////////////////////////////

    GF3_kernel << <gridGF3, nthreadsGF3 >> >(d_oldU_temp, width, height, d_partial_sum_t);


    // copy for final summation in cpu
    cudaDeviceSynchronize();
    cudaMemcpy(partial_sum_t, d_partial_sum_t, gridGF3 * sizeof(double),     cudaMemcpyDeviceToHost);
    cudaDeviceSynchronize();
    //Summation result
    sum_temp = 0;
    //calculate Summation
    for (int j = 0; j < gridGF3; j++) {
        sum_temp += partial_sum_t[j];
    }

类似的东西。

你怎么看?

图像是尺寸为 390x390 像素的 1 通道图像

【问题讨论】:

  • 由于(至少)一条指令——多数据模型和一些不明显的内存访问特性,GPGPU 的软件开发比常规开发复杂得多。试着写一些更简单的东西作为你的第一个例子,你应该会看到速度提高了。
  • 加速很大程度上取决于您的实现,因为需要更改算法以支持并行处理。此外,当数据从 CPU 复制到 GPU 再从 GPU 复制到 CPU 时,会丢失一些时间。因此,当您处理相当大的数据集时,GPGPU 将获得回报。
  • 除了所有其他说明(gpu 编程并非微不足道等)之外,您还必须处理用于测试的实际任务。例如,图像大小很重要。对于小图像,CPU 可能比 GPU 更快,因为您需要执行大量工作才能克服 gpu 的初始偏移缺陷:mcclanahoochie.com/blog/wp-content/uploads/2011/09/… 取自 mcclanahoochie.com/blog/2011/09/…
  • 我知道,我只有输入数据和输出被复制。并且在减少中,我将最后一部分发送到 cpu
  • 一个大问题是您使用的是双精度 - 大多数 GPU 的双精度性能非常差 - 请尝试改用 float

标签: c++ opencv cuda


【解决方案1】:

您不能指望 GPGPU 计算能带来奇迹。如果你比较高端 cpus 和 gpus 的浮点峰值性能,你会得到大约 4 到 10 的因子,具体取决于精度。 如果你设法以某种有效的方式使用你的 CPU,你可以期望你可以实现这个加速,如果你可以充分利用 GPU 的潜力。这很难。首先,并不是所有的问题都适合大规模并行 gpu 架构,其次优化 gpu 代码很难。

如果您想尝试从 GPU 中榨取更多性能,请尝试优化您的内存访问。这就是关键,您将失去大部分性能。 全局内存中的非对齐访问和随机访问将花费您 90% 以上的性能。尽量对齐数据并将其缓存在共享内存中。这会给你带来很长的路要走。 另一个技巧是让内核使用更少的寄存器。一般来说,每个线程使用的寄存器越少,一个流式多处理器上可以同时调度的寄存器就越多。

__global__ void GF3_kernel(
double* oldU_temp,
int width,
int height,
double* d_partial_sum_t) {
__shared__ double L[nthreadsGF3];

//Initialize shared memory:
L[threadIdx.x] = 0;
__syncthreads(); //__syncthreads does hurt performance. Use it if absolutly needed


//calculate pixel coordinates

//const int tid_in = blockIdx.x * blockDim.x + threadIdx.x; //not needed
int tid_test = blockIdx.x * blockDim.x + threadIdx.x;

//const int cacheXIndex = threadIdx.x; // not needed

//temperory sum
double temp = 0;


//tid_test = tid_in; //unneeded assignment
//power random pixels of thread
const int endRange = width * height; //most likely done by compiler, but just in case its not.
const int step = gridDim.x * blockDim.x; //same here
while (tid_test < endRange) {
    temp += oldU_temp[tid_test]; // aligned access in global memory is fine
    tid_test += step;
}
L[cacheXIndex] = temp;
__syncthreads();


// This looks like an reduction to me
// Looks fine but there is some potential
// https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwik5IO7nOzRAhWDDRoKHVUuC0kQFggcMAA&url=http%3A%2F%2Fdeveloper.download.nvidia.com%2Fcompute%2Fcuda%2F1.1-Beta%2Fx86_website%2Fprojects%2Freduction%2Fdoc%2Freduction.pdf&usg=AFQjCNFZmgdihbG17glvRmF-zPHfmUR4Aw&sig2=Yil-X_Byf6T6_dKAtN80Aw
int p_idx = blockDim.x / 2;
while (p_idx != 0) {
    if (cacheXIndex < p_idx) {
        L[cacheXIndex] += L[cacheXIndex + p_idx];
    }
    __syncthreads();
    p_idx /= 2;
}
if (cacheXIndex == 0) {
    d_partial_sum_t[blockIdx.x] = L[0];
}
}

【讨论】:

  • 感谢您的回答。 1)您能否提供有关未对齐访问全局内存的更多信息。我写了我想要使用的功能。你认为 GPU 是一个很好的方法吗?如果您想编写它们,您是否对这个带有 cuda 的系统有更多期望
  • 模糊过滤器基本上是一个卷积,可以用不同的快捷方式来解决。模糊过滤器很可能是可分离的,因此可以在列和行上在一维中求解。在卷积之前使用快速傅立叶变换很可能会更快。我敢打赌,这些技巧中至少有一个是在 opencv 中实现的。
  • 如果您的过滤器很小,您可以将其完全加载到共享内存中。图像应该很好地对齐,以便按列访问。棘手的一点是对结果图像的访问,这是非常随机的。您应该在共享内存中缓冲它的块,并在结果块完成后立即将其写回。
  • 我的模糊滤镜就是这样写的。顺便说一句,我没有使用纹理内存,我在使用时遇到了一些问题
  • 在这种情况下我没有使用共享内存 opencv 在列和行上使用 1d
猜你喜欢
  • 2020-09-16
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 2020-10-08
  • 1970-01-01
  • 2012-08-24
  • 2015-06-27
  • 2015-05-01
相关资源
最近更新 更多