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

时间:2013-03-06 07:03:55

标签: memory optimization cuda

我正在研究一些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;
}

全局内存代码是相同的,但它有一个参数(使用指向平面数组的指针)并使用它而不是全局数组。

我认为那些第一个全局内存读取

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

可能导致线程“失步”,然后它们不会利用常量内存读取的广播,所以我试图调用__syncthreads();在阅读常量记忆之前,它没有改变任何东西。

有什么问题?提前谢谢!

系统:

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

参数:

  • 顶点数:~2.5百万
  • 飞机数量:1024

结果:

  • 常数mem版本:46 ms
  • 全球版本:35毫秒

编辑:

所以我尝试了很多方法来更快地使常量内存,例如:

1)注释掉两个全局内存读取以查看它们是否有任何影响而它们没有。全球记忆仍然更快。

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

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

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

EDIT2:

另一天的调整,我试过了:

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;

不幸的是,这比使用if更慢(~30%)if ifs并不像我想的那么大。

EDIT3:

我结束这个问题,这个问题可能无法通过使用常量内存来改善,这些是我的结果*:

Graph of global and constant memory performance

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

1 个答案:

答案 0 :(得分:2)

虽然计算能力2.0芯片具有64k的常量内存,但每个多处理器只有8k的常量内存缓存。您的代码让每个线程都需要访问所有16k的常量内存,因此您会因缓存未命中而丢失性能。要有效地为平面数据使用常量内存,您需要重新构建实现。