[Signals] Named slot group usage?

I'm trying to determine how widely used named slot groups are in the Signals library. I would appreciate it if I could get some feedback on if they're used, how they're used, etc. In particular: (1) Do you use named slot groups? (2) Did the FIFO ordering guarantees in 1.32.0 and the ability to add to the front or back of the slot list alleviate the need for named slot groups? (3) If performance of connection/disconnection of named slot groups were to get worse (e.g., linear in the number of slots), would it seriously impact the performance of your application? Responses should probably come to me, personally, unless they would be of interest to the group. Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed. Thanks, Doug

Douglas Gregor wrote:
Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed.
Are you sure that this is the case? Last time I looked, alternatives beat Boost.Signals because they don't handle the corner cases of slot disconnection during signal invocation. Example: R operator()(void) const { typename list::iterator i = list_.begin(); while (i != list_.end()) { if (i->function_) { (i++)->function_(); } else { i = list_.erase(i); } } } (from Jody Hagins's implementation) AFAICS this doesn't work if (i++)->function_() disconnects *(i+1). To handle disconnection correctly, one needs to invoke a temporary list<>. I haven't looked at what boost::signal does, but this may account for the dynamic allocations that have been reported. Of course named slot groups may introduce additional overhead, I don't know.

On Feb 11, 2005, at 9:51 AM, Peter Dimov wrote:
Douglas Gregor wrote:
Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed.
Are you sure that this is the case?
There may be other issues, but going from a std::list to a std::map (as needed to efficiently support connection of named slots) hurts iteration performance and increases executable code size. To counter the latter problem, I've done some type erasure that slows it down further and causes some of the extra heap allocations :(
To handle disconnection correctly, one needs to invoke a temporary list<>. I haven't looked at what boost::signal does, but this may account for the dynamic allocations that have been reported.
It's not quite that bad in Signals; there's a boolean flag in each connection that checks whether the slot has been disconnected. If we're in the process of invoking slots (recursively), disconnection merely marks the slot as having been removed. Once the top-level signal invocation completes, it scans the slot list to remove any slots that were marked as disconnected. This does hurt iteration performance (because we have to skip over disconnected slots) and slots down the top-level invocation slightly (because the list is rescanned). Doug

Doug Gregor wrote:
On Feb 11, 2005, at 9:51 AM, Peter Dimov wrote:
Douglas Gregor wrote:
Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed.
Are you sure that this is the case?
There may be other issues, but going from a std::list to a std::map (as needed to efficiently support connection of named slots) hurts iteration performance and increases executable code size. To counter the latter problem, I've done some type erasure that slows it down further and causes some of the extra heap allocations :(
You mean disconnection of named slots, right? List connection is O(1), map - O(log N). You get ordering for free with a map, sure, but this is a separate issue. Anyway, I'm sure that few people would miss named slots. Lets' drop them. :-)

On Feb 11, 2005, at 11:49 AM, Peter Dimov wrote:
Doug Gregor wrote:
On Feb 11, 2005, at 9:51 AM, Peter Dimov wrote:
Douglas Gregor wrote:
Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed.
Are you sure that this is the case?
There may be other issues, but going from a std::list to a std::map (as needed to efficiently support connection of named slots) hurts iteration performance and increases executable code size. To counter the latter problem, I've done some type erasure that slows it down further and causes some of the extra heap allocations :(
You mean disconnection of named slots, right? List connection is O(1), map - O(log N). You get ordering for free with a map, sure, but this is a separate issue.
Both connection and disconnection of named slots are O(lg N), since we need to keep the whole list ordered when named slots are present. Connection/disconnection of unnamed slots is O(1) now. Iterating through a map really is slow, though... loops in operator++ really hurt us. I've tried a few times to come up with some ultra-slick formulation using a list for primary storage and a map indexing into the list... but I've always failed to deal with the iterator invalidation issues.
Anyway, I'm sure that few people would miss named slots.
That's what I'm trying to figure out; but I suspect that you're right.
Lets' drop them. :-)
You're not the only one to say that :) Doug

On Fri, 11 Feb 2005 13:34:05 -0500, Doug Gregor <dgregor@cs.indiana.edu> wrote:
Iterating through a map really is slow, though... loops in operator++ really hurt us. I've tried a few times to come up with some ultra-slick formulation using a list for primary storage and a map indexing into the list... but I've always failed to deal with the iterator invalidation issues.
Can't multi-index help you here? Bruno

On Fri, 11 Feb 2005 16:51:48 +0200 "Peter Dimov" <pdimov@mmltd.net> wrote:
Last time I looked, alternatives beat Boost.Signals because they don't
handle the corner cases of slot disconnection during signal invocation.
Example:
R operator()(void) const { typename list::iterator i = list_.begin(); while (i != list_.end()) { if (i->function_) { (i++)->function_(); } else { i = list_.erase(i); } } }
(from Jody Hagins's implementation)
AFAICS this doesn't work if (i++)->function_() disconnects *(i+1).
To handle disconnection correctly, one needs to invoke a temporary list<>. I haven't looked at what boost::signal does, but this may account for the dynamic allocations that have been reported.
That implementation was a very first try at something for comparison purposes. Because of the performance differences, I modified that code a bit and added a few pieces of functionality, one of which is the ability to replace a slot and/or disconnect a slot while it is being handled. The performance difference was measurable, but very small. A copy of the list is not necessary to implement this feature.

Douglas Gregor wrote:
Background: Signals performance is sub-par, and much of the problem can be traced back to support for named slot groups. I'm trying to determine the best way to proceed.
I would be surprised if the poor memory performance is caused by the named slot groups (from what tiny bit I know about it). Of course the speed performance is just as important. FWIW our company makes no use of the named slot groups. I'm very much looking forward to your changes (I'm currently using a sigc that's been hacked to have (mostly) the same interface as boost)! Thanks, Neal
participants (6)
-
Bruno MartÃnez Aguerre
-
Doug Gregor
-
Douglas Gregor
-
Jody Hagins
-
Neal Coombes
-
Peter Dimov