Pointers CUDA:\uuuuu限制\uuuu标签使用

Pointers CUDA:\uuuuu限制\uuuu标签使用,pointers,memory,memory-management,cuda,Pointers,Memory,Memory Management,Cuda,我不太理解CUDA中\uuuuu restrict\uuuu标记的概念 我已经读到,使用\uuuuuu restrict\uuuuu可以避免指针别名,特别是,如果指向的变量是只读的,则会优化变量的读取,因为它是缓存的 这是代码的简化版本: __constant__ float M[M_DIM1][M_DIM2]; __host__ void function(float N[][DIM2], float h_M[][M_DIM2], float P[][DIM2]); __global__

我不太理解CUDA中
\uuuuu restrict\uuuu
标记的概念

我已经读到,使用
\uuuuuu restrict\uuuuu
可以避免指针别名,特别是,如果指向的变量是只读的,则会优化变量的读取,因为它是缓存的

这是代码的简化版本:

__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);

}
uuu常量uuu浮点M[M_DIM1][M_DIM2];
__主机无效函数(浮点N[][DIM2],浮点h_M[][M_DIM2],浮点P[][DIM2]);
__全局无效核函数(常数浮点*限制,浮点*P);
__主机无效函数(浮点N[][DIM2],浮点h_M[][M_DIM2],浮点P[][DIM2]){
int IOSize=DIM1*DIM2*sizeof(浮点);
int ConstSize=M_DIM1*M_DIM2*sizeof(浮点);
浮点数*dN,*dP;
Cudamaloc((无效**)和dN,IOSize);
cudaMemcpy(dN、N、IOSize、cudaMemcpyHostToDevice);
cudaMemcpyToSymbol(M,hm,ConstSize);
Cudamaloc((无效**)和dP,IOSize);
dim3 dimBlock(DIM1、DIM2);
dim3 dimGrid(1,1);
核函数>(dN,dP);
cudaMemcpy(P、dP、IOSize、cudaMemcpyDeviceToHost);
cudaFree(dN);
cudaFree(dP);
}
我是否以正确的方式在N上使用了只读的
\uuuu restrict\uuu
标记?
此外,我还了解到,M上的关键字
\uuuuu constant\uuuu
表示只读和常量,因此这两者之间的区别是什么,nvcc使用的分配类型?

\uuuu restrict\uuu
。(注意,包括GNU编译器在内的各种C++编译器也支持这个精确的关键字,并同样使用它)。 它的语义基本上与C99
restrict
关键字相同,即

简而言之,
\uuuuu restrict\uuuuu
是程序员与编译器签订的一份合同,上面说,大致上是,“我将仅使用此指针引用底层数据”。从编译器的角度来看,这使表脱离的一个关键因素是指针别名,这会阻止编译器进行各种优化

如果您想要一篇关于
restrict
\uuu restrict\uuuu
确切定义的较长正式论文,请参考我已经给出的链接之一,或进行一些研究

因此,
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
通常对支持它的编译器非常有用,用于优化目的

对于compute capability 3.5或更高版本的设备,这些设备有一个称为的独立缓存,它独立于普通的一级缓存

如果同时使用
\uuuu restrict\uuuu
const
来修饰传递给内核的全局指针,那么在为cc3.5和更高版本的设备生成代码时,这也是编译器的一个强烈提示,以使这些全局内存负载流经只读缓存。这可以提供应用程序性能方面的好处,通常很少进行其他代码重构。这并不能保证只读缓存的使用,如果只读缓存能够满足必要的条件,编译器通常会尝试积极使用只读缓存,即使您不使用这些修饰符

\uuuu常量\uuuu
指的是不同的。有许多不同之处:

  • \uuuu常量\uuuu
    在所有GPU上都可用,只读缓存仅在cc3.5及更高版本上可用
  • 使用
    \uuuu constant\uuuu
    标记分配的内存(包括在指定内存分配的行中)最大限制为64KB。只读缓存没有这样的限制。我们不把
    \uuuu restrict\uuuu
    放在分配内存的行上;它用于装饰指针
  • 只读缓存中缓存的数据具有典型的全局内存访问注意事项-通常我们希望通过只读缓存进行相邻和连续访问,以实现全局内存读取的最佳合并。
    \uuuu常量
    机制oth期望所谓的统一访问以获得最快的性能。统一访问本质上意味着warp中的每个线程都从相同的位置/地址/索引请求数据
从内核代码的角度来看,
\uuu常量\uuu
内存和在传递给内核代码的指针上标记有
const
decorator的全局内存都是只读的


我看不出您所展示的代码中有任何明显的问题,无论是使用
\uuuuuuuuuuuuuu限制\uuuuuuuuuuuuuuuuu
还是其他什么。我唯一的意见是,为了获得最大的好处,您可能希望在内核声明/原型中用
\uuuu restrict\uuuu
修饰
N
P
指针,以获得最大的好处,如果这是您的意图的话。(显然,您不会用
const
装饰
P

是否适用于引用,就像它适用于原始指针一样?在中的示例中,
void foo(const float*\uuuuu restrict\a,const float*\uu restrict\uuuu\b,float*\uu restrict\uu\c)之间是否有任何区别
无效foo(常量浮点*\uuuuu限制\uuuuuua,常量浮点*\uuuu限制\uuuuuuuub,浮点*c)
?后者有意义吗?没有指定编译器的行为。我建议在所有指针上使用
\uuuuuu restrict\uuuuu
(如果适用),因为这将完全消除表中的别名。编程指南中也包括了这一点:“请注意,所有指针参数都需要进行限制,编译器优化器才能获得任何好处。”这实际上是对
\uuuuuuu restrict\uuuuuu
语义的精确描述,我在这个问题中并不打算涉及到这一点。你可能想问一个新问题。我不太可能回应关于一个3年前问题的评论中的长时间讨论。