用于数字比较的按位运算?

问题描述

在过去的一天中,我为此花费了太多的大脑周期。

我正在尝试提出一组按位操作,它们可能会重新实现以下条件:

uint8_t a,b;
uint8_t c,d;
uint8_t e,f;
...

bool result = (a == 0xff || a == b) && (c == 0xff || c == d) && (e == 0xff || e == f);

我正在查看的代码具有以下四个表达式,&&短路在一起(如上所述)。

我知道这是一个深奥的问题,但是它的短路特性以及上述代码在紧密循环中的时间安排,使得缺乏可预见的时间成为皇家的痛苦,而且坦率地说,它似乎真的很烂分支预测不可用或实施得很好的体系结构。

有没有那么简明的野兽?

解决方法

因此,如果您真的想做些细微调整以使其“快速”(您真正应该只在对代码进行性能分析以确保这是一个瓶颈之后才这样做),则要做的是通过打包将其向量化所有的值加在一起变成一个更宽的词,因此您可以一次进行所有比较(一条指令),然后从几位中提取答案。

有一些技巧。要比较两个值是否相等,可以对它们进行异或(^)并测试结果是否为零。要测试一个较宽的单词的字段是否为零,您可以在上面加一个1位来“打包”它,然后减去一个,然后查看添加的额外位是否仍为1-如果现在为0,则该字段的值为零。

将所有这些放在一起,您想一次进行6个8位比较。您可以将这些值打包到一个64位字的9位字段中(使用9位可以得到额外的1个保护位,以进行减法测试)。您可以在64位int中最多容纳7个这样的9位字段,所以没问题

// pack 6 9-bit values into a word
#define VEC6x9(A,B,C,D,E,F)  (((uint64_t)(A) << 45) | ((uint64_t)(B) << 36) | ((uint64_t)(C) << 27) | ((uint64_t)(D) << 18) | ((uint64_t)(E) << 9) | (uint64_t)(F))

// the two values to compare
uint64_t v1 = VEC6x9(a,a,c,e,e);
uint64_t v2 = VEC6x9(b,0xff,d,f,0xff);
uint64_t guard_bits = VEC6x9(0x100,0x100,0x100);
uint64_t ones = VEC6x9(1,1,1);
uint64_t alt_guard_bits = VEC6x9(0,0x100);

// do the comparisons in parallel
uint64_t res_vec = ((v1 ^ v2) | guard_bits) - ones;

// mask off the bits we'll ignore (optional for clarity,not needed for correctness)
res_vec &= ~guard_bits;

// do the 3 OR ops in parallel
res_vec &= res_vec >> 9;

// get the result
bool result = (res_vec & alt_guard_bits) == 0;

最后的OR和AND是“向后”的,因为如果比较为true(值相等),则每个比较的结果位为0;如果比较(false)(值不相等),则结果位为1。

如果您正在编写编译器-上面的所有内容都是最感兴趣的-最终实现向量比较的方式-并且向量化编译器很可能会自动为您完成所有操作。

如果您可以安排将初始值预先打包到向量中,则效率会更高。这反过来可能会影响您对数据结构和允许值的选择-如果您将值安排为7位或15位(而不是8位),则在添加保护位时它们可能会打包得更好。

,

您可以修改存储和解释数据的方式:

a为0xFF时,是否需要b的值。如果不是,则使b等于0xFF并通过删除测试0xFF的部分来简化表达式。

此外,您可以将abc合并到一个变量中。

uint32_t abc;
uint32_t def;

bool result = abc == def;

其他操作可能会更慢,但该循环应更快(单个比较而不是最多6个比较)。

您可能希望使用联合来单独或成组访问字节。在这种情况下,请确保第四个字节始终为0。

,

要使用&&,||消除时序变化,请使用&,|@molbdnilo。可能更快,也许不是。当然更容易并行化。

// bool result = (a == 0xff || a == b) && (c == 0xff || c == d) 
//     && (e == 0xff || e == f);
bool result = ((a == 0xff) | (a == b)) & ((c == 0xff) | (c == d))
    & ((e == 0xff) | (e == f));