简单的加法示例:共享内存版本的reduce执行速度比全局内存慢
Simple addition exmple: Shared memory version of reduction performing slower than global memory
我已经实现了两个版本的add。这两个版本中加法的概念是完全相同的。唯一的区别是,在一个代码中(下面的第一个代码)我使用全局内存,而在第二个代码中我使用共享内存。正如在几个地方提到的,共享内存版本应该更快,但对于我来说,全局内存版本更快。请告诉我哪里做错了注意:我有一个GPU与cc 2.1。因此,对于共享内存,我有32个银行。由于我在示例中只使用了16个int
,因此我的代码应该没有银行冲突。请让我知道这是否正确。
#include<stdio.h>
__global__ void reductionGlobal(int* in, int sizeArray, int offset){
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if(tid < sizeArray ){
if(tid % (offset * 2 ) == 0){
in[tid] += in[tid+offset];
}
}
}
int main(){
int size = 16; // size of present input array. Changes after every loop iteration
int cidata[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int* gidata;
cudaMalloc((void**)&gidata, size* sizeof(int));
cudaMemcpy(gidata,cidata, size * sizeof(int), cudaMemcpyHostToDevice);
int offset = 1;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
while(offset < size){
//use kernel launches to synchronize between different block. syncthreads() will not work
reductionGlobal<<<4,4>>>(gidata,size,offset);
offset *=2;
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
float elapsedTime;
cudaEventElapsedTime(&elapsedTime , start, stop);
printf("time is %f ms", elapsedTime);
int* output = (int*)malloc( size * sizeof(int));
cudaMemcpy(output, gidata, size * sizeof(int), cudaMemcpyDeviceToHost);
printf("The sum of the array using only global memory is %dn",output[0]);
getchar();
return 0;
}
共享内存版本:
#include<stdio.h>
__global__ void computeAddShared(int *in , int *out, int sizeInput){
extern __shared__ float temp[];
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int ltid = threadIdx.x;
temp[ltid] = 0;
while(tid < sizeInput){
temp[ltid] += in[tid];
tid+=gridDim.x * blockDim.x; // to handle array of any size
}
__syncthreads();
int offset = 1;
while(offset < blockDim.x){
if(ltid % (offset * 2) == 0){
temp[ltid] = temp[ltid] + temp[ltid + offset];
}
__syncthreads();
offset*=2;
}
if(ltid == 0){
out[blockIdx.x] = temp[0];
}
}
int main(){
int size = 16; // size of present input array. Changes after every loop iteration
int cidata[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int* gidata;
int* godata;
cudaMalloc((void**)&gidata, size* sizeof(int));
cudaMemcpy(gidata,cidata, size * sizeof(int), cudaMemcpyHostToDevice);
int TPB = 4;
int blocks = 10; //to get things kicked off
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
while(blocks != 1 ){
if(size < TPB){
TPB = size; // size is 2^sth
}
blocks = (size+ TPB -1 ) / TPB;
cudaMalloc((void**)&godata, blocks * sizeof(int));
computeAddShared<<<blocks, TPB,TPB>>>(gidata, godata,size);
cudaFree(gidata);
gidata = godata;
size = blocks;
}
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
float elapsedTime;
cudaEventElapsedTime(&elapsedTime , start, stop);
printf("time is %f ms", elapsedTime);
int *output = (int*)malloc(sizeof(int));
cudaMemcpy(output, gidata, sizeof(int), cudaMemcpyDeviceToHost);
//Cant free either earlier as both point to same location
cudaFree(godata);
cudaFree(gidata);
printf("The sum of the array is %dn", output[0]);
getchar();
return 0;
}
这里有很多错误。首先,一些一般性的注意事项:
- 你正在对16个数字执行缩减,这是一个荒谬的小的输入尺寸。CUDA在主机上有很多固定的开销还有设备方面。你让设备完成的工作量是如此之小,以至于你所测量的只是那些开销,而不是GPU执行时间。你看到的两个代码之间的区别可能只是由于增加了设置开销,在这种情况下共享内存版本。当然与代码本身无关。如果您想要度量代码的实际性能,那么您为代码所做的工作量必须足够大,以确保执行时间远远大于设置时间。请放心,即使在小型GPU上,您也有大约5个数量级的工作要做。
- 您提到了银行冲突,但这是您正在使用的体系结构上的稻草人。与旧的硬件相比,Fermi有一个完全不同的共享内存布局,只有一个相对较小的银行冲突问题。在这种情况下当然没什么好担心的。
对于实际的还原码本身:
- 如果你不能想出减少输入数组的方法对于单个内核启动中每个线程的部分和,那么您真的没有想过关于这个问题的讨论够多了。你目前的做法是在"全球"而"共享"版本效率极低。并行缩减是一个已经解决的问题,CUDA SDK附带了一份关于GPU上的优化和缩减性能的优秀白皮书。你应该在做任何事情之前先看看它。
- 一旦你达到了每个线程有一个部分和的程度,您希望对每个块执行一个共享内存缩减,以获得每个区块产生一个部分和。这将只需要
- 你的"共享"版本有一个缓冲区溢出,这应该导致运行时错误。启动时指定的动态共享内存大小时间的单位是字节,而不是单词。如果你的代码有错误检查,你早就发现了。费米有很好的共享存储器保护,如果您尝试写入,它将生成运行时错误
相关文章:
- 以下代码执行哪种内存分配(动态或静态)?
- 释放动态分配的内存时是否需要执行此额外步骤
- 如何在源代码中使用执行策略检测 C++17 的扩展内存管理算法的可用性?
- 测量任何 Windows 可执行文件的内存使用情况和执行时间
- 如何在 ubuntu 上的 php 脚本中获取程序(c,c++,java,python,php)的执行时间和内存使用量?
- 内核模式驱动程序可以在任何进程上执行读取进程内存吗?
- 为什么我的 C++ 程序在执行 TCMALLOC 堆检查器或堆配置文件时使用大量内存
- 为什么在 C++ 执行删除操作后仍可以访问释放的动态分配的内存
- x64内存执行
- 有没有办法获得功能大小并分配内存以复制和执行
- 为什么泄漏内存比在动态数组上执行 delete[] 慢
- 如果在执行过程中替换二进制文件,"const"数组是否驻留在内存中?
- Winapi:是否需要在可执行内存映射的文件上调用FlushInstructionCache
- 如何将x64机器代码写入虚拟内存并在C++中为Windows执行
- 执行以下 2 段代码会消耗相同的内存
- C++标准库中是否有保证不执行动态内存分配的函数或类
- 如果我想在执行过程中将其内存减少一半,我可以使用哪种数据结构
- 从另一个进程内的内存执行一个进程
- 从内存执行二进制文件
- c++从内存执行函数