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