c++ - std::pow 不同指数的行为非常不同

标签 c++ performance numerical-methods

我目前正在尝试优化一些代码,其中 50% 的时间花费在 std::pow() 上。我知道指数将始终 为正整数,而底数将始终为区间 (0, 1) 中的 double 。为了好玩,我写了一个函数:

inline double int_pow(double base, int exponent)
{
    double out = 1.0;
    for(int i = 0; i < exponent; i++)
    {
        out *= base;
    }

    return out;
}

我正在编译:

> g++ fast-pow.cpp -O3 --std=c++11

我在 (0, 1) 之间生成了 1 亿个 double ,并比较了 (1) std::pow (2) 我自制的 int_pow 函数的时间以及(3)直接乘法。这是我的计时程序的草图(这是一个非常快速的组合测试):

void time_me(int exp, size_t reps)
{
    volatile double foo = 0.0;
    double base = 0.0;

    size_t i;
    for (i = 0; i < reps; ++i)
    {
        base = ((double) rand() / (RAND_MAX)) + 1;
        foo = pow(base, exp);
        // foo = int_pow(base, exp);
        // foo = base * base * base;
    }

    // check that the loop made it to the end
    std::cout << foo << "  " << i <<  std::endl;
}

int main()
{
    std::clock_t start;

    start = std::clock();
    time_me(3, 1e8);
    std::cout << "Time: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << std::endl;

    return 0;
}

以下是我观察到的各种指数的时间:

  • 0: std::pow 0.71s, int_pow 0.77s
  • 2: std::pow 1.31s, int_pow 0.80s, direct mult 0.86s
  • 3: std::pow 6.9s (!!), int_pow 0.84s, 直接mult 0.76秒
  • 5: 类似于3:

我的问题

因此,我的问题是:

  1. 为什么 std::pow 的性能对于大于 2 的幂似乎下降得如此严重?
  2. 如果提前知道基数或指数类型,是否存在更快的幂函数?
  3. 有什么我忽略的非常明显的东西吗?我即将通过直觉 std::pow 来处理已知整数指数的情况,并且不想错过一些完全微不足道的事情。

谢谢!!

最佳答案

std::pow() 是一个通用函数,旨在接受任何一对浮点值。它执行昂贵的计算,应该被认为是一个慢函数。然而,显然,很多人滥用它来求平方,因此 IBM Accurate Mathematical Library(由 glibc 使用)中的 pow() 的实现针对该特定情况进行了优化:

sysdeps/ieee754/dbl-64/e_pow.c :

double
__ieee754_pow (double x, double y)
{
  ...
  ...
  if (y == 1.0)
    return x;
  if (y == 2.0)
    return x * x;
  if (y == -1.0)
    return 1.0 / x;
  if (y == 0)
    return 1.0;

如您所见,指数值 0、1 和 -1 也经过特殊处理,但至少这些是数学上重要的特殊情况,而平方只是统计上重要的情况,否则不应该进行特殊处理). 编辑:指数值012-1 是只有那些允许使用(更快的)算术运算来表达 std::pow(x,n) 而不会损失任何准确性的。参见 this answer更多细节。因此 2 的指数值不仅仅是一个具有统计意义的案例。 结束编辑

如果您想要一个快速替代 std::pow() 的指数的非负整数值并且不关心轻微的精度损失,那么

  1. 对于足够小的指数值,请使用您的 int_pow() 实现;
  2. 否则,使用exponentiation by squaring approach .

必须通过仔细的基准测试找到用于在第一种方法和第二种方法之间进行选择的指数的边界值。

关于c++ - std::pow 不同指数的行为非常不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38060139/

相关文章:

android - Boost::Spirit 编译错误

c++ - 更好地理解 makefile - 在这种情况下如何生成 .o 文件

Java 比 C 快 2 倍以上(作为 C++ 的子集)

matlab - 如何加速包含 kronecker 产品的 for 循环?

c - 将 regula falsi 方法修改为割线方法

c++ - 有没有办法使用 Linux 命令行或 C++ 从 zone.tab 读取坐标信息

c++ - 获取 cv::Mat 的黑色像素列表

java - C++ native OpenCV 到 opencv4android 端口太慢?

Mysql Join 2表非常慢,不添加其他索引

python - 查找与给定向量相似的所有向量的快速方法