【问题标题】:Why is texture memory version of below program slower than global memory version为什么下面程序的纹理内存版本比全局内存版本慢
【发布时间】:2011-07-10 06:56:25
【问题描述】:

我很困惑为什么我的纹理版本比我的全局内存版本慢,因为纹理版本应该利用空间局部性。我正在尝试在以下情况下计算点积。因此,如果一个线程访问索引 i,它的邻居应该访问 i+1。因此,我们看到了空间局部性。

下面是纹理内存版本:

#include<cuda_runtime.h>
#include<cuda.h>
#include<stdio.h>
#include<stdlib.h>
#define intMin(a,b) ((a<b)?a:b)
//Threads per block
#define TPB 128
//blocks per grid
#define BPG intMin(128, ((n+TPB-1)/TPB))

texture<float> arr1;
texture<float> arr2;


const int n = 4;

__global__ void addVal( float *c){
    int tid = blockIdx.x * blockDim.x + threadIdx.x;

    //Using shared memory to temporary store results
    __shared__ float cache[TPB];
    float temp = 0;
    while(tid < n){
        temp += tex1Dfetch(arr1,tid) * tex1Dfetch(arr2,tid);
        tid += gridDim.x * blockDim.x;


    }
    cache[threadIdx.x] = temp;
    __syncthreads();
    int i = blockDim.x/2;
    while( i !=0){
        if(threadIdx.x < i){
            cache[threadIdx.x] = cache[threadIdx.x] +cache[threadIdx.x + i] ;

        }
    __syncthreads();
    i = i/2;

    }
    if(threadIdx.x == 1){
        c[blockIdx.x ] = cache[0];
    }



}

int main(){

float a[n] , b[n] , c[BPG];
float *deva, *devb, *devc;
int i;
//Filling with random values to test
for( i =0; i< n; i++){
    a[i] = i;
    b[i] = i*2;
}
printf("Not using constant memory\n");
cudaMalloc((void**)&deva, n * sizeof(float));
cudaMalloc((void**)&devb, n * sizeof(float));
cudaMalloc((void**)&devc, BPG * sizeof(float));


cudaMemcpy(deva, a, n *sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(devb, b, n *sizeof(float), cudaMemcpyHostToDevice);
cudaBindTexture(NULL,arr1, deva,sizeof(float) * n); // note: deva shd be in gpu
cudaBindTexture(NULL,arr2, devb,sizeof(float) * n); // note: deva shd be in gpu
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);

//Call function to do dot product
addVal<<<BPG, TPB>>>(devc);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
float time;
cudaEventElapsedTime(&time,start, stop);
printf("The elapsed time is: %f\n", time);


//copy result back
cudaMemcpy(c, devc, BPG * sizeof(float), cudaMemcpyDeviceToHost);
float sum =0 ;
for ( i = 0 ; i< BPG; i++){
    sum+=c[i];

}
//display answer
printf("%f\n",sum);
cudaUnbindTexture(arr1);
cudaUnbindTexture(arr2);
cudaFree(devc);

getchar();

return 0;
}

全局内存版本:

#include<cuda_runtime.h>
#include<cuda.h>
#include<stdio.h>
#include<stdlib.h>
#define intMin(a,b) ((a<b)?a:b)
//Threads per block
#define TPB 128
//blocks per grid
#define BPG intMin(128, ((n+TPB-1)/TPB))

const int n = 4;

__global__ void addVal(float *a, float *b, float *c){
    int tid = blockIdx.x * blockDim.x + threadIdx.x;

    //Using shared memory to temporary store results
    __shared__ float cache[TPB];
    float temp = 0;
    while(tid < n){
        temp += a[tid] * b[tid];
        tid += gridDim.x * blockDim.x;


    }
    cache[threadIdx.x] = temp;
    __syncthreads();
    int i = blockDim.x/2;
    while( i !=0){
        if(threadIdx.x < i){
            cache[threadIdx.x] = cache[threadIdx.x] +cache[threadIdx.x + i] ;

        }
    __syncthreads();
    i = i/2;

    }
    if(threadIdx.x == 1){
        c[blockIdx.x ] = cache[0];
    }



}

int main(){

float a[n] , b[n] , c[BPG];
float *deva, *devb, *devc;
int i;
//Filling with random values to test
for( i =0; i< n; i++){
    a[i] = i;
    b[i] = i*2;
}
printf("Not using constant memory\n");
cudaMalloc((void**)&deva, n * sizeof(float));
cudaMalloc((void**)&devb, n * sizeof(float));
cudaMalloc((void**)&devc, BPG * sizeof(float));
cudaMemcpy(deva, a, n *sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(devb, b, n *sizeof(float), cudaMemcpyHostToDevice);

cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);

//Call function to do dot product
addVal<<<BPG, TPB>>>(deva, devb, devc);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
float time;
cudaEventElapsedTime(&time,start, stop);
printf("The elapsed time is: %f\n", time);


//copy result back
cudaMemcpy(c, devc, BPG * sizeof(float), cudaMemcpyDeviceToHost);
float sum =0 ;
for ( i = 0 ; i< BPG; i++){
    sum+=c[i];

}
//display answer
printf("%f\n",sum);


getchar();

return 0;
}

【问题讨论】:

  • 两个问题:为什么内核在浮点工作时使用整数纹理?为什么你的代码不包含任何错误检查?
  • @Talonmies:感谢您的回复。使纹理浮动有帮助。另外,我的 cudaBindTexture 调用是错误的。然而,虽然我的程序现在正在运行,但我很困惑为什么我的纹理版本比我的全局内存版本慢,因为纹理版本应该利用空间局部性。我已经发布了上面的两个程序。请看一下
  • @Talonmies:我没有任何错误处理,因为到目前为止,我不知道任何错误处理。如果你能教我一些,我会很高兴知道:)

标签: cuda textures nvidia


【解决方案1】:

虽然知道您的图形设备可能会有所帮助,但对于某些类型的问题,使用计算能力 2.x 时,L1 和 L2 缓存的效果与纹理缓存一样好。

在这种情况下,您没有利用纹理缓存,因为每个线程只读取一次值。另一方面,您正在利用 1D 中的空间局部性,可以通过全局内存合并访问来隐藏它。

我向您推荐《CUDA 示例:通用 GPU 编程简介》一书。适合初学者的好书。使用 JuliaSet 之类的图形示例或非常基本的 Raycasting(如果您更喜欢 thouse,还有常见的 add、reduce 和 dot product 示例:)。

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    进一步 pQB 的回答,您的程序中没有数据重用——每个输入只读取一次,并且只使用一次。内存索引在线程之间是连续的,因此可以完美地合并。由于这两个原因,不需要任何设备内存缓存,因此全局内存访问比纹理访问更高效。再加上纹理缓存中的额外延迟开销(纹理缓存旨在提高吞吐量,而不是降低延迟,这与 L1/L2 数据缓存不同),并解释了减速。

    顺便说一句,您所做的是并行缩减,因此您可能希望查看 CUDA SDK 中的“缩减”示例以快速实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-20
      • 2018-02-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-02
      • 1970-01-01
      • 2015-12-22
      • 2016-08-20
      相关资源
      最近更新 更多