Cuda 解决冲突-尝试使用smem合并gmem访问,但存在冲突

Cuda 解决冲突-尝试使用smem合并gmem访问,但存在冲突,cuda,memory-access,Cuda,Memory Access,我有这样的密码: struct __declspec(align(32)) Circle { float x, y; float prevX, prevY; float speedX, speedY; float mass; float radius; void init(const int _x, const int _y, const float _speedX = 0.0f, const float _speedY = 0.0f,

我有这样的密码:

   struct __declspec(align(32)) Circle
{
    float x, y;
    float prevX, prevY;
    float speedX, speedY;
    float mass;
    float radius;

void init(const int _x, const int _y, const float _speedX = 0.0f, const float   _speedY = 0.0f,
    const float _radius = CIRCLE_RADIUS_DEFAULT, 
    const float _mass = CIRCLE_MASS_DEFAULT);
};
第二个:

/*smem[threadIdx.x] = *(((float*)cOut) + threadIdx.x);
        smem[threadIdx.x + blockDim.x] = *(((float*)cOut) + threadIdx.x + blockDim.x);
        smem[threadIdx.x + blockDim.x * 2] = *(((float*)cOut) + threadIdx.x + blockDim.x * 2);
        smem[threadIdx.x + blockDim.x * 3] = *(((float*)cOut) + threadIdx.x + blockDim.x * 3);
        smem[threadIdx.x + blockDim.x * 4] = *(((float*)cOut) + threadIdx.x + blockDim.x * 4);
        smem[threadIdx.x + blockDim.x * 5] = *(((float*)cOut) + threadIdx.x + blockDim.x * 5);
        smem[threadIdx.x + blockDim.x * 6] = *(((float*)cOut) + threadIdx.x + blockDim.x * 6);
        smem[threadIdx.x + blockDim.x * 7] = *(((float*)cOut) + threadIdx.x + blockDim.x * 7);*/
        __syncthreads();
        /*float x, y;
        float prevX, prevY;
        float speedX, speedY;
        float mass;
        float radius;*/
        /*c.x = smem[threadIdx.x];
        c.y = smem[threadIdx.x + blockDim.x]; //there must be [threadId.x * 8 + 0]
        c.prevX = smem[threadIdx.x + blockDim.x * 2]; //[threadId.x * 8 + 1] and e.t.c.
        c.prevY = smem[threadIdx.x + blockDim.x * 3];
        c.speedX = smem[threadIdx.x + blockDim.x * 4];
        c.speedY = smem[threadIdx.x + blockDim.x * 5];
        c.mass = smem[threadIdx.x + blockDim.x * 6];
        c.radius = smem[threadIdx.x + blockDim.x * 7];*/
        c = cOut[j];
        //c = *((Circle*)(smem + threadIdx * SMEM));
有2个gmem(我指的是全局内存)访问: 1) 读取圆圈并检测与它的碰撞 2) 改变速度和位置后写下圆圈 还有CircleConst Massible of Circle,它是由Cudamallotosybol()分配的。它用于检查与从gmem读取的主圆C(在寄存器中)的圆的交点

正如我所想,我很好地使用了const内存,它让我获得了所有的性能:'(我错了吗?)

当我读到关于gmem的联合访问(是否有对其他类型内存的联合访问?我没有找到任何关于它的信息)时,我想尝试一下。 如您所见,圆形结构有8个变量,类型为float=32位。我试过(在代码中有注释)这样做,但是,首先,我得到了一个错误的答案(因为我必须正确阅读smem,如下所述),其次,我得到的性能降低了33%。为什么?我认为,这并不取决于错误的场关系

第二个问题,正如我在从smem到C的代码中的注释中所写的,我必须以另一种方式阅读,但是如果我这样做,将会有很多银行冲突,因此我将获得更少的性能。。。 那么,我如何才能在没有银行冲突的情况下加载coalassed,然后将其写回呢

p、 s是位于寄存器中的大小超过4*浮点的结构吗


更新: 最新版本为:

#define CF (9) //9 because the primary struct has 8 floats, so 1 is for wasting

