
On Sun, Sep 12, 2010 at 1:40 PM, John Bytheway <jbytheway+boost@gmail.com> 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