我不太了解CUDA中__restrict__
标记的概念。
我已经读过使用__restrict__
避免了指针别名,特别是如果指向的变量是只读的,那么变量的读取就会被优化,因为它已被缓存。
这是代码的简化版本:
__constant__ float M[M_DIM1][M_DIM2];
__host__ void function(float N[][DIM2], float h_M[][M_DIM2], float P[][DIM2]);
__global__ void kernel_function(const float* __restrict__ N, float *P);
__host__ void function(float N[][DIM2], float h_M[][M_DIM2], float P[][DIM2]) {
int IOSize = DIM1 * DIM2 * sizeof(float);
int ConstSize = M_DIM1* M_DIM2* sizeof(float);
float* dN, *dP;
cudaMalloc((void**)&dN, IOSize);
cudaMemcpy(dN, N, IOSize, cudaMemcpyHostToDevice);
cudaMemcpyToSymbol(M, h_M, ConstSize);
cudaMalloc((void**)&dP, IOSize);
dim3 dimBlock(DIM1, DIM2);
dim3 dimGrid(1, 1);
kernel_function << <dimGrid, dimBlock >> >(dN, dP);
cudaMemcpy(P, dP, IOSize, cudaMemcpyDeviceToHost);
cudaFree(dN);
cudaFree(dP);
}
我是否以正确的方式使用N上的__restrict__
标记,这是只读的?
此外,我已经读过M上的关键字__constant__
意味着它是只读的和常量的,那么它们之间的差异是什么,分配的类型是什么?
答案 0 :(得分:5)
__restrict__
使用的 nvcc
已记录为here。 (请注意,包括gnu编译器在内的各种c ++编译器也支持这个确切的关键字,并且类似地使用它。)
它与C99 restrict
关键字具有基本相同的语义,即an official part of that language standard。
简而言之,__restrict__
是您作为程序员使用编译器创建的合同,其中粗略,&#34;我将仅使用此指针来引用基础数据&#34;。从编译器的角度来看,这取消了表格的一个关键因素是指针别名,它可以阻止编译器进行各种优化。
如果您想要对restrict
或__restrict__
的确切定义进行更长时间的正式论述,请参阅我已经给出的其中一个链接,或进行一些研究。
因此,出于优化目的,__restrict__
通常对支持它的编译器很有用。
对于3.5或更高版本的计算能力,这些设备有一个名为read only cache的独立缓存,它独立于普通的L1类型缓存。
如果同时使用__restrict__
和const
来装饰传递给内核的全局指针,那么在为cc3.5及更高版本的设备生成代码时,这也是编译器的强烈提示,导致这些全局内存负载流过只读缓存。这可以提供应用程序性能优势,通常只需很少的其他代码重构。这并不保证使用只读缓存,并且编译器通常会尝试积极地使用只读缓存(如果它可以满足必要条件),即使您没有这样做也是如此。使用这些装饰器。
__constant__
指的是不同 hardware resource on the GPU。有很多不同之处:
__constant__
适用于所有GPU,只有cc3.5及更高版本的只读缓存__constant__
标记分配的内存(包含在指定内存分配的行中)限制为最大64KB。只读缓存没有这样的限制。我们不会将__restrict__
放在分配内存的行上;它用于装饰指针。__constant__
机制OTOH期望所谓的统一访问以获得最快的性能。统一访问本质上意味着warp中的每个线程都在从相同的位置/地址/索引请求数据。从内核代码的角度来看,__constant__
内存和在传递给内核代码的指针上标有const
装饰器的全局内存都是只读的。
我不会在您显示的代码中看到任何明显的问题,无论是使用__restrict__
还是其他任何问题。我唯一的评论是,为了获得最大的好处,您可能希望使用N
来装饰内核声明/原型中的P
和__restrict__
指针,以获得最大的好处,如果是你的意图。 (显然,你不会用P
装饰const
。)