Memory 为什么全局内存版本比CUDA代码中的常量内存快?

Memory 为什么全局内存版本比CUDA代码中的常量内存快?,memory,optimization,cuda,Memory,Optimization,Cuda,我正在研究某个CUDA程序,我想用常量内存加速计算,但结果证明,使用常量内存会使我的代码慢约30% 我知道恒定内存擅长将读取的内容广播到整个扭曲,我认为我的程序可以利用它 以下是恒定内存代码: __constant__ float4 constPlanes[MAX_PLANES_COUNT]; __global__ void faultsKernelConstantMem(const float3* vertices, unsigned int vertsCount, int* displac

我正在研究某个CUDA程序,我想用常量内存加速计算,但结果证明,使用常量内存会使我的代码慢约30%

我知道恒定内存擅长将读取的内容广播到整个扭曲,我认为我的程序可以利用它

以下是恒定内存代码:

__constant__ float4 constPlanes[MAX_PLANES_COUNT];

__global__ void faultsKernelConstantMem(const float3* vertices, unsigned int vertsCount, int* displacements, unsigned int planesCount) {

    unsigned int blockId = __mul24(blockIdx.y, gridDim.x) + blockIdx.x;
    unsigned int vertexIndex = __mul24(blockId, blockDim.x) + threadIdx.x;

    if (vertexIndex >= vertsCount) {
        return;
    }

    float3 v = vertices[vertexIndex];
    int displacementSteps = displacements[vertexIndex];

    //__syncthreads();

    for (unsigned int planeIndex = 0; planeIndex < planesCount; ++planeIndex) {
        float4 plane = constPlanes[planeIndex];
        if (v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w > 0) {
            ++displacementSteps;
        }
        else {
            --displacementSteps;
        }
    }

    displacements[vertexIndex] = displacementSteps;
}
可能会导致线程“去同步”,然后它们将不会利用恒定内存读取的广播优势,因此我尝试调用u syncthreads();在读取恒定内存之前,它没有改变任何东西

怎么了?提前谢谢

系统:

  • CUDA驱动程序版本:5.0
  • CUDA能力:2.0
参数:

  • 顶点数:~250万
  • 飞机数目:1024
结果:

  • 恒定mem版本:46毫秒
  • 全球mem版本:35毫秒
编辑:

因此,我尝试了许多方法来提高恒定内存的速度,例如:

1) 注释掉两个全局内存读取,看看它们是否有任何影响,而它们没有。全局内存速度更快

2) 每个线程处理更多顶点(从8到64),以利用CM缓存。这甚至比每个线程一个顶点还要慢

2b)使用共享内存存储位移和顶点-在开始时加载所有位移和顶点,处理并保存所有位移。同样,速度比示例中显示的慢

在这段经历之后,我真的不明白CM读取广播是如何工作的,以及如何在我的代码中正确地“使用”。此代码可能无法使用CM进行优化

编辑2:

又一天的调整,我尝试了:

3) 通过内存合并,每个线程处理更多顶点(8到64个)(每个线程的增量等于系统中的线程总数)——这比增量等于1的结果更好,但仍然没有加速

4) 替换此if语句

if (v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w > 0) {
    ++displacementSteps;
}
else {
    --displacementSteps;
}
这会给出“不可预测”的结果,但需要一点数学知识,以避免使用以下代码进行分支:

float dist = v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w;
int distInt = (int)(dist * (1 << 29));  // distance is in range (0 - 2), stretch it to int range
int sign = 1 | (distInt >> (sizeof(int) * CHAR_BIT - 1));  // compute sign without using ifs
displacementSteps += sign;
float dist=v.x*平面.x+v.y*平面.y+v.z*平面.z+平面.w;
int distInt=(int)(dist*(1>(sizeof(int)*CHAR_BIT-1));//不使用ifs计算符号
位移步数+=符号;
不幸的是,这比使用if-so-if的速度要慢很多(~30%),if-so-if并不像我想的那么大

编辑3:

我的结论是,这个问题可能无法通过使用恒定内存来改善,以下是我的结果*:


*从15次独立测量中以中位数报告的时间。当恒定内存不足以保存所有平面时(4096和8192),内核被多次调用。

尽管compute capability 2.0芯片有64k的恒定内存,但每个多处理器只有8k的恒定内存缓存。您的代码中的每个线程都需要访问所有16k的恒定内存,因此缓存未命中会使性能下降。为了有效地将恒定内存用于平面数据,则需要重新构造实现。

\uuuu syncthreads()
有不同的用途。当您想同步块级线程时,例如当您使用共享内存时,您可以使用它。这种情况是不可能的。此外,使用恒定内存的好处来自于恒定缓存。缓存的好处来自于数据的重复使用。内核中没有数据的重复使用。每次内核调用都会访问常量内存数组中的每个位置一次,并且仅访问一次。对于较少数量的平面,处理时间会线性下降(使用常量的128个平面的处理时间为5毫秒,而使用全局变量的处理时间为4毫秒)。我的朋友们做了非常类似的程序,他们的加速率是60%。我应该尝试每个线程处理更多顶点吗?但即使没有它,程序也应该更快。@NightElfik:你的算法只是部分使用了CUDA体系结构-使用了额外的内核,但没有有效地使用高速片上内存。只是对更好地使用该体系结构的算法将有所帮助。你的朋友是否使用与你相同的计算能力?@Pieter Geerkens:我知道我可以进一步优化程序,但首先我想解决这个问题,因为如果我使用不同的技术达到加速,这个问题将被“隐藏”。我的朋友使用相同的计算能力,甚至是相同的GPU。我的代码和他们的代码之间唯一显著的区别是在线程开始时读取了2次全局内存(他们没有)。
float dist = v.x * plane.x + v.y * plane.y + v.z * plane.z + plane.w;
int distInt = (int)(dist * (1 << 29));  // distance is in range (0 - 2), stretch it to int range
int sign = 1 | (distInt >> (sizeof(int) * CHAR_BIT - 1));  // compute sign without using ifs
displacementSteps += sign;