实施方面的巨大差异?

时间:2015-08-05 20:02:57

标签: c++ performance boost time-measurement

我正在为发行版编写一些功能,并使用正态分布在我的实现和C ++ Boost之间运行测试。

给出概率密度函数(pdf:http://www.mathworks.com/help/stats/normpdf.html

我写的是这样的:

double NormalDistribution1D::prob(double x) {
    return (1 / (sigma * (std::sqrt(boost::math::constants::pi<double>()*2))))*std::exp((-1 / 2)*(((x - mu) / sigma)*((x - mu) / sigma)));
}

将我的结果与C ++ Boost的完成方式进行比较:

    boost::math::normal_distribution <> d(mu, sigma);
    return boost::math::pdf(d, x);

我并不是非常惊讶 - 我的版本花了 44278 纳秒,提升 只有 326

所以我玩了一下,在我的NormalDistribution1D-Class中编写了方法probboost并比较了所有三个:

void MATTest::runNormalDistribution1DTest1() {
    double mu = 0;
    double sigma = 1;
    double x = 0;

    std::chrono::high_resolution_clock::time_point tn_start = std::chrono::high_resolution_clock::now();
    NormalDistribution1D *n = new NormalDistribution1D(mu, sigma);
    double nres = n->prob(x);
    std::chrono::high_resolution_clock::time_point tn_end = std::chrono::high_resolution_clock::now();

    std::chrono::high_resolution_clock::time_point tdn_start = std::chrono::high_resolution_clock::now();
    NormalDistribution1D *n1 = new NormalDistribution1D(mu, sigma);
    double nres1 = n1->probboost(x);
    std::chrono::high_resolution_clock::time_point tdn_end = std::chrono::high_resolution_clock::now();

    std::chrono::high_resolution_clock::time_point td_start = std::chrono::high_resolution_clock::now();
    boost::math::normal_distribution <> d(mu, sigma);
    double dres = boost::math::pdf(d, x);
    std::chrono::high_resolution_clock::time_point td_end = std::chrono::high_resolution_clock::now();

    std::cout << "Mu : " << mu << "; Sigma: " << sigma << "; x" << x << std::endl;
    if (nres == dres) {
        std::cout << "Result" << nres << std::endl;
    } else {
        std::cout << "\033[1;31mRes incorrect: " << nres << "; Correct: " << dres << "\033[0m" << std::endl;
    }


    auto duration_n = std::chrono::duration_cast<std::chrono::nanoseconds>(tn_end - tn_start).count();
    auto duration_d = std::chrono::duration_cast<std::chrono::nanoseconds>(td_end - td_start).count();
    auto duration_dn = std::chrono::duration_cast<std::chrono::nanoseconds>(tdn_end - tdn_start).count();

    std::cout << "own boost: " << duration_dn << std::endl;
    if (duration_n < duration_d) {
        std::cout << "Boost: " << (duration_d) << "; own implementation: " << duration_n << std::endl;
    } else {
        std::cout << "\033[1;31mBoost faster: " << (duration_d) << "; than own implementation: " << duration_n << "\033[0m" << std::endl;
    }
}

结果是(正在编译并运行检查 - 方法3次)

  

自己的提升:1082提升得更快:326;比自己的实施:44278

     

自己提升:774提升得更快:216;比自己的实施:34291

     

自己提升:769提升速度:230;比自己的实施:33456

现在这让我很困惑: 怎么可能该类的方法比直接调用的语句长3倍?

我的编译选项:

g++ -O2   -c -g -std=c++11 -MMD -MP -MF "build/Debug/GNU-Linux-x86/main.o.d" -o build/Debug/GNU-Linux-x86/main.o main.cpp

g++ -O2    -o ***Classes***

2 个答案:

答案 0 :(得分:3)

首先,您使用new动态分配对象:

NormalDistribution1D *n = new NormalDistribution1D(mu, sigma);
double nres = n->prob(x);

如果你做的就像你已经完成了提升一样,那就足以拥有相同(或相当)的速度:

NormalDistribution1D n(mu, sigma);
double nres = n.prob(x);

现在,我不知道你在NormalDistribution1D::prob()中拼写你的表达方式的意义是否重要,但我怀疑以更“优化”的方式编写它会有什么不同,因为算术这样的表达式是编译器可以非常好地优化的东西。如果您使用--ffast-math开关,它可能会更快,这将为编译器提供更多的优化自由。

此外,如果double NormalDistribution1D::prob(double x)的定义在另一个编译单元(另一个.cpp文件)中,编译器将无法内联它,这也会产生明显的开销(可能慢两倍或更少) 。在boost中,几乎是在标题内部实现,所以当编译器看起来合适时,内联总会发生。如果您使用gcc的-flto开关进行编译和链接,则可以解决此问题。

答案 1 :(得分:2)

您没有使用-ffast-math选项进行编译。这意味着编译器不能(事实上,不能!)将(-1 / 2)*(((x - mu) / sigma)*((x - mu) / sigma))简化为类似于boost::math::pdf中使用的形式,

expo = (x - mu) / sigma
expo *= -x
expo /= 2
result = std::exp(expo)
result /= sigma * std::sqrt(2 * boost::math::constants::pi<double>())

以上强制编译器执行快速(但可能不安全/不准确)的计算,而无需使用-ffast_math

其次,与从堆(new)与堆栈(局部变量)分配所需的时间相比,上述代码与代码之间的时间差异最小。您正在计算分配动态内存的成本。