考虑二进制序列:
11000111
我必须找到这个系列的总和(实际上并行)
Sum = 1 + 1 + 0 + 0 + 0 + 1 + 1 + 1 = 5
这是浪费资源,为什么要投入时间来增加0?
有没有巧妙的方法来总结这个序列,以便我可以避免不必要的添加?
答案 0 :(得分:3)
在字节级而不是位级操作。 Use a small LUT to convert a byte to a population count。这样你只需要进行一次查找,每8位进行一次添加。除非您的数据可能非常稀疏,否则这应该非常有效。
答案 1 :(得分:0)
这取决于你如何存储你的bitset。 如果它是一个数组,那么你不能做一个普通的。如果要并行执行此操作,只需将数组拆分并同时处理它们。
如果我们讨论的是bitset(以本机(32/64位)整数类型存储这些位),那么计算位的最简单方法就是这样:
int bitset;
int s = 0;
for (; bitset; s++)
bitset &= bitset-1;
这会删除每一步的最后一位,所以你有O(s)。
当然,如果需要超过32/64位,可以将这两种方法结合使用
答案 2 :(得分:0)
我不知道为什么人们会回答,甚至没有考虑从第一条评论到问题的链接。您可以轻松地在O(size_of_bitset)
下进行制作。至关重要的是,它涉及到恒定因素。
您可以使用此方法(由J.F.Sebastian在link中找到):
inline int count_bits(int num){
int sum = 0;
for (; bitset; sum++) bitset &= bitset-1;
return sum;
}
int main (void){
int array[N];
int total_sum = 0;
#pragma omp parallel for reduction(+:total_sum)
for (size_t i = 0; i < N, i++){
total_sum += count_bits(array[i]);
}
}
这将统计array
内存范围内的位数。内联对于避免不必要的复制很重要,编译器也应该更好地优化它。
您可以将count_bits
更换为更好的计算整数位的内容,以便在找到任何内容时更快。此版本的复杂度为O(bits_set)
(不是位集的大小!)。
调用并行构造会引入相当多的开销,而单个求和则需要相当大的补偿。
并行性是通过OpenMP完成的。每个线程的部分和在并行循环的末尾求和并存储在total_sum
中。请注意,由于reduction子句,total_sum
在每个线程reduction
的循环内都是私有的。
您可以更改代码以使其计数在任意内存区域中设置的位,但是当您在如此低的级别执行操作时,它与内存对齐非常重要。
答案 3 :(得分:-1)
将其视为字符串。您必须至少读取一次输入符号,因此您至少需要O(n)时间。
添加速度很快,只需一个周期。使用某种条件逻辑意味着不必要的分支 - 但如今编译器可能会优化它。
如果需要并行性,则拆分输入字符串并同时处理它们。这是一个令人尴尬的并行问题。
答案 4 :(得分:-1)
据我所知,尝试特别处理零会很浪费。正如@bdares所说,加入真的很便宜。至少,你需要执行N条指令来总结一个N位序列,如果你无条件地总和那么就是这样。如果添加测试以查看该位是0还是1,那么这是需要为每个位执行的另一条指令。即使没有分支惩罚,你每个位执行最小1条指令(条件测试),然后你也为任何等于1的位执行原始指令(add)。所以即使没有分支惩罚,这需要更多的时间来执行。
@bdares提到编译器会优化分支,但只有在编译时知道每个位的值,并且如果在编译时知道位的值,你应该自己添加它们提前。
可能有一些可爱的东西,你可以做点苦恼。例如,如果你一次取两个比特,你就要加上0,1,2或3的值,并且只有一半的加法要做。 可以通过某些东西然后你可以用结果将它转换成你想要的值,但我实际上并没有想过如何做到这一点。