c++ - 在我的机器上操作大 vector 时 CUDA 推力变慢

标签 c++ c cuda thrust

我是 CUDA 初学者,正在阅读一些推力教程。我编写了一个简单但组织严密的代码,并试图计算出推力的加速度。(这个想法正确吗?)。我尝试通过在 cpu 上添加数组并在 gpu 上添加 device_vector 来将两个 vector (具有 10000000 int)添加到另一个 vector 。

事情是这样的:

#include <iostream>
#include "cuda.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>

#define N 10000000
int main(void)
{
    float time_cpu;
    float time_gpu;
    int *a = new int[N];
    int *b = new int[N];
    int *c = new int[N];
    for(int i=0;i<N;i++)
    {
        a[i]=i;
        b[i]=i*i;
    }
    clock_t start_cpu,stop_cpu;
    start_cpu=clock();
    for(int i=0;i<N;i++)
    {
        c[i]=a[i]+b[i];
    }
    stop_cpu=clock();   
    time_cpu=(double)(stop_cpu-start_cpu)/CLOCKS_PER_SEC*1000;
    std::cout<<"Time to generate (CPU):"<<time_cpu<<std::endl;
    thrust::device_vector<int> X(N);
    thrust::device_vector<int> Y(N);
    thrust::device_vector<int> Z(N);
    for(int i=0;i<N;i++)
    {
        X[i]=i;
        Y[i]=i*i;
    }
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start,0);       
    thrust::transform(X.begin(), X.end(),
        Y.begin(),
        Z.begin(),
        thrust::plus<int>());
    cudaEventRecord(stop,0);
    cudaEventSynchronize(stop);
    float elapsedTime;
    cudaEventElapsedTime(&elapsedTime,start,stop);
    std::cout<<"Time to generate (thrust):"<<elapsedTime<<std::endl;
    cudaEventDestroy(start);
    cudaEventDestroy(stop); 
    getchar();
    return 0;
}

CPU 结果看起来非常快,但是 gpu 在我的机器上运行非常慢(i5-2320,4G,GTX 560 Ti),CPU 时间大约是 26,GPU 时间大约是 30!我只是在我的代码中犯了愚蠢的错误吗?还是有更深层次的原因?

作为一名 C++ 菜鸟,我一遍又一遍地检查我的代码,但在 GPU 上的推力速度仍然较慢,所以我做了一些实验来展示用五种不同的方法计算 vectorAdd 的差异。 我使用 Windows API QueryPerformanceFrequency() 作为统一的时间测量方法。

每个实验看起来像这样:

f = large_interger.QuadPart;  
QueryPerformanceCounter(&large_interger);  
c1 = large_interger.QuadPart; 

for(int j=0;j<10;j++)
{
    for(int i=0;i<N;i++)//CPU array adding
    {
        c[i]=a[i]+b[i];
    }
}
QueryPerformanceCounter(&large_interger);  
c2 = large_interger.QuadPart;  
printf("Time to generate (CPU array adding) %lf ms\n", (c2 - c1) * 1000 / f);

这是我用于添加 GPU 数组的简单 __global__ 函数:

__global__ void add(int *a, int *b, int *c)
{
    int tid=threadIdx.x+blockIdx.x*blockDim.x;
    while(tid<N)
    {
        c[tid]=a[tid]+b[tid];
        tid+=blockDim.x*gridDim.x;
    }
}

函数被称为:

for(int j=0;j<10;j++)
{
    add<<<(N+127)/128,128>>>(dev_a,dev_b,dev_c);//GPU array adding
}   

我将 vector a[N] 和 b[N] 添加到 vector c[N] 循环 10 次:

  1. 在 CPU 上添加数组
  2. 在 CPU 上添加 std::vector
  3. 在 CPU 上添加 thrust::host_vector
  4. 在 GPU 上添加 thrust::device_vector
  5. 在 GPU 上添加数组。这是结果

N=10000000

