将特定位置的位收集为一个新值
Gather bits at specific positions into a new value
我有一个大小为N个字符的位掩码,这是静态已知的(即可以在编译时计算,但它不是一个常量,所以我不能把它写下来),位设置为1表示"想要的"位。我有一个同样大小的值,只有在运行时才知道。我想从这个值中收集"想要的"位,按顺序,放到一个新值的开头。为简单起见,我们假设所需的位数为<= 32。
完全未优化的参考代码,希望有正确的行为:
template<int N, const char mask[N]>
unsigned gather_bits(const char* val)
{
unsigned result = 0;
char* result_p = (char*)&result;
int pos = 0;
for (int i = 0; i < N * CHAR_BIT; i++)
{
if (mask[i/CHAR_BIT] & (1 << (i % CHAR_BIT)))
{
if (val[i/CHAR_BIT] & (1 << (i % CHAR_BIT)))
{
if (pos < sizeof(unsigned) * CHAR_BIT)
{
result_p[pos/CHAR_BIT] |= 1 << (pos % CHAR_BIT);
}
else
{
abort();
}
}
pos += 1;
}
}
return result;
}
虽然我不确定这个公式是否真的允许在编译时访问掩码的内容。但是在任何情况下,它都是可用的,也许一个constexpr
函数会是一个更好的主意。我不是在这里寻找必要的c++魔法(我会弄清楚的),只是算法。
一个输入/输出示例,为了清晰起见,使用16位值和虚构的二进制表示法:
mask = 0b0011011100100110
val = 0b0101000101110011
--
wanted = 0b__01_001__1__01_ // retain only those bits which are set in the mask
result = 0b0000000001001101 // bring them to the front
^ gathered bits begin here
我的问题是:
这样做的最有效的方法是什么?(有什么硬件说明可以帮助吗?)
如果掩码和值都被限制为
unsigned
,所以一个单词,而不是一个无界的字符数组呢?那么,是否可以用一个固定的、简短的指令序列来完成?
将pext
(并行位提取),这正是你在英特尔Haswell中想要的。我不知道该指令的性能如何,但可能比其他指令更好。此操作也称为"右压缩"或简称为"压缩",Hacker’s Delight的实现如下:
unsigned compress(unsigned x, unsigned m) {
unsigned mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0's to right.
for (i = 0; i < 5; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
相关文章:
- C++使用另一个数组和新值初始化数组
- 变体 - 分配新值时是否清理旧值?
- C++:我可以在线程仍在运行时为线程提供新值,还是必须先结束它?
- C++类析构函数使用新值而不是实际值
- 写入新值是构成前增量表达式"value computation"的一部分,还是"side effect"?
- 我们是否可以保证任何原子写入都会立即将原子变量的新值存储在主存储器中?
- C++:初始化(新)一个不同初始大小的向量数组
- C++ - 读取进程内存到缓冲区,写入进程内存(同一缓冲区上的新值)将缓冲区恢复为旧值
- 新的一个一维阵列,非常大,例如60000*60000
- 将新值添加到链表中
- 如何在while循环之外分配一个新值
- 在为其分配一个新值之前,应将`nullptr`分配给`sTD :: simelod_ptr`
- 无限数组C++在一个表达式中使用两个新值调整数组大小
- 为什么reverse_iterator无法使用forward Itererator分配一个新值
- 在数组的元素中插入一个新值,然后移动其余元素
- 给QStringListModel的私有QStringList赋值一个新的QStringList
- c++函数返回右值,但可以为其赋一个新值
- 为什么可以给引用赋一个新值,以及如何使引用引用其他东西
- C++将两个变量中较大的一个设置为新值
- 将特定位置的位收集为一个新值