Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schedule.h|cpp refactored to class templates for performance and reuse #6902

Open
wants to merge 40 commits into
base: master
from

Conversation

@dok-net
Copy link
Contributor

dok-net commented Dec 12, 2019

The interrupt/signal safe linked list in Schedule.h|cpp can very well be used as a universal queue or event delegate (c# terminology ;-) ) class.
This PR also supersedes cores/esp8266/CallbackList.h.

The new Delegate.h class template provides compelling performance improvements in cases where std::function is repeatedly constructed during execution, which is quite slow. While a simple seek&replace drop-in for std::function in APIs, call sites can in turn be refactored; basically, the binding via lambda capture or std::bind is substituted for by using the C-function pointer style Delegate constructor's obj argument.

Performance comparisons:

Using C function pointer or std::function

One assignment and one call via bool(*f)(Foo*, int) : 108 cycles; call only: 31 cycles.
One assignment and one call via std::function<bool(int)> : 1360 cycles; call only: 34 cycles.

Using Delegate<bool(int), Foo*>

One assignment from bound C++-style Callable and one call: 1435 cycles; call only: 46 cycles.
One assignment from C-function pointer plus obj, and one call: 151 cycles; call only: 30 cycles.

@hreintke

This comment has been minimized.

Copy link
Contributor

hreintke commented Dec 12, 2019

@dok-net :
How should I use MultiDelegate as replacement for CallBackList ?

@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch from 560d917 to e35d6fb Dec 12, 2019
@dok-net

This comment has been minimized.

Copy link
Contributor Author

dok-net commented Dec 12, 2019

@hreintke I've tried for a few minutes to wrap Multidelegate as the experimental::CBListImplementation::CallBackList, but I think there's no need to mimic the interface exactly - right?
To get started on what you functionally need, I believe the two specializations used in Schedule.h|cpp provide that right match - execute exactly once, or forever until some condition applies, upon which the callback signals via it's false return value. So instead of resetting the std::shared_ptr returned from add, give the actual Delegate instances access to a corresponding predicate and have them return that.

struct SomeClass {
    void doSomething(int) const {};
    bool active = true; // for event handling sample
};
SomeClass obj;
// the function type void(int) passes an int value to all callbacks:
using CallbackOnceFuncT = Delegate<void(int), SomeClass>;
using CallOnceQueue = MultiDelegate< CallbackOnceFuncT, true >;
CallOnceQueue q;
q.add([obj](int i) { obj.doSomething(i); }); // construction very expensive.
// fast add, 712 fewer CPU cycles:
q.add({ [](SomeClass obj, int i) { obj.doSomething(i); }, obj });
// the function type bool(int) passes an int value to all event handlers.
// return false from the event handler to get it removed from the event multiplexer:
using EventHandlerFuncT = Delegate<bool(int), SomeClass>;
using EventHandlers = MultiDelegate< EventHandlerFuncT >;
EventHandlers ev;
ev.add([obj](int i) { obj.doSomething(i); return obj.active; }); // construction very expensive.
// fast add, 670 fewer CPU cycles:
ev.add({ [](SomeClass obj, int i) { obj.doSomething(i); return obj.active; }, obj });
q(42);
ev(42);

Instead of the active field in SomeClass, you might instead derive from EventHandlerFuncT and keep and retrieve that "remove" flag there - see Schedule.cpp's class mRecFuncT : public Delegate<bool(), void*>, which overwrites bool operator()().

@hreintke

This comment has been minimized.

Copy link
Contributor

hreintke commented Dec 13, 2019

@dok-net :
First impression, did not get into all details of your remark.

Main functionality of CallBacklist is that it prevents executing the callback when the Object that registered the callback is already destructed.
Because of that, the deletion from the list cannot be dependent on returning false.
When the returned CallBackHandler add(cbFunctionT af, bool ad = true) goes out of scope, the callback is deleted before executing it.

Do not (yet) see that possibility in (Multi)Delegate.
Maybe the std::list<CallBackHandler> callBackEventList; can be replaced my MultiDelegate but need to get more in the details to be sure.

@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch 4 times, most recently from 6680cae to ff4bdd6 Dec 13, 2019
@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch 5 times, most recently from 36b3353 to e7a2b49 Dec 20, 2019
@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch from e7a2b49 to 76eb453 Jan 18, 2020
@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch from 76eb453 to 2c11ba9 Jan 19, 2020
@dok-net dok-net force-pushed the dok-net:schedule_w_delegate branch from 2c11ba9 to ad94655 Jan 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.