diff options
author | mtklein <mtklein@chromium.org> | 2015-04-01 08:11:16 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-01 08:11:16 -0700 |
commit | 97312d0f7946991d5c529a1edc53ed676eecb655 (patch) | |
tree | 41a3af5a8c4e88d3b56fb87605021f381c4fd1f6 /src/core | |
parent | 79e378d9b78c3db28eb97ab9adecb5bfddbf52d8 (diff) |
small-object optimization for SkFunction
Anything <= sizeof(void*) will be inlined, avoiding heap allocation.
BUG=skia:
Review URL: https://codereview.chromium.org/1048243002
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/SkFunction.h | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/src/core/SkFunction.h b/src/core/SkFunction.h index 9ec421e2a6..3009593421 100644 --- a/src/core/SkFunction.h +++ b/src/core/SkFunction.h @@ -11,6 +11,7 @@ // TODO: document #include "SkTypes.h" +#include "SkTLogic.h" template <typename> class SkFunction; @@ -23,31 +24,41 @@ public: } template <typename Fn> - explicit SkFunction(Fn fn) : fVTable(GetVTable<Fn>()) { - // We've got a functor. The basic thing we can always do is copy it onto the heap. + explicit SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) > sizeof(void*)), void*) = nullptr) + : fVTable(GetOutlineVTable<Fn>()) { + // We've got a functor larger than a pointer. We've go to copy it onto the heap. fFunction = SkNEW_ARGS(Fn, (fn)); } - ~SkFunction() { fVTable.fDelete(fFunction); } + template <typename Fn> + explicit SkFunction(Fn fn, SK_WHEN_C((sizeof(Fn) <= sizeof(void*)), void*) = nullptr) + : fVTable(GetInlineVTable<Fn>()) { + // We've got a functor that fits in a pointer. We copy it right inline. + SkNEW_PLACEMENT_ARGS(&fFunction, Fn, (fn)); + } + + ~SkFunction() { fVTable.fCleanUp(fFunction); } R operator()(Args... args) { return fVTable.fCall(fFunction, args...); } private: struct VTable { R (*fCall)(void*, Args...); - void (*fDelete)(void*); + void (*fCleanUp)(void*); }; + // Used when fFunction is a function pointer of type R(*)(Args...). static const VTable& GetFunctionPointerVTable() { static const VTable vtable = { [](void* fn, Args... args) { return reinterpret_cast<R(*)(Args...)>(fn)(args...); }, - [](void*) { /* Don't delete function pointers. */ }, + [](void*) { /* Nothing to clean up for function pointers. */ } }; return vtable; } + // Used when fFunction is a pointer to a functor of type Fn on the heap (we own it). template <typename Fn> - static const VTable& GetVTable() { + static const VTable& GetOutlineVTable() { static const VTable vtable = { [](void* fn, Args... args) { return (*static_cast<Fn*>(fn))(args...); }, [](void* fn) { SkDELETE(static_cast<Fn*>(fn)); }, @@ -55,7 +66,25 @@ private: return vtable; } - void* fFunction; // Either a function pointer, or a pointer to a functor. + // Used when fFunction _is_ a functor of type Fn, not a pointer to the functor. + template <typename Fn> + static const VTable& GetInlineVTable() { + static const VTable vtable = { + [](void* fn, Args... args) { + union { void* p; Fn f; } pun = { fn }; + return pun.f(args...); + }, + [](void* fn) { + union { void* p; Fn f; } pun = { fn }; + pun.f.~Fn(); + (void)(pun.f); // Otherwise, when ~Fn() is trivial, MSVC complains pun is unused. + } + }; + return vtable; + } + + + void* fFunction; // A function pointer, a pointer to a functor, or an inlined functor. const VTable& fVTable; // How to call, delete (and one day copy, move) fFunction. }; @@ -65,6 +94,5 @@ private: // - make SkFunction copyable // - emulate std::forward for moveable functors (e.g. lambdas) // - forward args too? -// - implement small-object optimization to store functors inline #endif//SkFunction_DEFINED |