我得到了结果:

  1. CPU 阵列增加 268.992968ms
  2. CPU std::vector 添加 1908.013595ms
  3. CPU Thrust::host_vector 添加 10776.456803ms
  4. GPU Thrust::device_vector 添加 297.156610ms
  5. GPU 阵列增加 5.210573ms

这让我很困惑,我不熟悉模板库的实现。容器和原始数据结构之间的性能真的相差如此之大吗?

最佳答案

大部分执行时间花在初始化 X[i] 和 Y[i] 的循环中。虽然这是合法的,但这是一种初始化大型设备 vector 的非常缓慢的方法。最好创建宿主 vector ,初始化它们,然后将它们复制到设备。作为测试,像这样修改代码(在初始化设备 vector X[i] 和 Y[i] 的循环之后):

}  // this is your line of code
std::cout<< "Starting GPU run" <<std::endl;  //add this line
cudaEvent_t start, stop;   //this is your line of code

然后您会看到 GPU 时序结果几乎在添加的行打印出来后立即出现。因此,您等待的所有时间都花在了直接从主机代码初始化这些设备 vector 上。

当我在我的笔记本电脑上运行它时,我得到大约 40 的 CPU 时间和大约 5 的 GPU 时间,因此对于您实际计时的代码部分,GPU 的运行速度比 CPU 快大约 8 倍。

如果创建 X 和 Y 作为主机 vector ,然后创建类似的 d_X 和 d_Y 设备 vector ,整体执行时间会更短,如下所示:

thrust::host_vector<int> X(N);     
thrust::host_vector<int> Y(N);     
thrust::device_vector<int> Z(N);     
for(int i=0;i<N;i++)     
{     
    X[i]=i;     
    Y[i]=i*i;     
}   
thrust::device_vector<int> d_X = X;
thrust::device_vector<int> d_Y = Y;

并将您的转换调用更改为:

thrust::transform(d_X.begin(), d_X.end(),      
    d_Y.begin(),      
    Z.begin(),      
    thrust::plus<int>()); 

好的,您现在已经指出 CPU 运行测量比 GPU 测量快。对不起,我仓促下了结论。我的笔记本电脑是 HP 笔记本电脑,配备 2.6GHz 核心 i7 和 Quadro 1000M GPU。我正在运行 centos 6.2 linux。一些评论:如果您在 GPU 上运行任何繁重的显示任务,这可能会降低性能。此外,在对这些东西进行基准测试时,通常的做法是使用相同的机制进行比较,如果需要,您可以对两者使用 cudaEvents,它可以为 CPU 代码计时,与 GPU 代码计时相同。此外,推力的常见做法是进行不计时的热身运行,然后重复测试以进行测量,同样,通常的做法是循环运行测试 10 次或更多次,然后除以得到平均值。在我的例子中,我可以看出 clocks() 测量值非常粗略,因为连续运行会给我 30、40 或 50。在 GPU 测量值上,我得到类似 5.18256 的值。其中一些可能会有所帮助,但我无法确切说明为什么您的结果和我的结果差异如此之大(在 GPU 方面)。

好的,我做了另一个实验。编译器将在 CPU 方面产生很大的不同。我用 -O3 开关编译,CPU 时间下降到 0。然后我将 CPU 计时测量从 clocks() 方法转换为 cudaEvents,我得到的 CPU 测量时间为 12.4(使用 -O3 优化),在 GPU 上仍然为 5.1边。

您的里程会因计时方法和您在 CPU 端使用的编译器而异。

关于c++ - 在我的机器上操作大 vector 时 CUDA 推力变慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12623574/

相关文章:

opencv - Jetson TK1上的O​​penCV比自定义Cuda代码慢得多

c++ - CUDA:复数标量 * 双稀疏矩阵 * 双 vector

c++ - Qt多重继承和信号

c - 三元运算符输出

java - 尝试运行 JCuda 示例时 java.library.path 中没有 JCudaRuntime-0.9.2-windows-x86_64

c++ - 是否应删除回调传入的 void* 指针

c++ - 陷阱表示

c++ - Eclipse-C++-调试 : see content of an Array

C++ struct 默认构造函数 mbed

c# - 查找 2 个字符串中的差异数