澄清一下,这就是收集比特意味着:(在这个问题的背景下)
size_t gather_bits(size_t source, size_t mask) {
size_t result = 0, next_bit_index = 0;
for (size_t i = 0; i < sizeof(size_t) * 8; i++)
if ((mask >> i) & 1)
result |= ((source >> i) & 1) << next_bit_index++;
return result;
}
对于掩码中的每个第N个位,结果中的第N个位从源中设置,掩码中的第N个位的索引。 (result[mask_on_bit] = source[mask_bit_index]
)
我添加的代码段是最简单的实现,但遗憾的是,我找到的最快,我无法提供更好的功能。还有什么比这更快的吗?考虑mask
是完全随机的(因此在掩码中搜索大量的0应该不会有太大的好处)
答案 0 :(得分:3)
您可能需要考虑无分支解决方案,这通常可以在某些体系结构上提供显着的性能优势。像这样:
size_t gather_branchless( size_t source, size_t mask )
{
size_t result = 0, select = 1;
source &= mask;
while( source != 0 )
{
int used = (mask & 1);
result |= (source & select);
select <<= used;
source >>= !used;
mask >>= 1;
}
return result;
}
除了循环终止测试外,这完全避免了任何分支。我使用数百万随机生成的值运行此方法的基准来比较时间。在使用Clang和完全优化编译的英特尔酷睿i7 2.9GHz上运行:
+--------------+-------------+
| solution | approx time |
+--------------+-------------+
| txtechhelp | 1500 ms |
| yours | 1400 ms |
| SGeorgiades | 1300 ms |
| branchless | 600 ms |
+--------------+-------------+
精明的人可能会注意到,我的无分支版本会在没有剩余部分合并时提前终止。为了公平起见,我运行测试时始终为值和掩码设置最高位。这样做会在结果上再添加50毫秒。
所以你有它。无分支解决方案,至少在我测试过的英特尔架构上,运行速度是您的速度的两倍以上。另一个好处是,如果您想进一步优化大型数据集上的代码,它很容易转换为SIMD。
如果您想使用其他解决方案,可以see my benchmark online。请注意,它是用C ++编写的,而不是C.我的测试使用g++ -std=c++11 -O2
。这与包含使用gcc -O2
生成的目标函数的C对象文件链接。
答案 1 :(得分:1)
我认为这应该更快,因为它一次只能移位一位,而且个别计算(尤其是将该位掩盖为“结果”的计算)要简单得多。
size_t gather_bits(size_t source, size_t mask) {
size_t result = 0, next_bit_mask = 1;
while (value)
{
if (mask & 1)
{
if (source & 1)
result |= next_bit_mask;
next_bit_mask <<= 1;
}
mask >>= 1;
source >>= 1;
}
return result;
}
根据@ paddy的建议更新以终止值达到零时...好的通话!
答案 2 :(得分:1)
您的问题和发布的代码有点令人困惑。
在你的问题中,你要求掩码中第N位的每一个“,结果中的第N位是从掩码中第N位索引的源设置的。”我理解为mask
中的1
中的每个位都将result
中的位设置为source
中位1
的位在mask
中设置。
举一个简单的例子,如果source
是106
(二进制0110 1010
)而mask
是43
(二进制0010 1011
),结果应为0010 1010
(十进制42
);最简单,最快捷的方法是使用按位AND
,例如:
size_t gather_bits(size_t source, size_t mask)
{
return (source & mask);
}
但是你发布的代码并不能解决你在问题中提出的问题(除非我误解了你的问题)。相反,在您发布的代码中,如果Nth
中的mask
位置位,则会将Nth
位从source
推送到result
的LSB。< / p>
使用上面的示例,如果source
为106
且mask
为43
,则结果为0000 1110
。
如果 是你的意图,那么你可以使用已知值的静态地图(而不是在每次迭代中推送位),例如:
// assumes a 64-bit architecture
static size_t masks[] = {
1,2,4,8,16,32,64,128,256,512,1024,
2048,4096,8192,16384,32768,65536,131072,
262144,524288,1048576,2097152,4194304,
8388608,16777216,33554432,67108864,134217728,
268435456,536870912,1073741824,2147483648,
4294967296,8589934592,17179869184,34359738368,68719476736,
137438953472,274877906944,549755813888,1099511627776,
2199023255552,4398046511104,8796093022208,17592186044416,
35184372088832,70368744177664,140737488355328,281474976710656,
562949953421312,1125899906842624,2251799813685248,4503599627370496,
9007199254740992,18014398509481984,36028797018963968,72057594037927936,
144115188075855872,288230376151711744,576460752303423488,1152921504606846976,
2305843009213693952,4611686018427387904,0x8000000000000000
};
size_t gather_bits(size_t source, size_t mask)
{
size_t result = 0, next_bit_index = 0;
for (size_t i = 0; i < sizeof(size_t) * 8; ++i)
if (mask & masks[i])
result |= ((source & masks[i]) >> (i - next_bit_index++));
return result;
}
这减少了按位移位和分支,但如果实际更快将取决于很多事情,并且需要在您的环境中进行更多测试。
希望可以提供帮助。