Cuda有效地从字节数组复制到不同大小的共享存储器元素

时间:2015-07-19 10:47:20

标签: c++ cuda

我有一个字节数组( unsigned char * ),表示内存中的数据结构树。树的每个节点都包含不同大小的元素: 1 bool 开头, n unsigned int s和 n 无符号短。我这样做是因为拥有最少的内存使用对我来说非常重要。不幸的是,当我尝试访问从全局内存复制到共享内存时,会导致内存对齐问题:

__global__ void sampleerror(unsigned char * global_mem, unsigned int updated_idx...) {
    __shared__ unsigned int offsets[MAX_NUM_CHILDREN/2 +1]; 
    __shared__ unsigned int entries[ENTRIES_PER_NODE];
    __shared__ bool booleans[4];
    bool * is_last = &booleans[0];
    //First warp divergence here. We are reading in from global memory
    if (i == 0) {
        *is_last = (bool)global_mem[updated_idx];
    }
    __syncthreads();

    if (*is_last) {
        //The number of entries in the bottom most nodes may be smaller than the size
        if (i < (size - 1)/entry_size) {
            entries[i] = *(unsigned int *)(&global_mem[updated_idx + 1 + i*sizeof(unsigned int)]);
        }
    } else {
        int num_entries = (size - 1 - sizeof(unsigned int) - sizeof(unsigned short))/(entry_size + sizeof(unsigned short));
        //Load the unsigned int start offset together with the accumulated offsets to avoid warp divergence
        if (i < ((num_entries + 1)/2) + 1) {
            offsets[i] = *(unsigned int *)(&global_mem[updated_idx + 1 * i*sizeof(unsigned int)]);
        }
        __syncthreads();
        //Now load the entries
        if (i < num_entries) {
            entries[i] = *(unsigned int *)(&global_mem[updated_idx + 1 + (num_entries + 1)*sizeof(unsigned int) + i*sizeof(unsigned int)]);
        }
    }
    __syncthreads();
}

我得到了错误的内存访问,因为我试图在这里复制到共享内存(以及在else语句中):

entries[i] = *(unsigned int *)(&global_mem[updated_idx + 1 + i*sizeof(unsigned int)]);

因为 updated_idx + 1 没有必要对齐。问题:

1)如果我不想填充我的数据结构以便很好地对齐整数,那么逐字节复制是唯一的选择吗?

2)如果我将字节字节复制到全局的共享内存中,它会比我能够复制 unsigned int慢4倍 unsigned int

3)如果我逐字节地进行,是否有可能出现未对齐的内存访问?我想我已经读过字节访问总是对齐的。

编辑:

我有一个btree-ish数据结构,其中每个节点都包含以下形式的有效负载:

struct Entry {
    unsigned int key;
    unsigned int next_level_offset;
    float prob1;
    float prob2;
 }

为了搜索btree,我只需要每个条目的关键信息,而不是结构中的其他信息。因此,每个节点按以下方式折叠为字节数组:

(bool is_last)(key1,key2,key3 ...)((偏移,key1的prob1 prob2),(偏移,key2的prob1 prob2),(偏移,key3的prob1 prob2))(unsigned int first_child_start_offset) (short sizeofChild1,short sizeofChild2,short sizeofChild3 ...)

显然,如果is_last为false,则只存储没有childrenOffsets。

我以这种方式布局数据的原因是每个节点的条目数可以是可变的,所以如果我将单独的东西存储在单独的数组中,我将不得不追踪那些&#的开始和结束索引。 34;元数据&#34;在搜索期间会导致存储更多数据或必须使用状态机的数组,我想避免这种情况。我相信它可以通过相对较少的工作来完成每个节点的bool部分,但不能用于其他任何事情(如偏移)。

1 个答案:

答案 0 :(得分:1)

  1. 如果我不想填充我的数据结构以便很好地对齐整数,那么逐字节复制是唯一的选择吗?

    看看你提供的代码,或多或少会说,是的。您可能想要使用memcpy。编译器将通过这样做发出非常优化的字节复制循环。您可能还想调查已更改的ptxas默认缓存行为,以便加载绕过L1缓存(因此-Xptxas =&#34; - def-load-cache = cg&#34;选项)。它可能会带来更好的表现。

  2. 如果我从全局逐字节地复制到共享内存,它会比我能用unsigned int复制unsigned int慢4倍。

    您应该期望降低内存吞吐量。没有基准测试,很难说多少。如果你这么倾向,这就是你的工作

  3. 如果我逐字节地进行,是否可能会出现未对齐的内存访问?我想我已经读过字节访问始终是对齐的。

    对齐标准始终是字大小。所以单字节字总是对齐的。但请记住,如果您对共享内存缓冲区执行字节加载,然后尝试使用reinterpret_cast读出未与共享字节数组对齐的较大字大小,则会遇到同样的问题。

  4. 您还没有详细了解给定子树的大小。您可以使用一些模板技巧将先验已知大小的字节加载扩展为一系列32位char4加载,其中包含1到3个尾随字节加载,以便将字节缓冲区大小释放到内存中。如果它适合您的数据结构设计,那应该更高效。