c++ - 为什么在我的机器上 tanh 比 exp 快?

标签 c++

这个问题产生于 separate question ,结果证明它有一些明显的机器特定的怪癖。当我运行下面列出的 C++ 代码来记录 tanhexp 之间的时间差异时,我看到以下结果:

tanh: 5.22203
exp: 14.9393

tanh 的运行速度大约是 exp 的 3 倍。考虑到 tanh 的数学定义(并且不知道实现的算法定义),这有点令人惊讶。

此外,这发生在我的笔记本电脑(Ubuntu 16.04,英特尔酷睿 i7-3517U CPU @ 1.90GHz × 4)上,但不会发生在我的台式机上(相同的操作系统,现在不确定 CPU 规范)。

我用 g++ 编译了下面的代码。上述时间没有编译器优化,但如果我对每个 n 使用 -On,趋势仍然存在。我还摆弄了 ab 值,以查看正在评估的值范围是否有影响。这似乎并不重要。

什么会导致 tanh 在不同的机器上比 exp 更快?

#include <iostream>
#include <cmath>
#include <ctime>

using namespace std;

int main() {
    double a = -5;
    double b =  5;
    int N =  10001;
    double x[10001];
    double y[10001];
    double h = (b-a) / (N-1);

    clock_t begin, end;

    for(int i=0; i < N; i++)
        x[i] = a + i*h;

    begin = clock();

    for(int i=0; i < N; i++)
        for(int j=0; j < N; j++)
            y[i] = tanh(x[i]);

    end = clock();

    cout << "tanh: " << double(end - begin) / CLOCKS_PER_SEC << "\n";

    begin = clock();

    for(int i=0; i < N; i++)
        for(int j=0; j < N; j++)
            y[i] = exp(x[i]);

    end = clock();

    cout << "exp: " << double(end - begin) / CLOCKS_PER_SEC << "\n";


    return 0;
}

编辑:一些汇编输出

This is output当我使用 g++ -g -O -Wa,-aslh nothing2.cpp > stuff.txt 编译以下简化代码时。

#include <cmath>

int main() {
    double x = 0.0;
    double y,z;
    y = tanh(x);
    z = exp(x);
    return 0;
}

编辑:另一个更新

假设 nothing2.cpp 包含先前编辑中的简化代码。我跑:

g++ -o nothing2.so -shared -fPIC nothing2.cpp
objdump -d nothing2.so > stuff.txt

Here is the contents of stuff.txt

最佳答案

有多种可能的解释,适用于您的情况的解释取决于您使用的平台或具体使用的数学库。但一种可能的解释是:

首先 tanh 的计算并不依赖于 tanh 的标准定义,而是用 exp(-2*x) 来表示expm1(2*x) 这意味着只需要计算一个指数,这可能是繁重的操作(此外还有除法和一些加法)。

第二个可能是诀窍是,对于较大的 x 值,这将减少到 (exp(2*x)-1)/(exp(2*x)+1 ) = 1 - 2/(expm1(2*x)+2)。这里的优点是,由于第二项很小,因此不必计算到相同的相对精度即可获得相同的最终精度。这转化为一般情况下不需要 expm1 的。

对于 x 的小值也有类似的技巧,可以将其重写为 (1-exp(-2*x))/(1+exp(-2*x)) = - 1/(1 + 2/(expm1(-2*x)+2) 这再次意味着我们可以利用因子 exp(-2*x)大而且不必以相同的精度计算它。但是你不必以这种方式实际计算它,你可以使用表达式 expm1(-2*x)/(2+expm1(-2*x) ) 而不是对 expm1 具有相同的精度要求。

此外,还有其他优化可用于更大的 x 值,这对于基本相同来源的 exp 是不可能的。对于较大的 x,因子 expm1(2*x) 将变得如此之大,以至于我们可以简单地完全丢弃它,而对于 exp,我们仍然有计算它(对于大的负值 x 也是如此)。对于这些值,tanh 将立即确定为 1,而必须计算 exp

关于c++ - 为什么在我的机器上 tanh 比 exp 快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43045114/

相关文章:

c++ - 更改命名空间中的变量

c++ - const 对象作为函数参数

c++ - 循环范围和语法

c++ - 多线程,OpenMP,C

C++17 根据文件路径自动创建目录

c++ - std::shared_ptr 的 vector 未释放内存

c++ - R: C 符号不在加载表中

C++变量重载歧义

c++ - 如何使用C++根据当前时间自动生成新的CSV文件

python - C++/嵌入式 Python : Can I retrieve Python file name and line number when a C++ function is called from Python