i = blockIdx.x * blockDim.x;
        smem[threadIdx.x + blockDim.x * 0 + blockDim.x * 0 / (CF - 1) + threadIdx.x / (CF - 1)] =   *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 0);
        smem[threadIdx.x + blockDim.x * 1 + blockDim.x * 1 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 1);
        smem[threadIdx.x + blockDim.x * 2 + blockDim.x * 2 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 2);
        smem[threadIdx.x + blockDim.x * 3 + blockDim.x * 3 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 3);
        smem[threadIdx.x + blockDim.x * 4 + blockDim.x * 4 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 4);
        smem[threadIdx.x + blockDim.x * 5 + blockDim.x * 5 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 5);
        smem[threadIdx.x + blockDim.x * 6 + blockDim.x * 6 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 6);
        smem[threadIdx.x + blockDim.x * 7 + blockDim.x * 7 / (CF - 1)  + threadIdx.x / (CF - 1)] =  *(((float*)(cOut + i)) + threadIdx.x + blockDim.x * 7);

c.x =       smem[threadIdx.x * CF + 0];
    c.y =       smem[threadIdx.x * CF + 1];
    c.prevX =   smem[threadIdx.x * CF + 2];
    c.prevY =   smem[threadIdx.x * CF + 3];
    c.speedX =  smem[threadIdx.x * CF + 4];
    c.speedY =  smem[threadIdx.x * CF + 5];
    c.mass =    smem[threadIdx.x * CF + 6];
    c.radius =  smem[threadIdx.x * CF + 7];
使用smem合并gmem访问是否正确?我的意思是,我害怕BlockDim.x*1/(CF-1)+threadIdx.x/(CF-1)。
我想,我并没有得到一些提升,因为它不允许gmem合并阅读超过一个圈,但我不明白,如何使它合并两个圈。

免责声明

请注意,此答案包含的问题多于答案。还要注意,我猜了很多,因为我没有得到你的问题和源代码的大部分

重建

我猜你的全局内存是一个
Circle
结构的数组。您似乎已经通过将每个圆的浮动分别加载到共享内存中来优化加载这些圆。通过这种方式,您可以获得连续的访问模式,而不是快速的访问模式。我在这里还是对的吗

因此,现在您已经协同将
blockDim.x
圆圈加载到共享内存中,您希望从中为每个线程读取圆圈
c
,您似乎尝试了3种不同的方法:

  • 从跨步共享内存加载
    c

    c.prevX=smem[threadIdx.x+blockDim.x*2];
    等)
  • 直接从共享内存加载
    c

    c=*((圆圈*)(smem+threadIdx*smem));
  • 直接从全局内存加载
    c

    c=cOut[j];
  • 仍然正确吗

    评估

  • 当你像我之前描述的那样将圆圈加载到共享内存中时,没有任何意义。因此,您可能尝试了不同的加载模式。如您在评论中所述,与
    [threadId.x*8+0]
    类似的内容。此解决方案的优点是可以连续进行全局访问,但可以使用存储冲突存储到smem中
  • 也不会更好,因为它在读取寄存器时存在银行冲突
  • 更糟糕的是,全局内存访问速度加快
  • 回答

    通过插入虚拟值,可以轻松解决银行冲突。您将使用
    [threadId.x*8+0]
    而不是使用
    [threadId.x*9+0]
    。请注意,在跨银行分布数据时,您正在浪费一点共享内存(即每九个浮点)。请注意,首先将数据加载到共享内存中时,必须执行相同的操作。但是请注意,您仍然在做大量的工作来加载这些
    Circle
    结构。这让我想到了一个

    更好的答案

    只是不要在全局内存中使用
    Circle
    structs数组。相反,使用多个浮点数组来反转内存模式。一个
    圆的每个组件一个
    。然后可以直接加载到寄存器中

    c.x = gmem_x[j];
    c.y = gmem_y[j];
    ...
    
    根本没有更多的共享内存,由于指针计算更少而导致的寄存器更少,连续的全局访问模式,没有银行冲突。所有这些都是免费的


    现在,在主机上准备数据并返回结果时,您可能会认为它有一个缺点。我的最佳(也是最后一个)猜测是,总体而言,它仍然会快得多,因为您可能会在不将数据传输回主机的情况下每帧启动内核并使用着色器进行可视化,或者在下载结果之前连续多次启动内核。对吗?

    是的,你很清楚我的意思。正如您所建议的,我尝试使用虚拟值。我得到了正确的答案(正确的渲染),但我没有得到任何加速或减速。是不是因为sizeof(Circle)==32b?(或者我做错了?我会给这个问题添加新版本)我的意思是,全局内存联合访问(正如我在文章中读到的)的大小是64b(对于float(我的情况)),128b(对于float2),256b(对于float3和float4)?我以前读过SoA与AoS模式的比较,但在我的项目中,我无法使用SoA重新创建它(出于某些原因)。