【问题标题】:What could be the benefit of such a complicated function to test if variable is not zero?如此复杂的函数来测试变量是否不为零有什么好处?
【发布时间】:2020-08-25 20:55:44
【问题描述】:

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

函数测试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
}

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

static inline uint8_t gf16_is_nonzero(uint8_t a) {
    return (a & 15) != 0;
}

编辑

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

TonyDelroy 在 cmets 中建议了第二个代码 sn-p 的更简单方法。

【问题讨论】:

  • 很好奇。为什么不同:& 0xf 和后来使用 & 15
  • 是一样的。十六进制的 F 是十进制的 15。后来我只用了15,因为它更明显。
  • 最明显的是0b1111;然后0xF15 是最不明显的位掩码。
  • 我认为这取决于,当您使用 GF16 时,它可能非常明显。但整件事不是重点。

标签: galois-field c performance bit-manipulation galois-field post-quantum-cryptography


【解决方案1】:

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

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

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

andl    $15, %edi
negl    %edi
shrl    $31, %edi
movl    %edi, %eax
ret

虽然第二个函数编译成这样:

andl    $15, %edi
setne   %al
ret

故事的寓意:编写清楚说明您的意图的代码,让编译器找出其余部分。

【讨论】:

  • 附加说明:编译器可能无论如何都会生成无分支代码,并且此代码可能会更慢/更长。
  • 既然是加密代码,恐怕连可能都有分支。 “只依赖编译器”将是加密货币中的一个有趣的笑话。
  • @TonyDelroy 编译a4 != 0 的天真方法是使用比较和分支。我们碰巧得到setcc 是幸运的编译器优化的结果。
  • @dbush:比较并不昂贵 - 请参阅 agner.org/optimize/instruction_tables.pdf - 他们将标志设置为副作用,然后可以使用类似 setne 的东西而无需任何跳转,但这一切都取决于 CPU 指令放。尽管如此,除了x & 15 == 0 之外,没有任何理由做任何事情,而且我无法想象问题中复杂的代码是为了避免分支而编写的。奇怪的代码对我来说似乎是 FUD 驱动的。可能是由硬件工程师或其他人写的。
  • 嗯,很容易说比较不是一个分支,它都是 FUD,而是一个比较may result in a branch。它也可能不会,因为在优化之后往往会发生。无论哪种方式都无法保证。
猜你喜欢
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多