Boost logo

Boost :

Subject: Re: [boost] [local] Help for the Alternatives section
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-04-03 13:47:53


On Sun, Apr 3, 2011 at 1:23 PM, Lorenzo Caminiti <lorcaminiti_at_[hidden]> wrote:
> I have done an (hopefully more correct) benchmark of Boost.Local
> performances compared with the alternative methods -- please check my
> doing :)
>
> In summary:
> 1) Boost.Phoenix, global functors, and local functors run in ~15s.
> 2) Boost.Lambda runs in ~40s.
> 3) Boost.Local runs in ~53s.
> 4) I don't have a C++0x lambda compiler so I could not benchmark C++0x lambdas.
>
> I don't know why Boost.Lambda takes longer than 1). Boost.Local seems
> to take longer than 1) because of the extra virtual function call
> introduced by the trick that allows to pass the local struct as a
> template parameter -- I will follow up with another email to explain
> this point in detail.

This is the trick used by Boost.Local to call a function defined
within a local struct from a functor passed as template parameter:

1) The local struct inherits from abstract_function and implementing operator().
2) The local function is of a non-local type `function` so it can be
passed as template parameter.
3) The functor `function` calls abstract_function's virtual operator()
which in turn calls the local struct implementation of operator().

The following code shows the idea:

#include <iostream>
#include <vector>
#include <algorithm>

#define N 10000

template<typename R, typename A0>
struct abstract_function {
    virtual R operator()(A0) = 0;
};

template<typename R, typename A0>
struct function {
    function(abstract_function<R, A0>& ref): ptr_(&ref) {}
    R operator()(A0 a0) { return (*ptr_)(a0); }
private:
    abstract_function<R, A0>* ptr_;
};

int main() {
    double sum = 0.0;
    int factor = 10;

    struct add_function: abstract_function<void, const double&> {
        add_function(double& _sum, const int& _factor):
                sum_(_sum), factor_(_factor) {}
        void operator()(const double& num) { return body(num, sum_, factor_); }
    private:
        double& sum_;
        const int& factor_;
        void body(const double& num, double& sum, const int& factor) {
            sum += factor * num;
        }
    };
    add_function functor_add(sum, factor);
    function<void, const double&> add(functor_add);

    std::vector<double> v(N * 100);
    std::fill(v.begin(), v.end(), 10);

    for (size_t n = 0; n < N; ++n) {
// for (size_t i = 0; i < v.size(); ++i) {
// functor_add(v[i]); // (1)
// add(v[i]); // (2)
// }
        std::for_each(v.begin(), v.end(), add); // (3) OK add as tparam!
    }

    std::cout << sum << std::endl;
    return 0;
}

The call at line (3) calls functionr::operator() --<<virtual>>-->
abstract_function::operator() --> add_function::operator() so the
local struct operator() is called.

A) Now if I comment line (3), uncomment the for-loop and line (2):

    ...
    for (size_t n = 0; n < N; ++n) {
        for (size_t i = 0; i < v.size(); ++i) {
// functor_add(v[i]); // (1)
            add(v[i]); // (2)
        }
// std::for_each(v.begin(), v.end(), add); // (3) OK add as tparam!
    }
    ...

This runs in 49s.

B) But if I use line (1) instead of (2):

    ...
    for (size_t n = 0; n < N; ++n) {
        for (size_t i = 0; i < v.size(); ++i) {
            functor_add(v[i]); // (1)
// add(v[i]); // (2)
        }
// std::for_each(v.begin(), v.end(), add); // (3) OK add as tparam!
    }
    ...

This runs in 15s!!

As far I can see, line (1) is faster because it does not have the
overhead of the virtual function call that line (2) has:

A) Line (2) call: functionr::operator() --<<virtual>>-->
abstract_function::operator() --> add_function::operator() (runs in
49s)
B) Line (1) call: add_function::operator() (runs in 15s)

What do you think? Is there any way I can make the A) faster (i.e.,
run-time similarly to B))?

Thank you very much.

--
Lorenzo

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk