Optimization OpenCL—将字节拆分为8分量向量的最有效方法
我正在OpenCL中构建伊辛模型的模拟,这意味着我的数据由一系列状态组成,这些状态可以是up/1或down/-1 为了节省内存带宽,这些状态中的8个被编码成一个字节(up=1,down=0)。现在在其中一个计算中,我需要一个整数向量,其值对应于原始状态,即1或-1 示例:Optimization OpenCL—将字节拆分为8分量向量的最有效方法,optimization,opencl,Optimization,Opencl,我正在OpenCL中构建伊辛模型的模拟,这意味着我的数据由一系列状态组成,这些状态可以是up/1或down/-1 为了节省内存带宽,这些状态中的8个被编码成一个字节(up=1,down=0)。现在在其中一个计算中,我需要一个整数向量,其值对应于原始状态,即1或-1 示例: 输入字节(OpenCL中的uchar):01010011 转换为:(int8)(-1,1,-1,1,-1,1,1) 我确实有一个解决这个问题的有效方法,但我想知道是否有一个更快、更有效的方法: uchar c = spins[
输入字节(OpenCL中的uchar):
01010011
转换为:
(int8)(-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个线程访问相同的位置):
然后在每个线程上处理不同的位索引:
spin.s0 = (c >> 0) & 1; (on thread 0)
应该给它一些性能,但只有一个旋转元素。许多最新的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;
如果spin必须是私有变量,则可以使用与通信数组相同的本地内存数组将值复制到所有线程的私有变量。这将从(指令级+线程级)并行性转变为仅线程级并行性
选项3)
您可以将位选择(全部8个)分配到内核的不同“单元”,如果操作是在不同单元中完成的,那么无序执行可能会带来好处
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
谢谢不过我并没有真正得到第三个元素,它就像用一种昂贵但独立的方法得到一个spin元素,当它计算繁重的工作时,其他元素是用指令级并行计算的。另外,最后一个元素不需要与1进行“and”运算。因为右边只有一个位。你用这种方式保存另一条指令。我认为在这种情况下使用工会是行不通的。它确实在我的机器(AMD)上编译,但会产生奇怪的结果。我不认为可以用这种方式处理字节的位,因为布尔值可能不仅仅是一个位宽。这似乎已经是一个很好的解决方案了,为什么要用更复杂的方法呢<代码>int8自旋=((int8)(c)>>(int8)(0,1,2,3,4,5,6,7)&1)*2-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;
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