【问题标题】:Integer powers in CC中的整数幂
【发布时间】:2017-09-04 15:20:01
【问题描述】:

在 C 代码中很常见

a = b*b;

而不是

a = pow(b, 2.0);

用于double 变量。我明白了,因为pow 是一个能够处理非整数指数的通用函数,人们应该天真地认为第一个版本更快。但是,我想知道编译器 (gcc) 是否会将对 pow 的调用转换为带有整数指数的直接乘法,作为任何可选优化的一部分。

假设不进行这种优化,那么手动写出乘法的最大整数指数是多少,例如b*b* ... *b?

我知道我可以在给定的机器上进行性能测试以确定我是否应该关心,但我想更深入地了解什么是“正确的事情”。

【问题讨论】:

  • 架构相关。
  • pow()(我假设是您的意思的函数)不仅仅是一个能够处理非整数指数的函数;它是一个接受double 类型参数并返回double 的函数。这有点微妙,但参数和返回值的类型与它们可能采用的值一样重要。
  • 编译器不会进行这种转换,因为power 处理浮点值。写出b*b ... *b 来计算整数幂几乎总是更快。
  • 这很可能是this question 的副本,除了您明确询问整数,而该问题的大多数答案都假设浮点数。不过答案是一样的:手动写出乘法会更快,因为库函数必须足够通用以处理所有可能的情况。这对于整数和浮点值都是正确的,但对于整数尤其是正确,因为 FP 转换非常慢。
  • 有趣的是,x86-64 上的 gcc 和 clang 似乎将 pow(b, 2.0) 转换为 b * b,但 b 仍被视为双精度值 (godbolt reference)。查看mulsd %xmm0, %xmm0 指令。将 2.0 替换为 3.0 时似乎没有发生相同的优化。

标签: c gcc optimization gnu compiler-optimization


【解决方案1】:

你想要的是-ffinite-math-only -ffast-math 和可能的#include <tgmath.h> 这与-Ofast 相同,但没有强制-O3 优化。

它不仅在启用-ffinite-math-only and -ffast-math 时有助于这些类型的优化,而且当您忘记将正确的后缀附加到(非双精度)数学函数时,类型通用数学也有助于弥补。

例如:

#include <tgmath.h>
float pow4(float f){return pow(f,4.0f);}
//compiles to
pow4:
    vmulss  xmm0, xmm0, xmm0
    vmulss  xmm0, xmm0, xmm0
    ret

对于 clang,这适用于高达 32 的幂,而 gcc 则适用于至少 2,147,483,647 的幂(据我检查),除非启用了-Os(因为从技术上讲,pow 函数的jmp 是更小) - 使用 -Os,它只会做 2 的幂。

警告-ffast-math 只是其他几个优化的方便别名,其中许多优化违反了各种标准。如果您宁愿只使用最少的标志来获得这种所需的行为,那么您可以使用-fno-math-errno -funsafe-math-optimizations -ffinite-math-only

【讨论】:

    【解决方案2】:

    就正确的事情而言 - 考虑您的维护者,而不仅仅是性能。我有一种预感,您正在寻找一般规则。如果您正在做一个简单且一致的数字平方或立方体,我不会使用 pow 来处理这些。 pow 很可能会进行某种形式的子例程调用,而不是执行寄存器操作(这就是 Martin 指出架构依赖性的原因)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-30
      • 2013-07-17
      • 2013-05-02
      • 2013-06-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 2016-06-10
      相关资源
      最近更新 更多