与libc相比,Gnu科学图书馆的性能不佳

时间:2018-06-27 11:14:48

标签: c++ gsl

我已经实现了一个简单的程序,用于通过gnu科学库和l​​ibc测量sin计算性能。这是源代码:

#include <iostream>
#include <vector>
#include <math.h>
#include <chrono>
#include <list>
#include "gsl/gsl_sf_trig.h"

int main()
{
    std::uint32_t numStepsToCalculate = 1000000;
    const double startX = 0.0;
    const double finishX = M_PI*2;
    const double stepX  = (finishX - startX)/numStepsToCalculate;

    double currentX = startX;
    std::list<double> res;

    auto startT = std::chrono::steady_clock::now();
    while ( currentX <= finishX ) {
        res.push_back( sin ( currentX ) );
        currentX += stepX;
    }
    auto endT = std::chrono::steady_clock::now();

    auto diffT = endT - startT;
    std::cout << "STD : " << std::chrono::duration <double, std::milli> (diffT).count() << " ms" << std::endl;
    std::cout << "STD res size " << res.size() << std::endl;

    std::list<double> resOpt;
    currentX = startX;
    auto startTopt = std::chrono::steady_clock::now();
    while ( currentX <= finishX ) {
        resOpt.push_back( gsl_sf_sin ( currentX ) );
        currentX += stepX;
    }

    auto endTopt = std::chrono::steady_clock::now();

    auto diffTopt = endTopt - startTopt;

    std::cout << "GSL : " << std::chrono::duration <double, std::milli> (diffTopt).count() << " ms" << std::endl;
    std::cout << "GSL res size " << resOpt.size() << std::endl;
    return 0;
}

这是结果: 性病:57.8869 ms GSL:106.787毫秒

那么GSL比libc慢还可以吗?

2 个答案:

答案 0 :(得分:1)

GSL使用软件sin(即使有可用的CPU sin),它也可以为您提供错误估计:gsl_sf_sin只是当前gsl_sf_sin_e的包装器(提供错误估计)。这个例程的速度没有优化太多。

另一方面,libc sin通常在速度方面进行了很好的优化。

此外,您的基准测试可能存在缺陷,因为如果启用了优化,编译器会优化sin

答案 1 :(得分:0)

很遗憾,我无法发表评论,因此将其作为单独的答案发布。 我认为@geza的答案是正确的,我只想补充一些细节。 根据GSL源代码,它们具有自己的三角函数实现:

/*-*-*-*-*-*-*-*-*-*-*-* Functions with Error Codes *-*-*-*-*-*-*-*-*-*-*-*/

/* I would have prefered just using the library sin() function.
 * But after some experimentation I decided that there was
 * no good way to understand the error; library sin() is just a black box.
 * So we have to roll our own.
 */
int
gsl_sf_sin_e(double x, gsl_sf_result * result)
{
/* CHECK_POINTER(result) */

{
const double P1 = 7.85398125648498535156e-1;
const double P2 = 3.77489470793079817668e-8;
const double P3 = 2.69515142907905952645e-15;

const double sgn_x = GSL_SIGN(x);
const double abs_x = fabs(x);

if(abs_x < GSL_ROOT4_DBL_EPSILON) {
  const double x2 = x*x;
  result->val = x * (1.0 - x2/6.0);
  result->err = fabs(x*x2*x2 / 100.0);
  return GSL_SUCCESS;
}
else {
  double sgn_result = sgn_x;
  double y = floor(abs_x/(0.25*M_PI));
  int octant = y - ldexp(floor(ldexp(y,-3)),3);
  int stat_cs;
  double z;

  if(GSL_IS_ODD(octant)) {
    octant += 1;
    octant &= 07;
    y += 1.0;
  }

  if(octant > 3) {
    octant -= 4;
    sgn_result = -sgn_result;
  }

  z = ((abs_x - y * P1) - y * P2) - y * P3;

  if(octant == 0) {
    gsl_sf_result sin_cs_result;
    const double t = 8.0*fabs(z)/M_PI - 1.0;
    stat_cs = cheb_eval_e(&sin_cs, t, &sin_cs_result);
    result->val = z * (1.0 + z*z * sin_cs_result.val);
  }
  else { /* octant == 2 */
    gsl_sf_result cos_cs_result;
    const double t = 8.0*fabs(z)/M_PI - 1.0;
    stat_cs = cheb_eval_e(&cos_cs, t, &cos_cs_result);
    result->val = 1.0 - 0.5*z*z * (1.0 - z*z * cos_cs_result.val);
  }

  result->val *= sgn_result;

  if(abs_x > 1.0/GSL_DBL_EPSILON) {
    result->err = fabs(result->val);
  }
  else if(abs_x > 100.0/GSL_SQRT_DBL_EPSILON) {
    result->err = 2.0 * abs_x * GSL_DBL_EPSILON * fabs(result->val);
  }
  else if(abs_x > 0.1/GSL_SQRT_DBL_EPSILON) {
    result->err = 2.0 * GSL_SQRT_DBL_EPSILON * fabs(result->val);
  }
  else {
    result->err = 2.0 * GSL_DBL_EPSILON * fabs(result->val);
  }

  return stat_cs;
}
}
}

您应该记住,GSL是由科学家创建并供科学家使用的库。因此,您需要一种方法来控制结果及其正确性(有关详细信息,请参见上面的注释和this answer),在这种情况下,速度是次要的。

与此同时,您需要记住,系统三角函数是由聪明的IBM人实现的,他们知道如何快速(绝对不可读)。