Научная библиотека Гну плохая производительность по сравнению с libc - PullRequest
0 голосов
/ 27 июня 2018

Я реализовал простую программу для измерения производительности вычислений sin с помощью научной библиотеки gnu и libc. Вот исходный код:

#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;
}

Вот результат: STD: 57,8869 мс GSL: 106,787 мс

Так нормально ли, что GSL работает медленнее, чем libc?

Ответы [ 2 ]

0 голосов
/ 21 июля 2018

К сожалению, я не могу комментировать, поэтому я публикую это как отдельный ответ. Я думаю, что ответ @ 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 - это библиотека, созданная учеными и для ученых. Поэтому вам нужен способ контролировать свои результаты и их правильность (см. Этот комментарий выше и этот ответ для деталей), скорость в этом случае вторична.

В то же время вы должны помнить, что системные тригонометрические функции были реализованы умными IBM парнями, которые знают, как сделать это быстро (и абсолютно нечитаемо).

0 голосов
/ 27 июня 2018

GSL использует программный грех (даже когда грех процессора), и он также может дать вам оценки ошибок: gsl_sf_sin - это просто оболочка для gsl_sf_sin_e (которая дает оценки ошибок). Эта процедура не слишком оптимизирована для скорости.

С другой стороны, libc sin обычно довольно хорошо оптимизирован для скорости.

Кроме того, ваш тест может быть ошибочным, так как sin может быть оптимизирован компилятором, если оптимизация включена.

...