【发布时间】:2020-07-13 22:35:22
【问题描述】:
问题:
我有一系列索引位7 6 5 4 3 2 1 0,我想通过以下方式混合它们:
7 6 5 4 3 2 1 0 = 7 6 5 4 3 2 1 0
_____| | | | | | | |_____
| ___| | | | | |___ |
| | _| | | |_ | |
| | | | | | | |
v v v v v v v v
_ 3 _ 2 _ 1 _ 0 7 _ 6 _ 5 _ 4 _
|___________________|
|
v
7 3 6 2 5 1 4 0
即我想从一个字节中交错低半字节和高半字节的位。
天真的解决方案:
我可以使用以下方式在 C 中实现此行为:
int output =
((input & (1 << 0)) << 0) |
((input & (1 << 1)) << 1) |
((input & (1 << 2)) << 2) |
((input & (1 << 3)) << 3) |
((input & (1 << 4)) >> 3) |
((input & (1 << 5)) >> 2) |
((input & (1 << 6)) >> 1) |
((input & (1 << 7)) >> 0);
但是它显然很笨重。
争取更优雅的解决方案:
我想知道是否有什么方法可以用更少的机器指令更快地实现这种行为。以 SSE 为例?
好奇的人的一些背景:
我使用它来将 2d 有符号整数向量坐标打包成 1d 值,在处理内存和缓存时可以保持邻近度。这个想法类似于移动设备上某些 GPU 使用的一些纹理布局优化。
(i ^ 0xAAAAAAAA) - 0xAAAAAAAA 将 1d 整数转换为 1d 有符号整数,使用我所说的两个邻近度的幂。
(x + 0xAAAAAAAA) ^ 0xAAAAAAAA 只是反向操作,从 1d 有符号整数到 1d 整数,仍然具有相同的属性。
为了让它变成 2d 并保持邻近属性,我想交替 x 和 y 位。
【问题讨论】:
-
我可能会生成一个包含所有 256 个字符(覆盖整个值空间)的预混排查找表,例如
char swiz[256]; for (int i = 0; i < 256; i++) swiz[i] = swizzle(i); -
@paddy 谢谢你的回答。我虽然关于那个选择。我仍然想知道是否没有一条指令可以避免自己在缓存中保存这个查找表的麻烦。
-
你签出this了吗?它将两个 16 位值的位交错。据推测,您可以将该方法调整为 4 位值,尽管它可能效率低下。或者,也许你真的想在更广泛的数据上做这件事。
-
@paddy 哇,现在这很有趣!指令量基本上减半。我需要更好地研究它才能完全理解它......但是,如果它符合我的期望,那么它绝对是一个很好的解决方案。我会进行一些测试,看看它会导致什么。不,对于非常小的向量,我确实需要它,因为这与 GPU 的限制有关。然而,即使这种方法看起来也是可扩展的。非常感谢您富有洞察力的回答。
-
也许可以看看上面那个interleaves two 8-bit values with 64-bit multiplication的方法。我认为您可以将其更改为将两个 4 位值与 32 位乘法相交。这可能非常适合您正在寻找的高效 SIMD 解决方案,例如 SSE。
标签: c binary bit-manipulation sse