这样一个复杂的函数来测试变量是否不为零有什么好处?

lin*_*erd 4 c performance bit-manipulation galois-field post-quantum-cryptography

我正在撰写关于为后量子安全签名编写的代码的硕士论文(计算机科学)。整个事情都可以在这里找到但在这里并不重要。在我的论文中,我试图解释一个“简单”的函数,它根本不是那么简单。

该函数测试,如果一个变量在GF(16) 中是非零的。(这里的GF(16)可以理解为4位无符号整数)。该函数如下所示:

static inline uint8_t gf16_is_nonzero(uint8_t a) {
    unsigned a4 = a & 0xf; // mask lowest 4 bits of a
    unsigned r = 0u - a4;  // set 4 high bits if a is nonzero
    r >>= 4;               // right-shift high bits into low bits
    return r & 1;          // return lowest bit
}
Run Code Online (Sandbox Code Playgroud)

我明白它是如何工作的,但我不明白为什么这个功能需要这么复杂。这有什么好的理由吗?很好的理由可能是性能或安全性(例如针对定时攻击的安全性)的好处。因为如果没有这样的好处,那么以简单的方式编写该函数不是更聪明,例如:

static inline uint8_t gf16_is_nonzero(uint8_t a) {
    return (a & 15) != 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑

这段代码不是我写的,而是由加密研究人员编写的,他们试图让 NIST 标准化他们的 PQ 算法。

TonyDelroy 在评论中为第二个代码片段建议了一种更简单的方法。

dbu*_*ush 8

这段代码的原因是因为它是无分支的

测试条件往往是一项昂贵的操作,而加法、减法和按位运算符则不然。

然而,这是过早的优化。使用-O3,第一个函数编译为:

andl    $15, %edi
negl    %edi
shrl    $31, %edi
movl    %edi, %eax
ret
Run Code Online (Sandbox Code Playgroud)

虽然第二个函数编译为:

andl    $15, %edi
setne   %al
ret
Run Code Online (Sandbox Code Playgroud)

这个故事的寓意是:编写清楚地说明您的意图的代码,让编译器解决其余的问题。

  • 由于它是加密代码,因此可能甚至*可能*都不允许有分支。“只依赖编译器”将是加密货币中的一个有趣的笑话。 (4认同)
  • 附加说明:无论如何,编译器可能会生成无分支代码,并且该代码可能会更慢/更长。 (3认同)
  • @dbush:比较并不昂贵 - 请参阅 https://www.agner.org/optimize/instruction_tables.pdf - 他们将标志设置为副作用,然后可以使用类似“setne”的东西而无需任何跳转,尽管这一切都取决于在CPU指令集上。尽管如此,除了“x & 15 == 0”之外,没有理由做任何事情,而且我无法想象问题中的复杂代码是为了避免分支而编写的。对我来说,这些奇怪的代码似乎是由 FUD 驱动的。也许是由硬件工程师或其他对此深思熟虑的人编写的。 (3认同)
  • 很容易说比较不是分支并且都是 FUD,但是比较[可能会导致分支](https://godbolt.org/z/Kvodnz)。也可能不会,优化后往往会发生这种情况。无论哪种方式都没有保证。 (2认同)