OpenCL - 将字节拆分为 8 分量向量的最有效方法
OpenCL - Most efficient way to split byte into an 8-component-vector
我正在 OpenCL 中构建 Ising 模型的模拟,这意味着我的数据由一堆状态组成,可以是 up/1 或 down/-1。
为了节省内存带宽,这些状态中的 8 个被编码为一个字节(上=1,下=0)。现在,在其中一个计算中,我需要一个整数向量,其值对应于原始状态,即 1 或 -1。
示例:
输入字节(OpenCL 中的 uchar):01010011
转换为:(int8)(-1,1,-1,1,-1,-1,1,1);
我确实有解决该问题的方法,但我想知道是否有更快、更有效的方法:
uchar c = spins[id];
int8 spin;
spin.s0 = (c >> 0) & 1;
spin.s1 = (c >> 1) & 1;
spin.s2 = (c >> 2) & 1;
spin.s3 = (c >> 3) & 1;
spin.s4 = (c >> 4) & 1;
spin.s5 = (c >> 5) & 1;
spin.s6 = (c >> 6) & 1;
spin.s7 = (c >> 7) & 1;
spin = spin * 2 - 1;
编辑:
在我的情况下似乎没有更快,但至少更简洁:
__constant uchar8 bits = (uchar8)(0,1,2,3,4,5,6,7);
uchar c = spins[id];
int8 spin = convert_int8((uchar8)(c) >> bits & 1) * 2 - 1;
bool8 看起来仍然是一个保留类型。我以为现在就对用户开放了,我错了
选项 1)
不安全也不(%100 肯定)在所有硬件上工作,但你可以定义这个联合
typedef union hardwareBool8{
char v;
bool bit_select[8];
} vecb8;
然后在内核中:
vecb8 t={5}; // initialize with any number from your uchar/char
t.v=1; // or initialize with this
t.bit_select[4]=0; // set or get to some integer
int intVariable =t.bit_select[7]; // can be 1 or 0 or -1,you should try. If not -1 then you can negate
int intVariable2=-t.bit_select[7];
这是在我的 amd 机器上编译的,但我不确定是否适用于任何其他硬件。
甚至字节顺序也可能是个问题。
选项 2)
可能向 8 个线程广播相同的字符(或从 8 个线程访问相同的位置):
char charVar= ... load from same address/index ;
然后在每个线程上处理不同的位索引:
spin.s0 = (c >> 0) & 1; (on thread 0)
...
spin.s7 = (c >> 7) & 1; (on thread 7)
应该给它一些性能,但只针对单个旋转元素。许多最新的 gpu 架构支持在一条指令中向所有线程广播相同的数据。如果您的设备是 CPU,每个工作组 8 个线程应该不会慢很多,但是如果它是 gpu,那么每连续 8 个线程选择 1 个字符就很棘手了。像
charArrayIndex = globalThreadId / 8
c = charArray[charArrayIndex];
// assuming spin is local memory array and shared by work group threads
spin[globalThreadId % 8] = (c >> (globalThreadId % 8)) & 1;
如果自旋必须是私有变量,您可以使用相同的本地内存数组作为通信数组,将值复制到所有线程的私有变量。这是从(指令级+线程级)并行到只有线程级并行。
选项 3)
您可以将位选择(全部 8 个)分配给内核的不同 "units",如果操作在不同的单元中完成,那么乱序执行可能会有所帮助。
spin.s2 = (c / 4) & 1; // 1 division and 1 logical
spin.s0 = (c) & 1; // 1 logical
spin.s1 = (c & 2)>0; // 1 logical and 1 comparison
我正在 OpenCL 中构建 Ising 模型的模拟,这意味着我的数据由一堆状态组成,可以是 up/1 或 down/-1。
为了节省内存带宽,这些状态中的 8 个被编码为一个字节(上=1,下=0)。现在,在其中一个计算中,我需要一个整数向量,其值对应于原始状态,即 1 或 -1。
示例:
输入字节(OpenCL 中的 uchar):01010011
转换为:(int8)(-1,1,-1,1,-1,-1,1,1);
我确实有解决该问题的方法,但我想知道是否有更快、更有效的方法:
uchar c = spins[id];
int8 spin;
spin.s0 = (c >> 0) & 1;
spin.s1 = (c >> 1) & 1;
spin.s2 = (c >> 2) & 1;
spin.s3 = (c >> 3) & 1;
spin.s4 = (c >> 4) & 1;
spin.s5 = (c >> 5) & 1;
spin.s6 = (c >> 6) & 1;
spin.s7 = (c >> 7) & 1;
spin = spin * 2 - 1;
编辑:
在我的情况下似乎没有更快,但至少更简洁:
__constant uchar8 bits = (uchar8)(0,1,2,3,4,5,6,7);
uchar c = spins[id];
int8 spin = convert_int8((uchar8)(c) >> bits & 1) * 2 - 1;
bool8 看起来仍然是一个保留类型。我以为现在就对用户开放了,我错了
选项 1)
不安全也不(%100 肯定)在所有硬件上工作,但你可以定义这个联合
typedef union hardwareBool8{
char v;
bool bit_select[8];
} vecb8;
然后在内核中:
vecb8 t={5}; // initialize with any number from your uchar/char
t.v=1; // or initialize with this
t.bit_select[4]=0; // set or get to some integer
int intVariable =t.bit_select[7]; // can be 1 or 0 or -1,you should try. If not -1 then you can negate
int intVariable2=-t.bit_select[7];
这是在我的 amd 机器上编译的,但我不确定是否适用于任何其他硬件。 甚至字节顺序也可能是个问题。
选项 2)
可能向 8 个线程广播相同的字符(或从 8 个线程访问相同的位置):
char charVar= ... load from same address/index ;
然后在每个线程上处理不同的位索引:
spin.s0 = (c >> 0) & 1; (on thread 0)
...
spin.s7 = (c >> 7) & 1; (on thread 7)
应该给它一些性能,但只针对单个旋转元素。许多最新的 gpu 架构支持在一条指令中向所有线程广播相同的数据。如果您的设备是 CPU,每个工作组 8 个线程应该不会慢很多,但是如果它是 gpu,那么每连续 8 个线程选择 1 个字符就很棘手了。像
charArrayIndex = globalThreadId / 8
c = charArray[charArrayIndex];
// assuming spin is local memory array and shared by work group threads
spin[globalThreadId % 8] = (c >> (globalThreadId % 8)) & 1;
如果自旋必须是私有变量,您可以使用相同的本地内存数组作为通信数组,将值复制到所有线程的私有变量。这是从(指令级+线程级)并行到只有线程级并行。
选项 3)
您可以将位选择(全部 8 个)分配给内核的不同 "units",如果操作在不同的单元中完成,那么乱序执行可能会有所帮助。
spin.s2 = (c / 4) & 1; // 1 division and 1 logical
spin.s0 = (c) & 1; // 1 logical
spin.s1 = (c & 2)>0; // 1 logical and 1 comparison