OpenCL 设备的字节顺序
Endianness Of OpenCL devices
除了 gpu、cpu 和定制的 opencl 芯片之间的精度/准确性/对齐不一致之外,为什么互联网上的所有 opencl 示例都直接接受 host 端缓冲区?
clEnqueueWriteBuffer ( cl_command_queue command, ...)
__kernel void vecAdd(__global float * c, ..)
{
int id=get_global_id(0);
c[id]=b[id]+a[id]
}
假设 host 是小端,但设备是大端,a、b、c 是浮点数:
b和a会被加载为非标准化垃圾吗?
- 如果是,我是否必须发送一个字节告诉内核有关缓冲区字节顺序的信息?
- 如果不是,它们是否在缓冲区读写操作期间自动转换(使用 CPU 的 4 字节 SIMD 改组?或在 GPU 上?)?即使对于 USE_HOST_PTR(RAM) 类型的缓冲区和 CL_MEM_READ_WRITE(GDDR)?
如果未知,下面的示例函数是否总是有效?
int endianness_of_device()
{
unsigned int x = 1;
return (( ( (char *)&x) [0])==0?0:1) ;
}
int reverseBytesInt(int bytes)
{
void * p = &bytes;
uchar4 bytesr = ((uchar4 *)p)[0].wzyx;
int * p2=(int *)&bytesr;
return *p2;
}
float reverseBytesFloat(float bytes)
{
void * p = &bytes;
uchar4 bytesr = ((uchar4 *)p)[0].wzyx;
float * p2=(float *)&bytesr;
return *p2;
}
uint sizeOf2(uint structSize)
{
uit mult0=structSize/256;mult0++;
return mult0*256;
}
typedef struct
{
uchar end;
uchar sizeRelByteAdr;
uchar adrRelByteAdr;
uchar idRelByteAdr;
uchar valRelByteAdr;
uchar woRelByteAdr;
int size;
uint adr;
int id;
float val;
float wo;
}nn_p_n;
uint malloc(__global uchar * heap, __global uint * mallocCtr, int size)
{
return (atomic_add(mallocCtr,(uint)size)+(uint)heap);
}
帮助像这样的内核:
__kernel void nn( __global uchar *heap,__global uint * mallocCtr)
{
int id=get_global_id(0);
if(id==0)
{
nn_p_n * np=(nn_p_n *)malloc(heap,mallocCtr,sizeOf2(sizeof(nn_p_n)));
np->end=endianness_of_device();
np->size=sizeOf2(sizeof(nn_p_n));
np->id=9;
np->val=99.9f;
// lets simulate different endianness
np->end=1-endianness_of_device();
np->adr=(uint)np-(uint)heap;
mem_fence(CLK_GLOBAL_MEM_FENCE);
}
if(id==900)
{
// simulating another device reads buffer
for(int i=0;i<1000000;i++){int dummy=0; dummy++; if(dummy>id) dummy++;}
nn_p_n n=*((nn_p_n *)&heap[0]);
if(n.end!=endianness_of_device())
{
n.size=reverseBytesInt(n.size);
//if(n.size!=sizeof2(sizeof(nn_p_n)))
//{ return; }
n.adr=reverseBytesInt(n.adr);
n.val=reverseBytesFloat(n.val);
}
nn_p_n * np=(nn_p_n *)malloc(heap,mallocCtr,sizeOf2(sizeof(nn_p_n)));
*np = n;
}
}
因为它目前正在使用 Intel CPU 为我的 Intel igpu 工作,而使用 Amd CPU 和 Amd GPU 的其他机器没有任何字节序问题。如果我将来在 Intel CPU 之上获得 Nvidia gpu 和 Amd gpu 怎么办?
当然,在集群计算中,需要涵盖计算机之间的字节序情况,但是虚拟 os 运行 在另一台 os 中并使用相同的设备呢?该设备是一个具有多个字节序内核的 fpga 怎么样(possible?)?
最后一个问题:OS 是否可以强制所有设备,甚至 CPU 变为相同的字节顺序?(我不这么认为,但可以由 OS 在性能 cost?)
编辑:无法os在设备端预处理只读数据。在 host 侧 those 标记为 "written" 的元素上 postprocess 是矫枉过正的,因为在整个数据中只能写入 1-2 个元素下载到 host.
可能有千兆字节
传递给内核的参数保证具有正确的字节序(因此所有类型定义 cl_int
,等等),但缓冲区并非如此。这是有道理的,因为缓冲区的内容对于 OpenCL 是完全不透明的:只有用户知道如何理解里面的内容。因此,用户有责任在进行计算之前执行潜在的字节顺序转换(可能通过启动专用内核)。
换句话说:
__kernel void vecAdd(__global float * c, ..)
这里,c
的 value 保证是正确的字节顺序(指针的字节本身是正确的设备顺序),但是字节 由c
指向的顺序是用户在主机上设置的顺序。
why all opencl examples over internet directly accept host-side buffers?
大多数程序都是在一组相当狭窄的目标平台上开发的,这些平台的特征是事先已知的:如果它们都是小端,为什么还要支持大端呢?可移植性已经是一个难题,我怀疑在一般情况下,关心字节顺序是一种显着的额外复杂性,几乎没有附加值。对于绝大多数程序来说,这根本不值得。
如果您认为这种级别的可移植性很有价值,那么实施它是您的选择。
除了 gpu、cpu 和定制的 opencl 芯片之间的精度/准确性/对齐不一致之外,为什么互联网上的所有 opencl 示例都直接接受 host 端缓冲区?
clEnqueueWriteBuffer ( cl_command_queue command, ...)
__kernel void vecAdd(__global float * c, ..)
{
int id=get_global_id(0);
c[id]=b[id]+a[id]
}
假设 host 是小端,但设备是大端,a、b、c 是浮点数:
b和a会被加载为非标准化垃圾吗?
- 如果是,我是否必须发送一个字节告诉内核有关缓冲区字节顺序的信息?
- 如果不是,它们是否在缓冲区读写操作期间自动转换(使用 CPU 的 4 字节 SIMD 改组?或在 GPU 上?)?即使对于 USE_HOST_PTR(RAM) 类型的缓冲区和 CL_MEM_READ_WRITE(GDDR)?
如果未知,下面的示例函数是否总是有效?
int endianness_of_device() { unsigned int x = 1; return (( ( (char *)&x) [0])==0?0:1) ; } int reverseBytesInt(int bytes) { void * p = &bytes; uchar4 bytesr = ((uchar4 *)p)[0].wzyx; int * p2=(int *)&bytesr; return *p2; } float reverseBytesFloat(float bytes) { void * p = &bytes; uchar4 bytesr = ((uchar4 *)p)[0].wzyx; float * p2=(float *)&bytesr; return *p2; } uint sizeOf2(uint structSize) { uit mult0=structSize/256;mult0++; return mult0*256; } typedef struct { uchar end; uchar sizeRelByteAdr; uchar adrRelByteAdr; uchar idRelByteAdr; uchar valRelByteAdr; uchar woRelByteAdr; int size; uint adr; int id; float val; float wo; }nn_p_n; uint malloc(__global uchar * heap, __global uint * mallocCtr, int size) { return (atomic_add(mallocCtr,(uint)size)+(uint)heap); }
帮助像这样的内核:
__kernel void nn( __global uchar *heap,__global uint * mallocCtr)
{
int id=get_global_id(0);
if(id==0)
{
nn_p_n * np=(nn_p_n *)malloc(heap,mallocCtr,sizeOf2(sizeof(nn_p_n)));
np->end=endianness_of_device();
np->size=sizeOf2(sizeof(nn_p_n));
np->id=9;
np->val=99.9f;
// lets simulate different endianness
np->end=1-endianness_of_device();
np->adr=(uint)np-(uint)heap;
mem_fence(CLK_GLOBAL_MEM_FENCE);
}
if(id==900)
{
// simulating another device reads buffer
for(int i=0;i<1000000;i++){int dummy=0; dummy++; if(dummy>id) dummy++;}
nn_p_n n=*((nn_p_n *)&heap[0]);
if(n.end!=endianness_of_device())
{
n.size=reverseBytesInt(n.size);
//if(n.size!=sizeof2(sizeof(nn_p_n)))
//{ return; }
n.adr=reverseBytesInt(n.adr);
n.val=reverseBytesFloat(n.val);
}
nn_p_n * np=(nn_p_n *)malloc(heap,mallocCtr,sizeOf2(sizeof(nn_p_n)));
*np = n;
}
}
因为它目前正在使用 Intel CPU 为我的 Intel igpu 工作,而使用 Amd CPU 和 Amd GPU 的其他机器没有任何字节序问题。如果我将来在 Intel CPU 之上获得 Nvidia gpu 和 Amd gpu 怎么办?
当然,在集群计算中,需要涵盖计算机之间的字节序情况,但是虚拟 os 运行 在另一台 os 中并使用相同的设备呢?该设备是一个具有多个字节序内核的 fpga 怎么样(possible?)?
最后一个问题:OS 是否可以强制所有设备,甚至 CPU 变为相同的字节顺序?(我不这么认为,但可以由 OS 在性能 cost?)
编辑:无法os在设备端预处理只读数据。在 host 侧 those 标记为 "written" 的元素上 postprocess 是矫枉过正的,因为在整个数据中只能写入 1-2 个元素下载到 host.
可能有千兆字节传递给内核的参数保证具有正确的字节序(因此所有类型定义 cl_int
,等等),但缓冲区并非如此。这是有道理的,因为缓冲区的内容对于 OpenCL 是完全不透明的:只有用户知道如何理解里面的内容。因此,用户有责任在进行计算之前执行潜在的字节顺序转换(可能通过启动专用内核)。
换句话说:
__kernel void vecAdd(__global float * c, ..)
这里,c
的 value 保证是正确的字节顺序(指针的字节本身是正确的设备顺序),但是字节 由c
指向的顺序是用户在主机上设置的顺序。
why all opencl examples over internet directly accept host-side buffers?
大多数程序都是在一组相当狭窄的目标平台上开发的,这些平台的特征是事先已知的:如果它们都是小端,为什么还要支持大端呢?可移植性已经是一个难题,我怀疑在一般情况下,关心字节顺序是一种显着的额外复杂性,几乎没有附加值。对于绝大多数程序来说,这根本不值得。
如果您认为这种级别的可移植性很有价值,那么实施它是您的选择。