【问题标题】:What is the fastest way to check for oddness & perform divisions by powers of 2检查奇数和执行除法的最快方法是 2
【发布时间】:2016-01-09 08:56:37
【问题描述】:

出于各种原因,通常需要检查一个完整数字的奇数,一个这样的例子是数据网格,其中每条奇数行都是不同的颜色以提高可读性。程序员经常使用公式来实现这样的功能

odd = n mod 2 != 0

或者反过来:

even = n mod 2 == 0

但是这种方法很慢,因为它需要 FPU 计算除法的余数。

我还看到开发人员编写了以下代码:

x = n / 2
odd = x mod 2

有没有更好的方法来做到这一点?

【问题讨论】:

  • 对于许多/大多数编译器来说,您的前提是错误的。
  • @JamesKPolk - 事实上,这就是为什么答案指出这不仅适用于编译语言,而且许多编译器已经为此进行了优化。
  • 你好像同时写了答案和问题。
  • @JamesKPolk - 正确,这是 StackOverflow 的一个特性,值得鼓励。
  • @Geoffrey 你的意思是说'鼓励同时发布一个问题和一个问题的答案' 还是仅仅鼓励自己回答问题?

标签: bit-manipulation division bit-shift modulo


【解决方案1】:

这个问题的答案相当简单,但经常被忽视,请注意,这仅适用于整数,不适用于浮点数。

要让 x86 CPU 上的 FPU 执行除法/模数计算,单精度需要至少 17 个时钟周期,双精度需要至少 34 个时钟周期。

因此我们可以很好地了解上述代码中更复杂的示例需要多少个周期(最好的情况是不计算 CPU 加载/存储操作)。

x = n / 2      17 cycles
odd = x mod 2  17 cycles
               ---------
               34 cycles

因为计算机用二进制表示整数,我们可以通过检查第一位是否设置来简单地检查一个数字是否为奇数,这适用于任何支持位运算的语言。

例如:

Psuedo Code: odd = n and 1
C/C++/Java : odd = n & 1;

掩码操作仅需要 1 个周期,根据 FPU 模式,每个除法/模数操作的性能提高 17 倍至 34 倍

对于第二个例子中的除以2,可以使用位操作来执行,实际上,任何2的幂除都可以使用位移右操作来执行。例如:

Psuedo Code: x = n shr 1
C/C++/Java : x = n >> 1;

这会将n 中的所有位右移一位,从而执行除以 2,如果它右移 2,则将除以 4。这可以反过来乘以幂两个,但通常不以这种方式使用,因为乘法运算同样快。

如果我们将这两种技术结合起来,我们可以将第二个示例更改为:

Psuedo Code:
  x = n shr 1
  odd = x and 1

C/C++/Java:
  x = n >> 1;
  odd = x & 1;

这可以进一步简化为

Psuedo Code: odd = (n shr 1) and 1
C/C++/Java : odd = (n >> 1) & 1;

与使用 FPU 的版本相比,此版本将总共只使用两个时钟周期来执行数学运算,这是 1700% 的性能提升

许多编译器都知道这些技巧,并且会在适合您的情况下以这种方式编译代码,但许多脚本语言并不那么聪明,而且并非所有编译器都是平等的。

例如,编译器可能不会接受以下示例用法:

int doMath(int x, int y) {
  return x / y;
}

doMath(10, 2);
doMath(20, 4);  
doMath(30, 8);

但如果它是使用幂来编写的,它总是会更快,即:

int doMath(int x, int y) {
  return x >> y;
}

doMath(10, 1);
doMath(20, 2);
doMath(30, 3);

如果您学习并利用这些技巧,您将编写出在任何地方都能正常运行的高效代码,而无需猜测编译器是否会为您创建良好的输出,尤其是当您像往常一样在 Microsoft/Intel 和 GCC 之间交叉编译代码时在平台无关的项目中。

【讨论】:

  • 如果您只想测试整数值的奇数/偶数,我不确定您为什么要谈论浮点运算和 FPU?
  • @PaulR 因为我经常看到的示例代码 (i % 2) 使用 FPU,只需检查最低位即可避免。
  • 这似乎不太可能 - 为什么任何编译器都会为整数除法生成 FPU 指令?见神螺栓:goo.gl/s2ctRD
  • PaulR 重读答案...“许多脚本语言并不那么聪明,而且并非所有编译器都是平等的。”
  • 好吧——那为什么还要提到 C/C++/Java 和 gcc 呢?
猜你喜欢
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 2018-04-24
  • 1970-01-01
相关资源
最近更新 更多