Re: [boost] first steps to submitting - boost :: observers
On 09/17/2016 08:10 PM, Robert McInnis wrote:
What are the advantages of this library over Boost.Signals2 [1]?
Same question with regards to the Boost.Synapse [2] proposal.
My impression from a brief look at your repository is that you need to
focus more on thread-safety.
Is there any way to avoid the overhead of thread synchronization in a
single-threaded application?
Observers are invoked while the mutex is locked, so observers cannot
make calls on the subject.
How does the lock-free mutex perform, especially when congested?
Why is the lock-free mutex recursive?
There are several member variables that are used as if they are atomic
but they are not declared as such, e.g. Subject::_block, Numeric<T>::_x
(think T == long long.)
The Scope template can be replaced by lock_guard if you model the
LockFreeMutex after the BasicLockable concept [3].
The thread_self() and thread_pid() functions can be replaced by
this_thread::get_id().
Replace the hAtomic macro with a type alias.
Use <cstdint> instead of
On Sunday, September 18, 2016 6:50 AM, Bjorn Reese wrote:
What are the advantages of this library over Boost.Signals2 [1]?
Afaik, Signals2 is not meant to be associated with a single instance of a single object representing a particular event produced by that object.
Same question with regards to the Boost.Synapse [2] proposal.
I do not have boost::Synapse in 1.61. I did find it searching the site. Looking through its documentation, it seems to allow for the production of events within the object instance as well as producing numerous types of events, so that would be similar. I am unfamiliar with how it is implemented as the emitter seems to have to match the observer and if done on each trigger, that would be very costly. This also implies there is a large container of signals, indexed by source object ptr.
My impression from a brief look at your repository is that you need to focus more on thread-safety.
I believe all list manipulations have been protected, though I will go back and check. Specifying the template types as atomic will be updated.
Is there any way to avoid the overhead of thread synchronization in a single-threaded application?
The lock-free mechanism is as low overhead as can be without compiling out the mutex locks. I could add a compiler wrapper for single thread if you know a standard compiler wrap to use (ie: __SINGLE_THREAD, but that doesn't exist)
Observers are invoked while the mutex is locked, so observers cannot make calls on the subject.
Readers may read the value but updaters are restricted. Which is as it should be.
How does the lock-free mutex perform, especially when congested?
Under tests, it performs better than the standard mutex, as is expected. Keeping updates while locked to nonblocking/quick operations will keep the spin to a minimum.
Why is the lock-free mutex recursive?
There are several member variables that are used as if they are atomic but
By using the thread-id as the lock value, there can be situations where my own thread could try to update the same value again. Instead of dead locking, I allow for it to continue, which normally won't trigger a callback due to the value not being different. If the value did change, then the rule logic is circular and should be corrected by the developer. they are not declared
as such, e.g. Subject::_block, Numeric<T>::_x (think T == long long.)
Gotcha. I've made the change and will check it in.
The Scope template can be replaced by lock_guard if you model the LockFreeMutex after the BasicLockable concept [3].
The thread_self() and thread_pid() functions can be replaced by
done. this_thread::get_id(). can't really do that as I need to use the id as a value to set and compare. The thread_id returned by get_id is an object that doesn't allow for retrieval of the id.
Replace the hAtomic macro with a type alias. Use <cstdint> instead of
to reduce polution of the global namespace.
done and done. [1] http://www.boost.org/doc/html/signals2.html [2] http://zajo.github.io/boost-synapse/ [3] http://en.cppreference.com/w/cpp/concept/BasicLockable _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 19/09/2016 10:53, Robert McInnis wrote:
On Sunday, September 18, 2016 6:50 AM, Bjorn Reese wrote:
What are the advantages of this library over Boost.Signals2 [1]?
Afaik, Signals2 is not meant to be associated with a single instance of a single object representing a particular event produced by that object.
I'm not sure I'm parsing that explanation correctly, but Signals2 is a generic thread-safe event-raising mechanism; it can thus be used to raise events from a single instance or static class-based, as desired. Typically the observee/publisher provides the event/signal, but other models can be possible with some finagling, if such is desired -- and it's fairly straightforward to build hybrid patterns with it such as subscribing to a container to indirectly subscribe to the container's contents (assuming that the container tracks insert/remove actions). It's also trivial to do things like give it a different mutex type, so you can have your atomic spinlock mutexes if you wish. (I usually do this myself.)
Observers are invoked while the mutex is locked, so observers cannot make calls on the subject.
Readers may read the value but updaters are restricted. Which is as it should be.
That's debatable, and imposes some limitations that can be irritating in practice. Signals2 does not have this restriction.
On Sunday, September 18, 2016 9:54 PM Gavin Lambert said:
I'm not sure I'm parsing that explanation correctly, but Signals2 is a generic thread-safe event-raising mechanism; it can thus be used to raise events from a single instance or static class-based, as desired.
Typically the observee/publisher provides the event/signal, but other models can be possible with some finagling, if such is desired -- and it's fairly straightforward to build hybrid patterns with it such as subscribing to a container to indirectly subscribe to the container's contents (assuming that the container tracks insert/remove actions).
One of the examples I provided was a stock feed/portfolio management example. The 'market' consisted of just 16 stock symbols and their associated market price when I was making the example. 1000 portfolios were generated with 5-16 buy orders initially pushed (assume 10 positions on average). Any buy orders would also be tracked and would also force an update of the associated portfolio. I then generated 2000 random StockTicks and pushed them thru the market individually, simulating a ticker plant. This would trigger the necessary updates only to those portfolios containing the stock, as well as updating the overall portfolio value. This example took about 20 minutes to code, with a few more minutes to tweak the output to make it more appealing. How would you do that with the Signals2 object?
On 19/09/2016 14:22, Robert McInnis wrote:
One of the examples I provided was a stock feed/portfolio management example.
The 'market' consisted of just 16 stock symbols and their associated market price when I was making the example.
1000 portfolios were generated with 5-16 buy orders initially pushed (assume 10 positions on average). Any buy orders would also be tracked and would also force an update of the associated portfolio.
I then generated 2000 random StockTicks and pushed them thru the market individually, simulating a ticker plant. This would trigger the necessary updates only to those portfolios containing the stock, as well as updating the overall portfolio value.
This example took about 20 minutes to code, with a few more minutes to tweak the output to make it more appealing.
How would you do that with the Signals2 object?
The same way you'd do it with any other event-based (or pub/sub) system. Presumably a Stock would define a MarketPriceChanged signal and the Portfolios would subscribe to that signal for each of their contained stocks. Buy orders and stock ticks would trigger a change in the market price, which would raise the signal for the affected stocks.
On Sun, Sep 18, 2016 at 3:53 PM, Robert McInnis
On Sunday, September 18, 2016 6:50 AM, Bjorn Reese wrote:
What are the advantages of this library over Boost.Signals2 [1]?
Afaik, Signals2 is not meant to be associated with a single instance of a single object representing a particular event produced by that object.
Same question with regards to the Boost.Synapse [2] proposal.
I do not have boost::Synapse in 1.61. I did find it searching the site. Looking through its documentation
Yes, Synapse is not an official Boost library (pending review). Here is a short tutorial:http://zajo.github.io/boost-synapse/Tutorial.html.
it seems to allow for the production of events within the object instance as well as producing numerous types of events, so that would be similar. I am unfamiliar with how it is implemented as the emitter seems to have to match the observer and if done on each trigger, that would be very costly. This also implies there is a large container of signals, indexed by source object ptr.
In Synapse signals are types, the dispatch by signal is done at compile time. The run-time lookup is only within connections of the same type. So, emit<S>(pe), where S is a signal type and 'pe' is a raw pointer, searches only within S connections for a connection to an emitter with address equal to 'pe'. The lookup is necessary because of the non-intrusive nature of Synapse: any object of any type whatsoever can be used as an emitter. Performance is a priority for me, however it is pointless to discuss performance without a practical use case. In my experience the connected user function (the user code that is executed within emit<>(pe) for each connection to the passed emitter 'pe') is usually significantly heavier than the Synapse machinery. Emil
On Monday, September 19, 2016 1:16 AM Emil Dotchevski said:
In Synapse signals are types, the dispatch by signal is done at compile time. The run-time lookup is only within connections of the same type. So, emit<S>(pe), where S is a signal type and 'pe' is a raw pointer, searches only within S
connections for a connection to an emitter with address equal to 'pe'. The lookup is necessary because of the non-intrusive nature of Synapse: any object of any type whatsoever can be used as an emitter.
Performance is a priority for me, however it is pointless to discuss
without a practical use case. In my experience the connected user function (the user code that is executed within emit<>(pe) for each connection to the
I have designed systems with 10s of thousands of observable components being observed by thousands of other objects, which in turn may end up triggering events to alert other objects. As such, allowing the instance to emit the signal to be observed allows for a dependency driven update. Depending on the situation, this could radically cut down on the number of object instances that would need to be notified. In some cases, where a displayed item is updated very often (more than 50 updates/sec), I would trigger an 'update pending' call that would bundle the updates... thereby reducing The UI updates to whatever I wish (many times less than 20 updates/sec) performance passed
emitter 'pe') is usually significantly heavier than the Synapse machinery.
Stock portfolios are a good test of data driven performance. There are tens of thousands of observable objects (stocks) being observed by potentially thousands of objects. Once triggered, the update would need to update a value roll up of the portfolios updated worth. Once wired, the system should be able to push at least 2000 stock updates per second. Using the subject/observer patterns as I've implemented them allows for dependency driven updates and a fairly small memory overhead. It also allows for unrelated observers to hook into the notification without much of a hassle.
participants (4)
-
Bjorn Reese
-
Emil Dotchevski
-
Gavin Lambert
-
Robert McInnis