Boost logo

Boost :

Subject: Re: [boost] [local_function] passing local functions as template parameters
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2010-09-12 15:15:35


On Sun, Sep 12, 2010 at 1:40 PM, John Bytheway
<jbytheway+boost_at_[hidden]> wrote:
> On 12/09/10 16:41, Lorenzo Caminiti wrote:
>>
>> Can you please try this code on other compilers?
>
> Compiles fine on clang and icc.

Thanks a lot for checking this!

>> Do you see any issue with this code?
>
> Yes, by using boost::function you're introducing an extra virtual
> function call in each invocation of lfn.  It's optimistic to assume that
> the compiler will figure out what's going on well enough to optimize all
> that away.
>
> OTOH, I looked at the assembly generated by icc and it does indeed seem
> to have arranged for both calls to be made directly, not through
> vtables, so it is possible, but oddly the calls are not inlined (I guess
> icc wasn't able to prove that there's only one call to lfn_t::body,
> which it should in principle have been able to), so lfn_t::body is still
> two calls away from main().  For definiteness, the piece of
> Boost.Function called through is a 54-byte function including one
> conditional branch.
>
> I think it would be worth rolling your own simpler wrapper; something
> along these lines:
>
> #include <boost/function.hpp>
> #include <boost/bind.hpp>
> #include <iostream>
> #include <algorithm>
> #include <vector>
>
> template<typename F> struct lfn_global {};
> template<typename A1> struct lfn_global<void (A1)> {
>  virtual void operator()(A1) {}
> };
> // Similarly define `lfn_global` for other function arities and return
> types.
>
> template<typename F>
> struct ref_wrapper;
>
> template<typename A1>
> struct ref_wrapper<void (A1)> {
>  typedef lfn_global<void (A1)> wrapped;
>  explicit ref_wrapper(wrapped& f) : f_(f) {}
>  void operator()(A1 a1) {
>    f_(a1);
>  }
>  wrapped& f_;
> };
>
> int main () {
>    double x = 5.6;
>
>    struct lfn_t: public lfn_global<void (int)> {
>        lfn_t(double const& bound_x): x(bound_x) {}
>        void operator()(int i) {
>            std::cout << " " << i + x; // Local func body.
>        }
>    private:
>        double const& x;
>    } lfn_obj(x); // x is const& bound from scope.
>    ref_wrapper<void (int)> lfn(lfn_obj);
>
>    std::vector<int> v;
>    v.push_back(10);
>    v.push_back(20);
>    v.push_back(30);
>
>    std::cout << "v: ";
>    std::for_each(v.begin(), v.end(), lfn);
>    std::cout << std::endl;
>
>    return 0;
> }
>
> (I would have used boost::ref, but that doesn't overload operator())
>
> In this case icc chooses to inline the call to ref_wrapper::operator(),
> so that lfn_t::operator() is called directly from main (as before, that
> one is still not inlined).
>
> Anyway, the upshot of what I'm trying to say is: you may wish to make
> the compiler's life easier to head off efficiency concerns, and you
> should certainly do some profiling to measure the cost of a virtual
> function call in this context.  But, all in all, it's a neat trick.

I understand your concern and suggestion. If this trick works and it
makes it into Boost.LocalFunction, I will definitely consider
performance optimizations.

--
Lorenzo

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