我正在尝试将uint8_t数组转换为uint32_t数组。但是,当我尝试这样做时,我似乎无法访问每个连续的4个字节。
让我们说我有一个8字节的uint8_t数组。我想访问字节2 - > 6作为一个uint32_t。
这些都获得相同的值*((uint32_t*)&uint8Array[0])
,*((uint32_t*)&uint8Array[1])
,*((uint32_t*)&uint8Array[2])
,*((uint32_t*)&uint8Array[3])
*((uint32_t*)&uint8Array[4])
得到字节4 - > 8如预期的那样。
所以看起来我无法从任何地址访问4个连续字节?
我有什么方法可以做到这一点吗?
答案 0 :(得分:6)
虽然CUDA中不允许未对齐访问,但prmt
PTX instruction有一个方便的模式来模拟寄存器中未对齐读取的效果。这可以通过一点inline PTX assembly来暴露。如果你能够容忍读数超过数组的末尾,代码变得非常简单:
{ f -> print f }
为确保超出数组末尾的访问权限仍然无害,请将分配的字节数向上舍入为4的倍数,然后再添加4个字节。
以上设备代码与容忍未对齐访问的little-endian主机上的以下代码具有相同的效果:
// WARNING! Reads past ptr!
__device__ uint32_t read_unaligned(void* ptr)
{
uint32_t result;
asm("{\n\t"
" .reg .b64 aligned_ptr;\n\t"
" .reg .b32 low, high, alignment;\n\t"
" and.b64 aligned_ptr, %1, 0xfffffffffffffffc;\n\t"
" ld.u32 low, [aligned_ptr];\n\t"
" ld.u32 high, [aligned_ptr+4];\n\t"
" cvt.u32.u64 alignment, %1;\n\t"
" prmt.b32.f4e %0, low, high, alignment;\n\t"
"}"
: "=r"(result) : "l"(ptr));
return result;
}
答案 1 :(得分:1)
如果你想要字节2..6,你将需要组合多个对齐的加载来获得你想要的东西。
uint32_t *ptr = ...;
uint32_t value = (ptr[0] >> 16) | (ptr[1] << 16);
从技术上讲,这也是也是一般用C语言做的便携式方式,但我们都被宠坏了,因为你不需要在x86,ARM,Power上做额外的工作,或其他常见架构。
答案 2 :(得分:0)
正如@DietrichEpp建议的那样,你必须做两次装载;并且正如@tera所建议的那样,即使未使用uint8Array
PTX指令预先知道未对齐(即当prmt
的初始地址是任意的)时,您也可以将这两个负载合并为便宜。 / p>
我将提供基于@ tera的解决方案,让您可以:
value = read_unaligned(&uint8Array[offset]);
安全且(相对)有效。此外,它只有一个内联PTX汇编指令,如果你需要它还有一个“不安全”变量:
#include <cstdint>
#include <cuda_runtime_api.h>
__device__ __forceinline__ uint32_t prmt_forward_4_extract(
uint32_t first_word,
uint32_t second_word,
uint32_t control_bits)
{
uint32_t result;
asm("prmt.b32.f4e %0, %1, %2, %3;"
: "=r"(result)
: "r"(first_word), "r"(second_word), "r"(control_bits) );
return result;
}
/*
* This unsafe, faster variant may read past the 32-bit naturally-aligned
* word containing the last relevant byte
*/
__device__ inline uint32_t read_unaligned_unsafe(const uint32_t* __restrict__ ptr)
{
/*
* Clear the bottom 2 bits of the address, making the result aligned
* for the purposes of reading a 32-bit (= 4-byte) value
*/
auto aligned_ptr = (uint32_t*) ((uint64_t) ptr & ~((uint64_t) 0x3));
auto first_value = *aligned_ptr;
auto second_value = *(aligned_ptr + 1);
auto lower_word_of_ptr = (uint32_t)((uint64_t)(ptr));
return prmt_forward_4_extract(first_value, second_value, lower_word_of_ptr);
}
__device__ inline uint32_t read_unaligned(const uint32_t* __restrict__ ptr)
{
auto ptr_is_already_aligned = ((uint64_t)(ptr) & 0x3 == 0);
if (ptr_is_already_aligned) { return *ptr; }
return read_unaligned_unsafe(ptr);
}