A、B和C是某种无符号整型变量。从概念上讲,A是一个测试向量,B是“必需”位的掩码(至少一个对应的位在A中必须设置),而C是“禁止”位的掩码(A中不能设置任何对应的位)。由于我们同时使用位运算和逻辑运算符,因此通常看来合理的解决方案为
A & B & ~C
是不正确的。相反,标题表达式等同于伪代码
((a0 & b0) | ... | (an & bn)) & (~(a0 & c0) & ... & ~(an & cn))
a0
等代表单独的位(n
是最高位的索引)。我不知道如何有效地重新排列并提取相应的代码,但还是有没有巧妙的方法,也许用^
来简化标题中的表达式?
编辑:受@huseyintugrulbuyukisik的问题激发,我注意到我们可以假设(B&C)==0
,但我不知道这是否有帮助。
编辑2:结果:这取决于分支预测的好坏!
#include <chrono>
#include <cmath>
#include <iostream>
#include <vector>
using UINT = unsigned int;
int main(void)
{
const auto one = UINT(1);
const UINT B = (one << 9); // Version 1
// const UINT B = (one << 31) - 1; // Version 2
const UINT C = (one << 5) | (one << 15) | (one << 25);
const size_t N = 1024 * 1024;
std::vector<UINT> vecA(N);
for (size_t i = 0; i < N; ++i)
vecA[i] = (UINT)rand();
int ct = 0; //To avoid compiler optimizations
auto tstart = std::chrono::steady_clock::now();
for (size_t i = 0; i < N; ++i)
{
const UINT A = vecA[i];
if ((A & B) && !(A & C))
++ct;
}
auto tend = std::chrono::steady_clock::now();
auto tdur = std::chrono::duration_cast<std::chrono::milliseconds>(tend - tstart).count();
std::cout << ct << ", " << tdur << "ms" << std::endl;
ct = 0;
tstart = std::chrono::steady_clock::now();
for (size_t i = 0; i < N; ++i)
{
const UINT A = vecA[i];
if (!((!(A & B)) | (A & C)))
++ct;
}
tend = std::chrono::steady_clock::now();
tdur = std::chrono::duration_cast<std::chrono::milliseconds>(tend - tstart).count();
std::cout << ct << ", " << tdur << "ms" << std::endl;
return 0;
}
版本 1:
$ ./ops_test
65578, 8ms
65578, 3ms
版本 2:
$ ./ops_test
130967, 4ms
130967, 4ms
这些是代表性的数值(实际上我运行了多次每个测试)。g++ 4.8.4,默认优化。我只在B
中设置了4位,就得到类似版本2的结果。然而,我的使用情况仍更接近版本1,所以我认为@DougCurrie的答案是一个改进。
&&
,因为它通常涉及到分支。但是,像往常一样,在进行优化之前要先进行测量,如果你的“每个周期都很重要”的部分实际上受到 - 比如 - 从RAM中获取未在缓存中的数据的限制,那么担心这种问题可能完全没有意义。 - Matteo Italia