我有一个输入 uint64_tX
和N
要写入目标的最低有效位的数量Y
,Z
uint64_t 值从. 不受影响的部分,不应更改。我如何在 C++ 中为最新的英特尔 CPU 有效地实现它?M
Z
Y
Z
在循环中执行应该是有效的。我猜它不需要分支:使用的指令的数量应该是恒定的并且尽可能的小。
M
并且N
在编译时不固定。M 可以取 0 到 63 之间的任何值(Z 中的目标偏移量),N 的范围是 0 到 64(要复制的位数)。
插图:
我有一个输入 uint64_tX
和N
要写入目标的最低有效位的数量Y
,Z
uint64_t 值从. 不受影响的部分,不应更改。我如何在 C++ 中为最新的英特尔 CPU 有效地实现它?M
Z
Y
Z
在循环中执行应该是有效的。我猜它不需要分支:使用的指令的数量应该是恒定的并且尽可能的小。
M
并且N
在编译时不固定。M 可以取 0 到 63 之间的任何值(Z 中的目标偏移量),N 的范围是 0 到 64(要复制的位数)。
插图:
在合理的现代 IA 处理器上至少有四个指令序列可用。
X &= (1 << (N+1)) - 1; // mask off the upper bits
// bzhi rax, rdi, rdx
Z = X << M;
// shlx rax, rax, rsi
Y = X >> (64 - M);
// neg sil
// shrx rax, rax, rsi
值 M=0 会引起一些痛苦,因为在这种情况下 Y 需要为零,并且表达式N >> (64-M)
也需要清理。
克服这一问题的一种可能性是
x = bzhi(x, n);
y = rol(x,m);
y = bzhi(y, m); // y &= ~(~0ull << m);
z = shlx(x, m); // z = x << m;
由于 OP 实际上想要更新这些位,一个明显的解决方案是复制掩码的逻辑:
xm = bzhi(~0ull, n);
ym = rol(xm, m);
ym = bzhi(ym, m);
zm = shlx(xm, m);
但是,clang 似乎在应用了掩码的情况下总共产生了 24 条指令:
Y = (Y & ~xm) | y; // |,+,^ all possible
Z = (Z & ~zm) | z;
改变方法可能会更好:
x2 = x << (64-N); // align xm to left
y2 = y >> y_shift; // align y to right
y = shld(y2,x2, y_shift); // y fixed
这里y_shift = max(0, M+N-64)
固定 Z 稍微复杂一些,因为 Z 可以由三个部分组合而成:
zzzzz.....zzzzXXXXXXXzzzzzz, where m=6, n=7
如上所述,这应该可以通过两次双班制来实现。