Cuda 读取前将数组强制转换为不同的数据类型会导致间歇性问题

Cuda 读取前将数组强制转换为不同的数据类型会导致间歇性问题,cuda,Cuda,我在全局内存中有一个int数组。为了减少从全局内存读取的次数,我一直在尝试使用64位的数据类型进行读取,然后根据需要使用高或低32位。例如,这将从数组中获取第3个和第4个整数: __device__ void func1(int* arr) { unsigned long long int val = *((unsigned long long int *) &arr[3]); // Now operate on the individual ints } 使用此方法

我在全局内存中有一个int数组。为了减少从全局内存读取的次数,我一直在尝试使用64位的数据类型进行读取,然后根据需要使用高或低32位。例如,这将从数组中获取第3个和第4个整数:

__device__ void func1(int* arr)
{
    unsigned long long int val = *((unsigned long long int *) &arr[3]);

    // Now operate on the individual ints
}

使用此方法检索ints会给我带来未定义的行为,尽管这似乎应该可以工作。当它工作时,以这种方式读取值比读取单个整数要快得多。以前有人遇到过这个问题吗?

数量喜欢按大小排列。我不确定cuda如何处理您正在做的事情,也不确定它可能是特定于环境的,但您使用:

*((unsigned long long int *) &arr[3])
假设
arr
是8字节对齐的,则取的是仅4字节对齐的8字节数量。这当然是因为:

arr = 8n           // n is an integer
sizeof(int) = 4

&arr[3] = 8n + 3*4 // simplifies to 8(n+1) + 4
我知道,如果在处理器上使用32位和16位整数(尽管我从未尝试过使用64位和32位整数)执行相同的操作,您将遇到问题

您需要自制某种访问器协议,以确定您试图访问的数据块的位置。请考虑以下情况,与您的情况相似:

int get32BitValueFrom(unsigned long long int longArray[], int index)
{
    // get the 64 bit int containing the 32 bit int we want
    unsigned long long int value = longarray[index >> 1];

    // if we wanted an odd index, return the high order 32 bits
    // otherwise return the low order 32 bits
    return (int) ((index & 1) ? (value >> 32) : (value));
}

编辑:我知道您正在使用cuda,我知道可以避免分支,但我确信有一种方法可以使用某种按位或数学技巧编写等效代码,实现同样的效果。

数量喜欢按大小对齐。我不确定cuda如何处理您正在做的事情,也不确定它可能是特定于环境的,但您使用:

*((unsigned long long int *) &arr[3])
假设
arr
是8字节对齐的,则取的是仅4字节对齐的8字节数量。这当然是因为:

arr = 8n           // n is an integer
sizeof(int) = 4

&arr[3] = 8n + 3*4 // simplifies to 8(n+1) + 4
我知道,如果在处理器上使用32位和16位整数(尽管我从未尝试过使用64位和32位整数)执行相同的操作,您将遇到问题

您需要自制某种访问器协议,以确定您试图访问的数据块的位置。请考虑以下情况,与您的情况相似:

int get32BitValueFrom(unsigned long long int longArray[], int index)
{
    // get the 64 bit int containing the 32 bit int we want
    unsigned long long int value = longarray[index >> 1];

    // if we wanted an odd index, return the high order 32 bits
    // otherwise return the low order 32 bits
    return (int) ((index & 1) ? (value >> 32) : (value));
}

编辑:我知道您正在使用cuda,我知道可以避免分支,但我确信有一种方法可以使用某种按位或数学技巧编写等效代码,实现相同的功能。

GPU硬件要求所有内存访问都与访问宽度对齐(“自然对齐”)。因此,无符号长整型必须与8字节边界对齐。未对齐的访问导致未定义的行为;在加载的情况下,将返回随机数据。如果为sm_1x构建代码,则使用Open64前端。如果将以下内容添加到nvcc命令行中,Open64可以警告您这些情况:-Xopencc-Wcast ALIGNNE因此,我假设将数组对齐到8字节边界需要知道原始数组的开始?虽然很烦人,但跟踪它听起来并不坏,所以我必须尝试一下。最简单的方法是通过无符号long long int数组或uint2数组(也有8字节对齐)传递数据。否则,您必须在运行时检查对齐。如果要保持当前传入无符号int数组的方法,除非代码受计算限制,否则合成无符号long long int可能会有良好的性能。GPU硬件要求所有内存访问都与访问宽度对齐(“自然对齐”)。因此,无符号长整型必须与8字节边界对齐。未对齐的访问导致未定义的行为;在加载的情况下,将返回随机数据。如果为sm_1x构建代码,则使用Open64前端。如果将以下内容添加到nvcc命令行中,Open64可以警告您这些情况:-Xopencc-Wcast ALIGNNE因此,我假设将数组对齐到8字节边界需要知道原始数组的开始?虽然很烦人,但跟踪它听起来并不坏,所以我必须尝试一下。最简单的方法是通过无符号long long int数组或uint2数组(也有8字节对齐)传递数据。否则,您必须在运行时检查对齐。如果要保持当前传入无符号int数组的方法,合成无符号long long int可能会有良好的性能,除非您的代码是计算受限的。CUDA内置了向量类型int2和int4,这两种类型会更好。编译器了解这些指令的对齐方式,并将安全地生成64位或128位加载指令。在相同的条件下,读入int2或int4会生成相同的未定义行为。其思想是不读入int2或int4,而是读入整个结构,从而生成最佳加载指令。加载后,您可以将其转换为整数类型,并根据您的内心内容调整位。CUDA内置了向量类型int2和int4,这两种类型更适合于此。编译器了解这些指令的对齐方式,并将安全地生成64位或128位加载指令。在相同的条件下,读入int2或int4会生成相同的未定义行为。其思想是不读入int2或int4,而是读入整个结构,从而生成最佳加载指令。加载后,您可以将其转换为整数类型,并随心所欲地摆弄位。