在子字节上使用 SSE 进行位操作?
Posted
技术标签:
【中文标题】在子字节上使用 SSE 进行位操作?【英文标题】:Bit manipulations with SSE on subbytes? 【发布时间】:2011-07-12 20:04:00 【问题描述】:是否可以使用 SSE 对非字节对齐的数据进行位操作?例如,我想使用 SSE 来实现:
const char buf[8];
assert(n <= 8);
long rv = 0;
for (int i = 0; i < n; i++)
rv = (rv << 6) | (buf[i] & 0x3f);
相反,我想将 buf 加载到 xmm 寄存器中并使用 SSE 指令来避免循环。不幸的是,移位操作(例如 PSLLW)将每个压缩整数移位 same 数量,所以我不能在这里使用它。使用乘法 (PMULLW) 来模拟班次似乎也不对...
查看 SSE 文档,似乎一般来说位操作并没有得到特别好的支持。这是真的?或者有没有使用 SSE 的不错的位操作示例?
【问题讨论】:
【参考方案1】:我不确定 SSE 指令是否有助于减少实现代码在此处执行的操作所需的操作数量;如果有人知道,我也会很好奇。让我们稍微分解一下代码。
代码是一个递归移位/或序列,这意味着你取最低 6 位,将它们左移 6 位,或者接下来的 6 位,再次移位,依此类推。
因此,您将一个 8 位值数组转换为一个 6 位值的压缩数组,您将事物从 64 位缩小到 48 位。喜欢:
|76543210|76543210|76543210|76543210|76543210|76543210|76543210|76543210| |-----------------|54321054|32105432|10543210|54321054|32105432|10543210|因此,您可以展开循环并将其编写如下:
/*
* (buf[x] << 58)
* moves lowest six bits of a 64bit long into the highest bits, clears others
*
* >> (6 * x + 16)
* shifts the bits into the expected final position
*/
#define L(x) (((long)buf[x] << 58) >> (6 * x + 16))
long rv = L(0) | L(1) | L(2) | L(3) | L(4) | L(5) | L(6) | L(7);
如前所述,我不知道有什么 SSE 指令可以帮助进行这种打包(SSE 包执行四到字、字到短、短到字节)。
您可以在 SSE 寄存器中执行操作,但据我所知,不能减少获得最终结果所需的指令数量。
【讨论】:
【参考方案2】:您可以在 SSE 中执行很多按位运算。您可以只使用 _mm_and_si128、_mm_or_si128 并且有大量的移位操作。谷歌 _mm_slli_si128 找到完整列表。这些说明已添加到 SSE2,因此可以广泛使用。
【讨论】:
我发现Intel's Intrinsic Guide 对查找 SSE 指令特别有用。不幸的是,即使有许多移位指令,它们都采用固定的移位量(即,不能以不同的方式移位每个压缩整数)...... 您将能够在 2013 年使用 AVX 2.0 做到这一点,但在那之前您运气不佳。 是的,SSE 在某些操作方面非常糟糕。只需使用整数乘法即可获得相同的结果。 这里有_mm_sll_si128,取一个xmm寄存器和移位量。你想做的事都可以在上交所完成。 @drhirsh,如果我理解正确的话,这也会将每个打包整数移动 same 的数量,所以它在这里没有用......以上是关于在子字节上使用 SSE 进行位操作?的主要内容,如果未能解决你的问题,请参考以下文章
在 64 位机器上,我可以安全地并行处理 64 位四字的各个字节吗?
为什么一个指针在32位系统中占4个字节,在64位系统中占8个字节?