为什么tanh在我的机器上比exp更快?

时间:2017-03-27 11:26:24

标签: 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规格)。 / p>

我用g++编译了下面的代码。上述时间没有编译器优化,但如果我对每个-On使用n,趋势仍然存在。我还调整了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;
}

编辑:一些汇编输出

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

This is output

#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

1 个答案:

答案 0 :(得分:2)

有各种可能的解释,适用于您的情况取决于您正在使用的平台或确切使用的数学库。但一种可能的解释是:

首先,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}}。

同样对于(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我们仍然必须计算它(对于大的负{{ 1}})。对于这些值,tanh将立即决定1,而exp必须计算。