3

I'm trying to dertermine if the time overhead introduced by boost::function to evaluate mathematical functions is negligeable versus using function templates.

The code for the benchmark I use is bellow.

With traditionnal g++, overhead with boost::function is negligeable :

$ g++ -O3 main.cxx 
$ ./a.out
METHOD              INTEGRAL  TIME TO COMPUTE (SEC)         
Direct              0.379885  3.360000                      
Function template   0.379885  3.380000                      
Boost function      0.379885  3.400000   

With llvm-g++, there a speed gain of factor 1.5 for templates function, but no gain for boost::function.

$ llvm-g++ -O3 main.cxx
METHOD              INTEGRAL  TIME TO COMPUTE (SEC)         
Direct              0.379885  2.170000                      
Function template   0.379885  2.160000                      
Boost function      0.379885  3.360000    

Is it possible to obtain the 1.5 gain for boost::function and llvm-g++?

#include <boost/function.hpp>
#include <math.h>
#include <stdio.h>

typedef unsigned int UInt;

using namespace std;

//=============================================================================
// chrono
//=============================================================================
class Chrono
{
    clock_t t1_,t2_,dt_;
    public:
        Chrono(){}
        void   start() { t1_=clock(); };
        void   stop()  { t2_=clock(); };
        double diff()  { return ( (double)( t2_ - t1_) ) / CLOCKS_PER_SEC; };
};

//=============================================================================
// function to integrate
//=============================================================================
inline double fct(double x)
{
    return 1. / (1.+exp(x));
}

//=============================================================================
// using direct method
//=============================================================================
double direct(double a, double b, UInt numSamplePoints)
{
    double delta = (b-a) / (numSamplePoints-1);
    double sum = 0.;
    for (UInt i=0; i < numSamplePoints-1; ++i)
        sum += 1. / (1. + exp(a + i*delta));
    return sum * delta;
}

//=============================================================================
// using function template
//=============================================================================
template<double functionToIntegrate(double)>
double integrate(double a, double b, UInt numSamplePoints)
{
    double delta = (b-a) / (numSamplePoints-1);
    double sum = 0.;
    for (UInt i=0; i < numSamplePoints-1; ++i)
        sum += functionToIntegrate(a + i*delta);
    return sum * delta;
}

//=============================================================================
// using Boost function
//=============================================================================
typedef boost::function<double ( double )> fct_type;

class IntegratorBoost {
    public:
    fct_type functionToIntegrate;
    IntegratorBoost(fct_type fct): functionToIntegrate(fct){}
    double integrate(double a, double b, UInt numSamplePoints)
    {
        double delta = (b-a) / (numSamplePoints-1);
        double sum = 0.;
        for (UInt i=0; i < numSamplePoints-1; ++i)
        sum += functionToIntegrate(a + i*delta);
        return sum * (b-a) / numSamplePoints;
    }
};

//=============================================================================
// main
//=============================================================================
int main()
{
    double integral;
    UInt numSamplePoints = 5E07;
    Chrono chrono;

    printf("%-20s%-10s%-30s\n","METHOD","INTEGRAL","TIME TO COMPUTE (SEC)");

    // Direct
    chrono.start();
    integral = direct(0., 1., numSamplePoints);
    chrono.stop();
    printf("%-20s%-10f%-30f\n","Direct",integral,chrono.diff());

    // Function template
    chrono.start();
    integral = integrate<fct>(0., 1.,numSamplePoints);
    chrono.stop();
    printf("%-20s%-10f%-30f\n","Function template",integral,chrono.diff());

    // Boost function
    chrono.start();
    IntegratorBoost intboost(fct);
    integral = intboost.integrate(0.,1.,numSamplePoints);
    chrono.stop();
    printf("%-20s%-10f%-30f\n","Boost function",integral,chrono.diff());
}
user744629
  • 1,961
  • 1
  • 18
  • 27
  • 1
    Using `clock` for benchmarks isn’t all that reliable. I’d suspect that it could skew your results sufficiently to make them meaningless. – Konrad Rudolph Mar 15 '12 at 12:53
  • 1
    On Linux, I would recommend `gettimeofday` for a more precise timer. Also, llvm-g++ is deprecated (and was based on g++ 4.2), I would recommend switching to Clang 3.0 for better conformance and new and shiny C++11. – Matthieu M. Mar 15 '12 at 13:03
  • I'd also time a lambda function to see how it stacks up. It's got all the strengths of `boost::function` and lower level compiler support. – StilesCrisis Mar 15 '12 at 13:52
  • @MatthieuM. The right function for a precise timer is clock_gettime(CLOCK_MONOTONIC,...) – waynix Jun 17 '12 at 23:21

1 Answers1

0

Without actually measure, I am going to venture and claim that using boost::function (or std::function from C++11) cannot be as efficient as the other two options.

The reason is that function uses type erasure to remove the type of the actual functor being used, and that implies that function needs to store the actual object that makes the call through by pointer and use function calls. On the other hand, in the other two methods, the compiler is able to inline the logic and remove the cost of dispatch.

This is actually quite similar to the many times mentioned difference in performance of C library qsort compared to C++ sort, where by using a functor the compiler has better chances for inlining and optimizing.

A different question then, is whether this will have an impact on your application, and for that you need to measure. It might be the case that overall the cost of IO, or any other operation dominates your application and this won't make a difference at all.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489