Reduced copying in `defer`, `dispatch` and `Future`. This reduces number of copies made for each parameter in a piece of code like this:
``` future.then(defer(pid, &SomeProcess::someMethod, param1, param2)); ``` For the objects that do not support move semantics (e.g., protobuf messages), number of copies is reduced from 8-10 to 6. If move semantics is supported, then number of copies is reduced from 6-7 to 1 if parameter is passed with `std::move`, or 2 otherwise. Review: https://reviews.apache.org/r/60003/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f8e4f11e Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f8e4f11e Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f8e4f11e Branch: refs/heads/master Commit: f8e4f11e796b2b0d9bd101a7eb6f106e72cbaee1 Parents: 2ce2a0a Author: Dmitry Zhuk <dz...@twopensource.com> Authored: Mon Aug 7 11:01:04 2017 -0700 Committer: Michael Park <mp...@apache.org> Committed: Tue Aug 8 13:33:15 2017 -0700 ---------------------------------------------------------------------- 3rdparty/libprocess/include/process/defer.hpp | 81 +++++--- .../libprocess/include/process/deferred.hpp | 184 ++++++++++--------- .../libprocess/include/process/dispatch.hpp | 84 +++++---- 3rdparty/libprocess/include/process/future.hpp | 45 ++--- 3rdparty/libprocess/include/process/http.hpp | 3 +- 3rdparty/libprocess/src/tests/benchmarks.cpp | 86 +++++++++ 6 files changed, 318 insertions(+), 165 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/include/process/defer.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/defer.hpp b/3rdparty/libprocess/include/process/defer.hpp index 7f3369e..9457080 100644 --- a/3rdparty/libprocess/include/process/defer.hpp +++ b/3rdparty/libprocess/include/process/defer.hpp @@ -58,20 +58,31 @@ Deferred<void()> defer(const Process<T>* process, void (T::*method)()) // libc++) we can't use std::bind with a std::function so we have to // explicitly use the std::function<R(P...)>::operator() (see // http://stackoverflow.com/questions/20097616/stdbind-to-a-stdfunction-crashes-with-clang). + +// This assumes that type and variable base names are `A` and `a` respectively. +#define FORWARD(Z, N, DATA) std::forward<A ## N>(a ## N) + #define TEMPLATE(Z, N, DATA) \ template <typename T, \ ENUM_PARAMS(N, typename P), \ ENUM_PARAMS(N, typename A)> \ auto defer(const PID<T>& pid, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<void(ENUM_PARAMS(N, P))>::operator(), std::function<void(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))> /* NOLINT(whitespace/line_length) */ \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<void(ENUM_PARAMS(N, P))>::operator(), \ + std::function<void(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))> \ { \ std::function<void(ENUM_PARAMS(N, P))> f( \ [=](ENUM_BINARY_PARAMS(N, P, p)) { \ dispatch(pid, method, ENUM_PARAMS(N, p)); \ }); \ - return std::bind(&std::function<void(ENUM_PARAMS(N, P))>::operator(), std::move(f), ENUM_PARAMS(N, a)); /* NOLINT(whitespace/line_length) */ \ + return std::bind( \ + &std::function<void(ENUM_PARAMS(N, P))>::operator(), \ + std::move(f), \ + ENUM(N, FORWARD, _)); \ } \ \ template <typename T, \ @@ -79,10 +90,10 @@ Deferred<void()> defer(const Process<T>* process, void (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const Process<T>& process, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process.self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process.self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process.self(), method, ENUM_PARAMS(N, a)); \ + return defer(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename T, \ @@ -90,10 +101,10 @@ Deferred<void()> defer(const Process<T>* process, void (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const Process<T>* process, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process->self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process->self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process->self(), method, ENUM_PARAMS(N, a)); \ + return defer(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. @@ -127,14 +138,21 @@ Deferred<Future<R>()> defer(const Process<T>* process, Future<R> (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const PID<T>& pid, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::function<Future<R>(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))> /* NOLINT(whitespace/line_length) */ \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::function<Future<R>(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))> \ { \ std::function<Future<R>(ENUM_PARAMS(N, P))> f( \ [=](ENUM_BINARY_PARAMS(N, P, p)) { \ return dispatch(pid, method, ENUM_PARAMS(N, p)); \ }); \ - return std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::move(f), ENUM_PARAMS(N, a)); /* NOLINT(whitespace/line_length) */ \ + return std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::move(f), \ + ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -143,10 +161,10 @@ Deferred<Future<R>()> defer(const Process<T>* process, Future<R> (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const Process<T>& process, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process.self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process.self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process.self(), method, ENUM_PARAMS(N, a)); \ + return defer(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -155,10 +173,10 @@ Deferred<Future<R>()> defer(const Process<T>* process, Future<R> (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const Process<T>* process, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process->self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process->self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process->self(), method, ENUM_PARAMS(N, a)); \ + return defer(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. @@ -192,14 +210,21 @@ Deferred<Future<R>()> defer(const Process<T>* process, R (T::*method)()) ENUM_PARAMS(N, typename A)> \ auto defer(const PID<T>& pid, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::function<Future<R>(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))> /* NOLINT(whitespace/line_length) */ \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::function<Future<R>(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))> \ { \ std::function<Future<R>(ENUM_PARAMS(N, P))> f( \ [=](ENUM_BINARY_PARAMS(N, P, p)) { \ return dispatch(pid, method, ENUM_PARAMS(N, p)); \ }); \ - return std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::move(f), ENUM_PARAMS(N, a)); /* NOLINT(whitespace/line_length) */ \ + return std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::move(f), \ + ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -209,10 +234,10 @@ Deferred<Future<R>()> defer(const Process<T>* process, R (T::*method)()) auto \ defer(const Process<T>& process, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process.self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process.self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process.self(), method, ENUM_PARAMS(N, a)); \ + return defer(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -222,15 +247,17 @@ Deferred<Future<R>()> defer(const Process<T>* process, R (T::*method)()) auto \ defer(const Process<T>* process, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> decltype(defer(process->self(), method, ENUM_PARAMS(N, a))) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> decltype(defer(process->self(), method, ENUM(N, FORWARD, _))) \ { \ - return defer(process->self(), method, ENUM_PARAMS(N, a)); \ + return defer(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. #undef TEMPLATE +#undef FORWARD + // Now we define defer calls for functors (with and without a PID): http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/include/process/deferred.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/deferred.hpp b/3rdparty/libprocess/include/process/deferred.hpp index e446621..af65ead 100644 --- a/3rdparty/libprocess/include/process/deferred.hpp +++ b/3rdparty/libprocess/include/process/deferred.hpp @@ -63,7 +63,9 @@ private: template <typename F> struct _Deferred { - operator Deferred<void()>() const + // We expect that conversion operators are invoked on rvalues only, + // as _Deferred is supposed to be used directly as a result of defer call. + operator Deferred<void()>() && { // The 'pid' differentiates an already dispatched functor versus // one which still needs to be dispatched (which is done @@ -74,13 +76,13 @@ struct _Deferred // arguments which will just be dropped when invoking the // underlying bound function). if (pid.isNone()) { - return std::function<void()>(f); + return std::function<void()>(std::forward<F>(f)); } // We need to explicitly copy the members otherwise we'll // implicitly copy 'this' which might not exist at invocation. Option<UPID> pid_ = pid; - F f_ = f; + F&& f_ = std::forward<F>(f); return std::function<void()>( [=]() { @@ -88,14 +90,14 @@ struct _Deferred }); } - operator std::function<void()>() const + operator std::function<void()>() && { if (pid.isNone()) { - return std::function<void()>(f); + return std::function<void()>(std::forward<F>(f)); } Option<UPID> pid_ = pid; - F f_ = f; + F&& f_ = std::forward<F>(f); return std::function<void()>( [=]() { @@ -104,14 +106,14 @@ struct _Deferred } template <typename R> - operator Deferred<R()>() const + operator Deferred<R()>() && { if (pid.isNone()) { - return std::function<R()>(f); + return std::function<R()>(std::forward<F>(f)); } Option<UPID> pid_ = pid; - F f_ = f; + F&& f_ = std::forward<F>(f); return std::function<R()>( [=]() { @@ -120,14 +122,14 @@ struct _Deferred } template <typename R> - operator std::function<R()>() const + operator std::function<R()>() && { if (pid.isNone()) { - return std::function<R()>(f); + return std::function<R()>(std::forward<F>(f)); } Option<UPID> pid_ = pid; - F f_ = f; + F&& f_ = std::forward<F>(f); return std::function<R()>( [=]() { @@ -141,43 +143,43 @@ struct _Deferred // libc++) we can't use std::bind with a std::function so we have to // explicitly use the std::function<R(P...)>::operator() (see // http://stackoverflow.com/questions/20097616/stdbind-to-a-stdfunction-crashes-with-clang). -#define TEMPLATE(Z, N, DATA) \ - template <ENUM_PARAMS(N, typename P)> \ - operator Deferred<void(ENUM_PARAMS(N, P))>() const \ - { \ - if (pid.isNone()) { \ - return std::function<void(ENUM_PARAMS(N, P))>(f); \ - } \ - \ - Option<UPID> pid_ = pid; \ - F f_ = f; \ - \ - return std::function<void(ENUM_PARAMS(N, P))>( \ - [=](ENUM_BINARY_PARAMS(N, P, p)) { \ - std::function<void()> f__([=]() { \ - f_(ENUM_PARAMS(N, p)); \ - }); \ - dispatch(pid_.get(), f__); \ - }); \ - } \ - \ - template <ENUM_PARAMS(N, typename P)> \ - operator std::function<void(ENUM_PARAMS(N, P))>() const \ - { \ - if (pid.isNone()) { \ - return std::function<void(ENUM_PARAMS(N, P))>(f); \ - } \ - \ - Option<UPID> pid_ = pid; \ - F f_ = f; \ - \ - return std::function<void(ENUM_PARAMS(N, P))>( \ - [=](ENUM_BINARY_PARAMS(N, P, p)) { \ - std::function<void()> f__([=]() { \ - f_(ENUM_PARAMS(N, p)); \ - }); \ - dispatch(pid_.get(), f__); \ - }); \ +#define TEMPLATE(Z, N, DATA) \ + template <ENUM_PARAMS(N, typename P)> \ + operator Deferred<void(ENUM_PARAMS(N, P))>() && \ + { \ + if (pid.isNone()) { \ + return std::function<void(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \ + } \ + \ + Option<UPID> pid_ = pid; \ + F&& f_ = std::forward<F>(f); \ + \ + return std::function<void(ENUM_PARAMS(N, P))>( \ + [=](ENUM_BINARY_PARAMS(N, P, p)) { \ + std::function<void()> f__([=]() { \ + f_(ENUM_PARAMS(N, p)); \ + }); \ + dispatch(pid_.get(), f__); \ + }); \ + } \ + \ + template <ENUM_PARAMS(N, typename P)> \ + operator std::function<void(ENUM_PARAMS(N, P))>() && \ + { \ + if (pid.isNone()) { \ + return std::function<void(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \ + } \ + \ + Option<UPID> pid_ = pid; \ + F&& f_ = std::forward<F>(f); \ + \ + return std::function<void(ENUM_PARAMS(N, P))>( \ + [=](ENUM_BINARY_PARAMS(N, P, p)) { \ + std::function<void()> f__([=]() { \ + f_(ENUM_PARAMS(N, p)); \ + }); \ + dispatch(pid_.get(), f__); \ + }); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. @@ -185,14 +187,14 @@ struct _Deferred #define TEMPLATE(Z, N, DATA) \ template <typename R, ENUM_PARAMS(N, typename P)> \ - operator Deferred<R(ENUM_PARAMS(N, P))>() const \ + operator Deferred<R(ENUM_PARAMS(N, P))>() && \ { \ if (pid.isNone()) { \ - return std::function<R(ENUM_PARAMS(N, P))>(f); \ + return std::function<R(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \ } \ \ Option<UPID> pid_ = pid; \ - F f_ = f; \ + F&& f_ = std::forward<F>(f); \ \ return std::function<R(ENUM_PARAMS(N, P))>( \ [=](ENUM_BINARY_PARAMS(N, P, p)) { \ @@ -204,14 +206,14 @@ struct _Deferred } \ \ template <typename R, ENUM_PARAMS(N, typename P)> \ - operator std::function<R(ENUM_PARAMS(N, P))>() const \ + operator std::function<R(ENUM_PARAMS(N, P))>() && \ { \ if (pid.isNone()) { \ - return std::function<R(ENUM_PARAMS(N, P))>(f); \ + return std::function<R(ENUM_PARAMS(N, P))>(std::forward<F>(f)); \ } \ \ Option<UPID> pid_ = pid; \ - F f_ = f; \ + F&& f_ = std::forward<F>(f); \ \ return std::function<R(ENUM_PARAMS(N, P))>( \ [=](ENUM_BINARY_PARAMS(N, P, p)) { \ @@ -231,47 +233,63 @@ private: template <typename G> friend _Deferred<G> defer(const UPID& pid, G&& g); -#define TEMPLATE(Z, N, DATA) \ - template <typename T, \ - ENUM_PARAMS(N, typename P), \ - ENUM_PARAMS(N, typename A)> \ - friend auto defer(const PID<T>& pid, \ - void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<void(ENUM_PARAMS(N, P))>::operator(), std::function<void(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))>; // NOLINT(whitespace/line_length) +// This assumes type and variable base names are `A` and `a` respectively. +#define FORWARD(Z, N, DATA) std::forward<A ## N>(a ## N) + +#define TEMPLATE(Z, N, DATA) \ + template <typename T, \ + ENUM_PARAMS(N, typename P), \ + ENUM_PARAMS(N, typename A)> \ + friend auto defer(const PID<T>& pid, \ + void (T::*method)(ENUM_PARAMS(N, P)), \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<void(ENUM_PARAMS(N, P))>::operator(), \ + std::function<void(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))>; REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. #undef TEMPLATE -#define TEMPLATE(Z, N, DATA) \ - template <typename R, \ - typename T, \ - ENUM_PARAMS(N, typename P), \ - ENUM_PARAMS(N, typename A)> \ - friend auto defer(const PID<T>& pid, \ - Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::function<Future<R>(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))>; // NOLINT(whitespace/line_length) +#define TEMPLATE(Z, N, DATA) \ + template <typename R, \ + typename T, \ + ENUM_PARAMS(N, typename P), \ + ENUM_PARAMS(N, typename A)> \ + friend auto defer(const PID<T>& pid, \ + Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::function<Future<R>(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))>; REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. #undef TEMPLATE -#define TEMPLATE(Z, N, DATA) \ - template <typename R, \ - typename T, \ - ENUM_PARAMS(N, typename P), \ - ENUM_PARAMS(N, typename A)> \ - friend auto defer(const PID<T>& pid, \ - R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ - -> _Deferred<decltype(std::bind(&std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), std::function<Future<R>(ENUM_PARAMS(N, P))>(), ENUM_PARAMS(N, a)))>; // NOLINT(whitespace/line_length) +#define TEMPLATE(Z, N, DATA) \ + template <typename R, \ + typename T, \ + ENUM_PARAMS(N, typename P), \ + ENUM_PARAMS(N, typename A)> \ + friend auto defer(const PID<T>& pid, \ + R (T::*method)(ENUM_PARAMS(N, P)), \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ + -> _Deferred<decltype( \ + std::bind( \ + &std::function<Future<R>(ENUM_PARAMS(N, P))>::operator(), \ + std::function<Future<R>(ENUM_PARAMS(N, P))>(), \ + ENUM(N, FORWARD, _)))>; REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. #undef TEMPLATE +#undef FORWARD - _Deferred(const UPID& pid, F f) : pid(pid), f(f) {} + _Deferred(const UPID& pid, F&& f) : pid(pid), f(std::forward<F>(f)) {} - /*implicit*/ _Deferred(F f) : f(f) {} + /*implicit*/ _Deferred(F&& f) : f(std::forward<F>(f)) {} Option<UPID> pid; F f; http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/include/process/dispatch.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/dispatch.hpp b/3rdparty/libprocess/include/process/dispatch.hpp index 3a07938..096cae0 100644 --- a/3rdparty/libprocess/include/process/dispatch.hpp +++ b/3rdparty/libprocess/include/process/dispatch.hpp @@ -19,6 +19,7 @@ #include <process/process.hpp> +#include <stout/lambda.hpp> #include <stout/preprocessor.hpp> #include <stout/result_of.hpp> @@ -183,6 +184,11 @@ void dispatch(const Process<T>* process, void (T::*method)()) // Due to a bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933) // with variadic templates and lambdas, we still need to do // preprocessor expansions. + +// The following assumes base names for type and variable are `A` and `a`. +#define FORWARD(Z, N, DATA) std::forward<A ## N>(a ## N) +#define DECL(Z, N, DATA) typename std::decay<A ## N>::type& a ## N + #define TEMPLATE(Z, N, DATA) \ template <typename T, \ ENUM_PARAMS(N, typename P), \ @@ -190,16 +196,19 @@ void dispatch(const Process<T>* process, void (T::*method)()) void dispatch( \ const PID<T>& pid, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ std::shared_ptr<std::function<void(ProcessBase*)>> f( \ new std::function<void(ProcessBase*)>( \ - [=](ProcessBase* process) { \ - assert(process != nullptr); \ - T* t = dynamic_cast<T*>(process); \ - assert(t != nullptr); \ - (t->*method)(ENUM_PARAMS(N, a)); \ - })); \ + std::bind([method](ENUM(N, DECL, _), \ + ProcessBase* process) { \ + assert(process != nullptr); \ + T* t = dynamic_cast<T*>(process); \ + assert(t != nullptr); \ + (t->*method)(ENUM_PARAMS(N, a)); \ + }, \ + ENUM(N, FORWARD, _), \ + lambda::_1))); \ \ internal::dispatch(pid, f, &typeid(method)); \ } \ @@ -210,9 +219,9 @@ void dispatch(const Process<T>* process, void (T::*method)()) void dispatch( \ const Process<T>& process, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - dispatch(process.self(), method, ENUM_PARAMS(N, a)); \ + dispatch(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename T, \ @@ -221,9 +230,9 @@ void dispatch(const Process<T>* process, void (T::*method)()) void dispatch( \ const Process<T>* process, \ void (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - dispatch(process->self(), method, ENUM_PARAMS(N, a)); \ + dispatch(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. @@ -271,18 +280,22 @@ Future<R> dispatch(const Process<T>* process, Future<R> (T::*method)()) Future<R> dispatch( \ const PID<T>& pid, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ std::shared_ptr<Promise<R>> promise(new Promise<R>()); \ \ std::shared_ptr<std::function<void(ProcessBase*)>> f( \ new std::function<void(ProcessBase*)>( \ - [=](ProcessBase* process) { \ - assert(process != nullptr); \ - T* t = dynamic_cast<T*>(process); \ - assert(t != nullptr); \ - promise->associate((t->*method)(ENUM_PARAMS(N, a))); \ - })); \ + std::bind([promise, method](ENUM(N, DECL, _), \ + ProcessBase* process) { \ + assert(process != nullptr); \ + T* t = dynamic_cast<T*>(process); \ + assert(t != nullptr); \ + promise->associate( \ + (t->*method)(ENUM_PARAMS(N, a))); \ + }, \ + ENUM(N, FORWARD, _), \ + lambda::_1))); \ \ internal::dispatch(pid, f, &typeid(method)); \ \ @@ -296,9 +309,9 @@ Future<R> dispatch(const Process<T>* process, Future<R> (T::*method)()) Future<R> dispatch( \ const Process<T>& process, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \ + return dispatch(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -308,9 +321,9 @@ Future<R> dispatch(const Process<T>* process, Future<R> (T::*method)()) Future<R> dispatch( \ const Process<T>* process, \ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \ + return dispatch(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. @@ -358,18 +371,21 @@ Future<R> dispatch(const Process<T>* process, R (T::*method)()) Future<R> dispatch( \ const PID<T>& pid, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ std::shared_ptr<Promise<R>> promise(new Promise<R>()); \ \ std::shared_ptr<std::function<void(ProcessBase*)>> f( \ new std::function<void(ProcessBase*)>( \ - [=](ProcessBase* process) { \ - assert(process != nullptr); \ - T* t = dynamic_cast<T*>(process); \ - assert(t != nullptr); \ - promise->set((t->*method)(ENUM_PARAMS(N, a))); \ - })); \ + std::bind([promise, method](ENUM(N, DECL, _), \ + ProcessBase* process) { \ + assert(process != nullptr); \ + T* t = dynamic_cast<T*>(process); \ + assert(t != nullptr); \ + promise->set((t->*method)(ENUM_PARAMS(N, a))); \ + }, \ + ENUM(N, FORWARD, _), \ + lambda::_1))); \ \ internal::dispatch(pid, f, &typeid(method)); \ \ @@ -383,9 +399,9 @@ Future<R> dispatch(const Process<T>* process, R (T::*method)()) Future<R> dispatch( \ const Process<T>& process, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \ + return dispatch(process.self(), method, ENUM(N, FORWARD, _)); \ } \ \ template <typename R, \ @@ -395,14 +411,16 @@ Future<R> dispatch(const Process<T>* process, R (T::*method)()) Future<R> dispatch( \ const Process<T>* process, \ R (T::*method)(ENUM_PARAMS(N, P)), \ - ENUM_BINARY_PARAMS(N, A, a)) \ + ENUM_BINARY_PARAMS(N, A, &&a)) \ { \ - return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \ + return dispatch(process->self(), method, ENUM(N, FORWARD, _)); \ } REPEAT_FROM_TO(1, 12, TEMPLATE, _) // Args A0 -> A10. #undef TEMPLATE +#undef DECL +#undef FORWARD // We use partial specialization of // - internal::Dispatch<void> vs http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/include/process/future.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/future.hpp b/3rdparty/libprocess/include/process/future.hpp index cce9505..2f5f0a2 100644 --- a/3rdparty/libprocess/include/process/future.hpp +++ b/3rdparty/libprocess/include/process/future.hpp @@ -175,32 +175,34 @@ public: template <typename F> const Future<T>& onDiscard(_Deferred<F>&& deferred) const { - return onDiscard(deferred.operator std::function<void()>()); + return onDiscard(std::move(deferred).operator std::function<void()>()); } template <typename F> const Future<T>& onReady(_Deferred<F>&& deferred) const { - return onReady(deferred.operator std::function<void(const T&)>()); + return onReady( + std::move(deferred).operator std::function<void(const T&)>()); } template <typename F> const Future<T>& onFailed(_Deferred<F>&& deferred) const { return onFailed( - deferred.operator std::function<void(const std::string&)>()); + std::move(deferred).operator std::function<void(const std::string&)>()); } template <typename F> const Future<T>& onDiscarded(_Deferred<F>&& deferred) const { - return onDiscarded(deferred.operator std::function<void()>()); + return onDiscarded(std::move(deferred).operator std::function<void()>()); } template <typename F> const Future<T>& onAny(_Deferred<F>&& deferred) const { - return onAny(deferred.operator std::function<void(const Future<T>&)>()); + return onAny( + std::move(deferred).operator std::function<void(const Future<T>&)>()); } private: @@ -334,21 +336,22 @@ public: // and associates the result of the callback with the future that is // returned to the caller (which may be of a different type). template <typename X> - Future<X> then(const lambda::function<Future<X>(const T&)>& f) const; + Future<X> then(lambda::function<Future<X>(const T&)> f) const; template <typename X> - Future<X> then(const lambda::function<X(const T&)>& f) const; + Future<X> then(lambda::function<X(const T&)> f) const; template <typename X> - Future<X> then(const lambda::function<Future<X>()>& f) const + Future<X> then(lambda::function<Future<X>()> f) const { - return then(lambda::function<Future<X>(const T&)>(lambda::bind(f))); + return then( + lambda::function<Future<X>(const T&)>(lambda::bind(std::move(f)))); } template <typename X> - Future<X> then(const lambda::function<X()>& f) const + Future<X> then(lambda::function<X()> f) const { - return then(lambda::function<X(const T&)>(lambda::bind(f))); + return then(lambda::function<X(const T&)>(lambda::bind(std::move(f)))); } private: @@ -360,7 +363,7 @@ private: { // note the then<X> is necessary to not have an infinite loop with // then(F&& f) - return then<X>(f.operator std::function<Future<X>(const T&)>()); + return then<X>(std::move(f).operator std::function<Future<X>(const T&)>()); } // Refer to the less preferred version of `onReady` for why these SFINAE @@ -373,13 +376,13 @@ private: F>::type()>::type>::type> Future<X> then(_Deferred<F>&& f, LessPrefer) const { - return then<X>(f.operator std::function<Future<X>()>()); + return then<X>(std::move(f).operator std::function<Future<X>()>()); } template <typename F, typename X = typename internal::unwrap<typename result_of<F(const T&)>::type>::type> // NOLINT(whitespace/line_length) Future<X> then(F&& f, Prefer) const { - return then<X>(std::function<Future<X>(const T&)>(f)); + return then<X>(std::function<Future<X>(const T&)>(std::forward<F>(f))); } // Refer to the less preferred version of `onReady` for why these SFINAE @@ -392,7 +395,7 @@ private: F>::type()>::type>::type> Future<X> then(F&& f, LessPrefer) const { - return then<X>(std::function<Future<X>()>(f)); + return then<X>(std::function<Future<X>()>(std::forward<F>(f))); } public: @@ -1396,14 +1399,14 @@ void after( template <typename T> template <typename X> -Future<X> Future<T>::then(const lambda::function<Future<X>(const T&)>& f) const +Future<X> Future<T>::then(lambda::function<Future<X>(const T&)> f) const { std::shared_ptr<Promise<X>> promise(new Promise<X>()); lambda::function<void(const Future<T>&)> thenf = - lambda::bind(&internal::thenf<T, X>, f, promise, lambda::_1); + lambda::bind(&internal::thenf<T, X>, std::move(f), promise, lambda::_1); - onAny(thenf); + onAny(std::move(thenf)); // Propagate discarding up the chain. To avoid cyclic dependencies, // we keep a weak future in the callback. @@ -1416,14 +1419,14 @@ Future<X> Future<T>::then(const lambda::function<Future<X>(const T&)>& f) const template <typename T> template <typename X> -Future<X> Future<T>::then(const lambda::function<X(const T&)>& f) const +Future<X> Future<T>::then(lambda::function<X(const T&)> f) const { std::shared_ptr<Promise<X>> promise(new Promise<X>()); lambda::function<void(const Future<T>&)> then = - lambda::bind(&internal::then<T, X>, f, promise, lambda::_1); + lambda::bind(&internal::then<T, X>, std::move(f), promise, lambda::_1); - onAny(then); + onAny(std::move(then)); // Propagate discarding up the chain. To avoid cyclic dependencies, // we keep a weak future in the callback. http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/include/process/http.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/http.hpp b/3rdparty/libprocess/include/process/http.hpp index f637999..ba1b086 100644 --- a/3rdparty/libprocess/include/process/http.hpp +++ b/3rdparty/libprocess/include/process/http.hpp @@ -1017,7 +1017,8 @@ Future<Nothing> serve( template <typename F> Future<Nothing> serve(const network::Socket& s, F&& f) { - return internal::serve(s, std::function<Future<Response>(const Request&)>(f)); + return internal::serve( + s, std::function<Future<Response>(const Request&)>(std::forward<F>(f))); } http://git-wip-us.apache.org/repos/asf/mesos/blob/f8e4f11e/3rdparty/libprocess/src/tests/benchmarks.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/tests/benchmarks.cpp b/3rdparty/libprocess/src/tests/benchmarks.cpp index 245ff77..a691705 100644 --- a/3rdparty/libprocess/src/tests/benchmarks.cpp +++ b/3rdparty/libprocess/src/tests/benchmarks.cpp @@ -488,3 +488,89 @@ TEST(ProcessTest, Process_BENCHMARK_ThroughputPerformance) wait(destination->self()); } } + + +class DispatchProcess : public Process<DispatchProcess> +{ +public: + struct Movable + { + std::vector<int> data; + }; + + // This simulates protobuf objects, which do not support moves. + struct Copyable + { + std::vector<int> data; + + Copyable(std::vector<int>&& data) : data(std::move(data)) {} + Copyable(const Copyable& that) = default; + Copyable& operator=(const Copyable&) = default; + }; + + DispatchProcess(Promise<Nothing> *promise, long repeat) + : promise(promise), repeat(repeat) {} + + template <typename T> + Future<Nothing> handler(const T& data) + { + count++; + if (count >= repeat) { + promise->set(Nothing()); + return Nothing(); + } + + dispatch(self(), &Self::_handler).then( + defer(self(), &Self::handler<T>, data)); + + return Nothing(); + } + + template <typename T> + static void run(const string& name, long repeats) + { + Promise<Nothing> promise; + + Owned<DispatchProcess> process(new DispatchProcess(&promise, repeats)); + spawn(*process); + + T data{std::vector<int>(10240, 42)}; + + Stopwatch watch; + watch.start(); + + dispatch(process.get(), &DispatchProcess::handler<T>, data); + + AWAIT_READY(promise.future()); + + cout << name << " elapsed: " << watch.elapsed() << endl; + + terminate(process.get()); + wait(process.get()); + } + +private: + Future<Nothing> _handler() + { + return Nothing(); + } + + Promise<Nothing> *promise; + long repeat; + long count = 0; +}; + + +TEST(ProcessTest, Process_BENCHMARK_DispatchDefer) +{ + constexpr long repeats = 100000; + + // Test performance separately for objects which support std::move, + // and which don't (e.g. like protobufs). + // Note: DispatchProcess::handler code is not fully optimized, + // to take advantage of std::move support, e.g. parameter is passed + // by const reference, so some copying is unavoidable, however + // this resembles how most of the handlers are currently implemented. + DispatchProcess::run<DispatchProcess::Movable>("Movable", repeats); + DispatchProcess::run<DispatchProcess::Copyable>("Copyable", repeats); +}