About the non-use of Announcer in Bloc
Hi all, Doru, Stephan, Norbert, Denis and me spoke at ESUG about the non-use of Announcer in Bloc. I made some test cases in Bloc-Tests to compare performances between Announcer and BlEventRegistry. The result is that Announcer is at least 5x slower in all tested cases. Bloc has only specific needs about event dispatching, the first one is the efficiency during event propagation. It may be interesting to investigate why Announcer is slower and also what are the uncovered cases in BlEventRegistry. So, i'm interested in continuing our discussion about that. Regards, Glenn. ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Probably from the use of critical (mutex), the way #subscriptionsHandling: is done, and that Announcer is built for X number of announcers and Y number of subscribers, which means that each announcement the announcer has to filter and find the correct subscribers. ---- | ann a | ann := Announcer new. ann when: Announcement do: [ :e ]. a := Announcement new. [[ ann announce: a ] bench ] timeProfile ----- Other drawbacks to consider when using announcements: Memory overhead from the Announcer object and each subscription. Memory ownership, the announcement subscriptions will strongly reference the announcement target unless the announcement is weakly, which can lead to memory leakage especially if strong and weak subscriptions are mixed in certain situations (see the mailing list from around the release of Pharo 6). This also leads to code where the announcer is the one who owns the target (multiple times if there are different announcements), which makes understanding who references what etc.. much harder to understand. In many places the control/message flow, and the relationship between the different parts of a bigger object tends to become much more difficult to understand (For example Rubric). Announcer usage shines when there is a need for a common place that X objects can communicate Y messages to Z recipients without needing to know who sends what, or who wants to receive what (For example System Announcements). Communication when only one Object "A" announces to another Object(s) "B", is better implemented as A referencing B, then B implements the necessary methods needed for this communication. Best regards, Henrik -----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of Glenn Cavarlé Sent: Sunday, August 28, 2016 9:11 PM To: pharo-dev@lists.pharo.org Subject: [Pharo-dev] About the non-use of Announcer in Bloc Hi all, Doru, Stephan, Norbert, Denis and me spoke at ESUG about the non-use of Announcer in Bloc. I made some test cases in Bloc-Tests to compare performances between Announcer and BlEventRegistry. The result is that Announcer is at least 5x slower in all tested cases. Bloc has only specific needs about event dispatching, the first one is the efficiency during event propagation. It may be interesting to investigate why Announcer is slower and also what are the uncovered cases in BlEventRegistry. So, i'm interested in continuing our discussion about that. Regards, Glenn. ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Hi henrik thanks for your analysis. I always wanted to deeply understand the difference between announcements and a simple registry mechanism. Now I'm too tired to think (yes I need to sleep a 9h25 :) but I will reread this mail when my brain is back . stef Le 29/8/16 à 00:54, Henrik Nergaard a écrit :
Probably from the use of critical (mutex), the way #subscriptionsHandling: is done, and that Announcer is built for X number of announcers and Y number of subscribers, which means that each announcement the announcer has to filter and find the correct subscribers. ----
| ann a |
ann := Announcer new.
ann when: Announcement do: [ :e ]. a := Announcement new. [[ ann announce: a ] bench ] timeProfile
-----
Other drawbacks to consider when using announcements: Memory overhead from the Announcer object and each subscription. Memory ownership, the announcement subscriptions will strongly reference the announcement target unless the announcement is weakly, which can lead to memory leakage especially if strong and weak subscriptions are mixed in certain situations (see the mailing list from around the release of Pharo 6). This also leads to code where the announcer is the one who owns the target (multiple times if there are different announcements), which makes understanding who references what etc.. much harder to understand. In many places the control/message flow, and the relationship between the different parts of a bigger object tends to become much more difficult to understand (For example Rubric).
Announcer usage shines when there is a need for a common place that X objects can communicate Y messages to Z recipients without needing to know who sends what, or who wants to receive what (For example System Announcements).
Communication when only one Object "A" announces to another Object(s) "B", is better implemented as A referencing B, then B implements the necessary methods needed for this communication.
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of Glenn Cavarlé Sent: Sunday, August 28, 2016 9:11 PM To: pharo-dev@lists.pharo.org Subject: [Pharo-dev] About the non-use of Announcer in Bloc
Hi all, Doru, Stephan, Norbert, Denis and me spoke at ESUG about the non-use of Announcer in Bloc. I made some test cases in Bloc-Tests to compare performances between Announcer and BlEventRegistry. The result is that Announcer is at least 5x slower in all tested cases. Bloc has only specific needs about event dispatching, the first one is the efficiency during event propagation. It may be interesting to investigate why Announcer is slower and also what are the uncovered cases in BlEventRegistry. So, i'm interested in continuing our discussion about that.
Regards, Glenn.
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Hi Glenn, Thanks a lot for this experiment. Could you send us the code snippet so people can play with it? The question is if perhaps we can identify a slimmer announcement support so that people can choose depending on requirements (e.g. parallelism or not). Cheers, Doru -- www.tudorgirba.com "Every thing has its own flow"
On 28 Aug 2016, at 21:11, Glenn Cavarlé <glenn.cavarle@gmail.com> wrote:
Hi all, Doru, Stephan, Norbert, Denis and me spoke at ESUG about the non-use of Announcer in Bloc. I made some test cases in Bloc-Tests to compare performances between Announcer and BlEventRegistry. The result is that Announcer is at least 5x slower in all tested cases. Bloc has only specific needs about event dispatching, the first one is the efficiency during event propagation. It may be interesting to investigate why Announcer is slower and also what are the uncovered cases in BlEventRegistry. So, i'm interested in continuing our discussion about that.
Regards, Glenn.
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
You can load Bloc and show the tests using this script: ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Hi, And the script is? :) Doru
On Aug 29, 2016, at 4:31 PM, Glenn Cavarlé <glenn.cavarle@gmail.com> wrote:
You can load Bloc and show the tests using this script:
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com "Every thing should have the right to be different."
Ha... tag doesn't work... You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Thank's. I try tests for subscribing and what I found is: Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense. Another problem is access protection by mutex (semaphore) which make it 3 times slower. So with OrderedCollection and disabled mutex performance for subscribing is same. I pretty sure that protection is required for Block handlers too. 2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>:
Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About- the-non-use-of-Announcer-in-Bloc-tp4913008p4913088.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
And expenses to deliver announcement caused also by protection and usage of IdentitySet. Also in announcements we first collect all subscriptions with interest to separate collection and only after we iterate and process given announcement. But in Bloc you use #select:thenDo: method instead. It is unsafe because event handlers can be modified during delivering (by producing new subscriptions or by unsubscribing inside handlers). So if you want to address it (and I sure you do) it will lead to same code and performance of announcements. 2016-08-29 18:05 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
Thank's. I try tests for subscribing and what I found is:
Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense.
Another problem is access protection by mutex (semaphore) which make it 3 times slower.
So with OrderedCollection and disabled mutex performance for subscribing is same.
I pretty sure that protection is required for Block handlers too.
2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>:
Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-th e-non-use-of-Announcer-in-Bloc-tp4913008p4913088.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
And now question is why SubscriptionRegistry manages subscriptions as IdentitySet instead of OrderedCollection? 2016-08-29 18:36 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
And expenses to deliver announcement caused also by protection and usage of IdentitySet. Also in announcements we first collect all subscriptions with interest to separate collection and only after we iterate and process given announcement.
But in Bloc you use #select:thenDo: method instead. It is unsafe because event handlers can be modified during delivering (by producing new subscriptions or by unsubscribing inside handlers).
So if you want to address it (and I sure you do) it will lead to same code and performance of announcements.
2016-08-29 18:05 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
Thank's. I try tests for subscribing and what I found is:
Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense.
Another problem is access protection by mutex (semaphore) which make it 3 times slower.
So with OrderedCollection and disabled mutex performance for subscribing is same.
I pretty sure that protection is required for Block handlers too.
2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>:
Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-th e-non-use-of-Announcer-in-Bloc-tp4913008p4913088.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
On 29 Aug 2016, at 6:39 , Denis Kudriashov <dionisiydk@gmail.com> wrote:
And now question is why SubscriptionRegistry manages subscriptions as IdentitySet instead of OrderedCollection?
1) Performance of remove operation is the main reason. Sure, registration has a larger constant overhead than using an OrderedCollection, but it's still a theta(1) operation.Removal however, is theta(1)'ish, while with OrderedCollection, it would be theta(n/2). So for a general implementation where the usage pattern is unknown, it gives more balanced performance considering all use cases. * 2) Lessen chance of user relying on delivery order implied by registration order. And yes, it's possible to create much better performing announcers for specific use patterns, such as a (short-lived) announcer where there's a period of registration, followed by a period of delivery, before eventual garbage collection of the announcer rather than explicit deregistration. Personally, I think the (original) announcer pattern, where subscription collection is copied at registration, then replaced atomically, would be a better alternative. You'd still need a mutex for the copy->add part to keep it thread-safe, but delivery would have much less overhead (no copy needed to avoid mutex guarding against mutation) I'd be *very* wary of adding announcers that do not have thread-safe interaction between registration and delivery, though one suited for registration limited to a single thread may be a valid scenario. Cheers, Henry
2016-08-30 16:01 GMT+02:00 Henrik Johansen <henrik.s.johansen@veloxit.no>:
On 29 Aug 2016, at 6:39 , Denis Kudriashov <dionisiydk@gmail.com> wrote:
And now question is why SubscriptionRegistry manages subscriptions as IdentitySet instead of OrderedCollection?
1) Performance of remove operation is the main reason. Sure, registration has a larger constant overhead than using an OrderedCollection, but it's still a theta(1) operation.Removal however, is theta(1)'ish, while with OrderedCollection, it would be theta(n/2). So for a general implementation where the usage pattern is unknown, it gives more balanced performance considering all use cases. * 2) Lessen chance of user relying on delivery order implied by registration order.
And yes, it's possible to create much better performing announcers for specific use patterns, such as a (short-lived) announcer where there's a period of registration, followed by a period of delivery, before eventual garbage collection of the announcer rather than explicit deregistration. Personally, I think the (original) announcer pattern, where subscription collection is copied at registration, then replaced atomically, would be a better alternative. You'd still need a mutex for the copy->add part to keep it thread-safe, but delivery would have much less overhead (no copy needed to avoid mutex guarding against mutation)
I'd be *very* wary of adding announcers that do not have thread-safe interaction between registration and delivery, though one suited for registration limited to a single thread may be a valid scenario.
The problem I see with that scenario is the risk of code evolution and the fact one must be aware, in the announcement listener, that single threading is to be maintained... and this is out of the scope of the Announcer code itself (or even the object creating the announcer instance). I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case... Regards, Thierry
Cheers, Henry
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's). That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library. Cheers, Henry
2016-08-30 17:36 GMT+02:00 Henrik Johansen <henrik.s.johansen@veloxit.no>:
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Intuitively, I would say that delivery speed would be more important. But I wonder what would be a way to ensure we optimise for the correct usual use. Either way, I agree with you about the interest of another implementation targetting faster delivery. Regards, Thierry
Cheers, Henry
Numbers for the discussion: No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders. Tracing done with Metalinks during one minute. Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements. Thierry Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
Hi all (I was out for some days), What i see is that Bloc EventRegistry: 1) is not thread safe (no mutex + #select:thenDo:) 2) doesn't allow handler removal from the handler list currently used in the thenDo: loop 3) is used specifically for a one-to-many communication (1 BlElement -> X handlers) The (1) doesn't seem to be a problem in Bloc during UI event processing because a new event handler, even if it is added from another thread, would be taken into account for the next event. I note that it is really not recommended (at all) to update an UI element elsewhere than in the UI thread and it is like that in all other UI frameworks i know. The (2) is a very specific use case and i don't know if it is a real need in Bloc because this implies that an element has 2 handlers for the same event and one of them has to remove the other before it is executed and during the same event processing (maybe more a conception issue than a common use case...). In other cases, no problem for handler removal. But maybe i miss some specific cases in (1) and in (2). To me, Announcer is really usefull and efficient when it is used as an event bus that can be shared between multiple objects. For instance, it seems to be useless to use an Announcer in NewValueHolder because it cannot be shared and it is only used internally to register handlers and to propagate only the same event (ValueChanged) to them. What is the gain of using a full featured Announcer in this case? In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps, so we cannot afford to x4 the time to process events even if Announcer is safer and better tested. Regards, Glenn. ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49133... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
2016-08-31 10:10 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>:
Hi all (I was out for some days),
Hi Glenn
What i see is that Bloc EventRegistry: 1) is not thread safe (no mutex + #select:thenDo:) 2) doesn't allow handler removal from the handler list currently used in the thenDo: loop 3) is used specifically for a one-to-many communication (1 BlElement -> X handlers)
The (1) doesn't seem to be a problem in Bloc during UI event processing because a new event handler, even if it is added from another thread, would be taken into account for the next event.
Problem that concurrent modification of OrderedCollection could just fail. You will got debugger.
I note that it is really not recommended (at all) to update an UI element elsewhere than in the UI thread and it is like that in all other UI frameworks i know.
Does it means that I should not subscribe on UI events from different (not UI) processes? How to do this if it is needed? Should I always put subscription code into #defer: message (analogue)? In that case what to do if I need to be sure that I really subscribe on event and only after this continue my business logic process?
The (2) is a very specific use case and i don't know if it is a real need in Bloc because this implies that an element has 2 handlers for the same event and one of them has to remove the other before it is executed and during the same event processing (maybe more a conception issue than a common use case...). In other cases, no problem for handler removal.
Very simple example: I want one shot handler for MouseMove to highlight element once to see that my mouse was moved around. So my handler will highlight target element and unsubscribe it immediately. Problem that if you do it on OrderedCollection during iteration you could skip one of the handlers. And probably some failures are also possible (debugger again). Of course you could say users do not do all of this. But what the alternative? And if people will do this by "incident" it will be very difficult to discover reasons.
But maybe i miss some specific cases in (1) and in (2).
To me, Announcer is really usefull and efficient when it is used as an event bus that can be shared between multiple objects. For instance, it seems to be useless to use an Announcer in NewValueHolder because it cannot be shared and it is only used internally to register handlers and to propagate only the same event (ValueChanged) to them. What is the gain of using a full featured Announcer in this case?
But in practice we never share any announcer instance. We always hide it inside owner object. ValueHolder is not specific example. Also ValueHolder is kind of active model for UI application which could be shared between different views. And when it will be modified in one view another view will be updated automatically.
In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps, so we cannot afford to x4 the time to process events even if Announcer is safer and better tested.
I wondering what the source of this constraint? Does Morphic follows it? I guess not, but all Pharo works quite well with it.
Hi Denis,
Problem that concurrent modification of OrderedCollection could just fail. You will got debugger.
Yes, effectively, if there are multiple threads that try to add an handler on the same element at the same time, this could just fail. To me, it is not a problem, it is just a rule: the scene graph is not thread-safe. When a developper have to do this kind of specific stuff, he has to know that it is really not recommended and that he has to manage concurrency by hand. But yes, he could use #runLater:. Maybe, Bloc will have to provide kind of Task (using TaskIt?) with a specific api to ease ui synchronization using concurrency (later).
In that case what to do if I need to be sure that I really subscribe on event and only after this continue my business logic process?
Weird use case, you mean that sometime your business behavior declared after a subscription needs that the subscription have to be called before continue? Please can you be more explicit? For what i understand, it look like a workaround or a conception issue.
Very simple example: I want one shot handler for MouseMove to highlight element once to see that my mouse was moved around. So my handler will highlight target element and unsubscribe it immediately. Problem that if you do it on OrderedCollection during iteration you could skip one of the handlers. And probably some failures are also possible (debugger again).
Yes, thanks for pointing this issue, i didn't remember that #select:thenDo: uses the index at each iteration. I will update that using (handlers select:[...]) do:[...] instead. Since #select: create a new collection, there should not be any problem.
But in practice we never share any announcer instance. We always hide it inside owner object. ValueHolder is not specific example. Also ValueHolder is kind of active model for UI application which could be shared between different views. And when it will be modified in one view another view will be updated automatically.
I know what is a ValueHolder and how data binding is usefull in GUI. I didn't say that NewValueHolder and Announcer are not good and i don't think that, i just asked myself about why the use of Announcer in NewValueHolder make it 5x slower only to ensure that it is thread-safe while in most cases, all stuff is done in one thread. It is why i didn't use NewValueHolder in Bloc. (I commited tests between NewValueHolder and BlObservableProperty in Bloc-Tests). If performances was not a critical issue in Bloc, obviously i would have used NewValueHolder and Announcer because there are well done and well tested.
I wondering what the source of this constraint? Does Morphic follows it? I guess not, but all Pharo works quite well with it.
Just move you mouse quickly on screen and think about how many MouseMoveEvent are generated by the system. Yes, Morphic follows it. Morphic doesn't use Announcer for UI events and concurrent modifications of a morph seems to be not thread-safe. ----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49134... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Le 31/8/16 à 11:23, Denis Kudriashov a écrit :
2016-08-31 10:10 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com <mailto:glenn.cavarle@gmail.com>>:
Hi all (I was out for some days),
Hi Glenn
What i see is that Bloc EventRegistry: 1) is not thread safe (no mutex + #select:thenDo:) 2) doesn't allow handler removal from the handler list currently used in the thenDo: loop 3) is used specifically for a one-to-many communication (1 BlElement -> X handlers)
The (1) doesn't seem to be a problem in Bloc during UI event processing because a new event handler, even if it is added from another thread, would be taken into account for the next event.
Problem that concurrent modification of OrderedCollection could just fail. You will got debugger.
I note that it is really not recommended (at all) to update an UI element elsewhere than in the UI thread and it is like that in all other UI frameworks i know.
Does it means that I should not subscribe on UI events from different (not UI) processes? How to do this if it is needed? Should I always put subscription code into #defer: message (analogue)? In that case what to do if I need to be sure that I really subscribe on event and only after this continue my business logic process?
The (2) is a very specific use case and i don't know if it is a real need in Bloc because this implies that an element has 2 handlers for the same event and one of them has to remove the other before it is executed and during the same event processing (maybe more a conception issue than a common use case...). In other cases, no problem for handler removal.
Very simple example: I want one shot handler for MouseMove to highlight element once to see that my mouse was moved around. So my handler will highlight target element and unsubscribe it immediately. Problem that if you do it on OrderedCollection during iteration you could skip one of the handlers. And probably some failures are also possible (debugger again).
Of course you could say users do not do all of this. But what the alternative? And if people will do this by "incident" it will be very difficult to discover reasons.
But maybe i miss some specific cases in (1) and in (2).
To me, Announcer is really usefull and efficient when it is used as an event bus that can be shared between multiple objects. For instance, it seems to be useless to use an Announcer in NewValueHolder because it cannot be shared and it is only used internally to register handlers and to propagate only the same event (ValueChanged) to them. What is the gain of using a full featured Announcer in this case?
But in practice we never share any announcer instance.
Really? You lost me on that sentence. SystemAnnouncer current is shared by all the tools no?
We always hide it inside owner object. ValueHolder is not specific example. Also ValueHolder is kind of active model for UI application which could be shared between different views. And when it will be modified in one view another view will be updated automatically.
In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps, so we cannot afford to x4 the time to process events even if Announcer is safer and better tested.
I wondering what the source of this constraint? Does Morphic follows it? I guess not, but all Pharo works quite well with it.
2016-09-01 8:18 GMT+02:00 stepharo <stepharo@free.fr>:
But in practice we never share any announcer instance.
Really? You lost me on that sentence. SystemAnnouncer current is shared by all the tools no?
Yes, you are right. I forgot this big monster :). Now I look at senders of #announcer and there are many places with chain like "aSomebody announcer when:send:..." or also "aSomebody somebodyElse announcer when:send:...". So forgot my words :)
2016-09-01 9:57 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
Really? You lost me on that sentence. SystemAnnouncer current is shared by
all the tools no?
Yes, you are right. I forgot this big monster :). Now I look at senders of #announcer and there are many places with chain like "aSomebody announcer when:send:..." or also "aSomebody somebodyElse announcer when:send:...". So forgot my words :)
But these places are definitely smell.
On 31 Aug 2016, at 10:10 , Glenn Cavarlé <glenn.cavarle@gmail.com> wrote:
In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps
That's only a small part of the picture though, how many listeners are there per each event? And how large do you think the overhead specific to delivery guards is compared to the constraint propagation handlers? On my machine (an old 2011 MBP), the following gives throughput on the order of 300k-600k deliveries per second (very variable, due to the amount of garbage generated from copying subs, I presume) #(1 10 100 1000) collect: [ :listeners | a := Announcer new. b := Object new. "Single message send in handler" listeners timesRepeat: [a when: Announcement do: [ :a | a yourself ] for: b.]. "announcement creation overhead not included" ann := Announcement new. [a announce: ann] bench]. Cheers, Henry And with a registry tuned for delivery, yet maintaining the guards (attached)/exception/error handling properties, 900k -> 2.5M #(1 10 100 1000) collect: [ :listeners | a := Announcer new. b := Object new. a instVarNamed: 'registry' put: DeliveringSubscriptionRegistry new. "Single message send in handler" listeners timesRepeat: [a when: Announcement do: [ :a | a yourself ] for: b.]. "announcement creation overhead not included" ann := Announcement new. [a announce: ann] bench.].
2016-08-31 18:01 GMT+02:00 Henrik Johansen <henrik.s.johansen@veloxit.no>:
On 31 Aug 2016, at 10:10 , Glenn Cavarlé <glenn.cavarle@gmail.com> wrote:
In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps
That's only a small part of the picture though, how many listeners are there per each event? And how large do you think the overhead specific to delivery guards is compared to the constraint propagation handlers?
On my machine (an old 2011 MBP), the following gives throughput on the order of 300k-600k deliveries per second (very variable, due to the amount of garbage generated from copying subs, I presume)
So close to 3 times the performance of entry-level celeron for laptops today (if I take the lowest of the 15" MBP of early 2011).
#(1 10 100 1000) collect: [ :listeners |
a := Announcer new. b := Object new. "Single message send in handler" listeners timesRepeat: [a when: Announcement do: [ :a | a yourself ] for: b.]. "announcement creation overhead not included" ann := Announcement new. [a announce: ann] bench].
Cheers, Henry
And with a registry tuned for delivery, yet maintaining the guards (attached)/exception/error handling properties, 900k -> 2.5M
#(1 10 100 1000) collect: [ :listeners |
a := Announcer new. b := Object new. a instVarNamed: 'registry' put: DeliveringSubscriptionRegistry new. "Single message send in handler" listeners timesRepeat: [a when: Announcement do: [ :a | a yourself ] for: b.]. "announcement creation overhead not included" ann := Announcement new. [a announce: ann] bench.].
Thanks for the numbers. We need those to optimise what really matters :) Thierry
To me, Announcer is really usefull and efficient when it is used as an event bus that can be shared between multiple objects. For instance, it seems to be useless to use an Announcer in NewValueHolder because it cannot be shared and it is only used internally to register handlers and to propagate only the same event (ValueChanged) to them. What is the gain of using a full featured Announcer in this case? I particularly like these two points and I would really like to have a confirmation because in that case this is really a place for improvements.
In Bloc the constraint is to propagate more than 2000 events/second without to decrease fps, so we cannot afford to x4 the time to process events even if Announcer is safer and better tested.
Regards, Glenn.
Hi thierry I think that if we would have a tool to show us the activity I'm quite sure that we would find bugs or mistaken behavior. could you send the scripts you did? Stef Le 30/8/16 à 22:36, Thierry Goubier a écrit :
Numbers for the discussion:
No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute
Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute
Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute
Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders.
Tracing done with Metalinks during one minute.
Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements.
Thierry
Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
Hi Stef, 2016-09-01 8:13 GMT+02:00 stepharo <stepharo@free.fr>:
Hi thierry
I think that if we would have a tool to show us the activity I'm quite sure that we would find bugs
or mistaken behavior.
Yes. I did some checks on Morphic and AltBrowser after getting the numbers, to see if I wasn't doing something wrong with my code. could you send the scripts you did?
Later today. I don't have access now to the laptop where I did it. Thierry
Stef
Le 30/8/16 à 22:36, Thierry Goubier a écrit :
Numbers for the discussion:
No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute
Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute
Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute
Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders.
Tracing done with Metalinks during one minute.
Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements.
Thierry
Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier
<thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
Hi Stef, here is the script I use: | c1 c2 c3 n1 n2 n3 l1 l2 l3 | c1 := c2 := c3 := 0. n1 := (Announcer>>#announce:) ast. n2 := #(subscribe:do: subscribe:send:to: basicSubscribe:) collect: [ :e | (Announcer>>e) ast ]. n3 := #(unsubscribe: removeSubscription:) collect: [ :e | (Announcer>>e) ast ]. l1 := MetaLink new metaObject: [ c1 := c1 + 1 ]; selector: #value. l2 := MetaLink new metaObject: [ c2 := c2 + 1 ]; selector: #value. l3 := MetaLink new metaObject: [ c3 := c3 + 1 ]; selector: #value. [ n1 link: l1. n2 do: [ :e | e link: l2 ]. n3 do: [ :e | e link: l3 ]. (Duration minutes: 1) wait. n1 removeLink: l1. n2 do: [ :e | e removeLink: l2 ]. n3 do: [ :e | e removeLink: l3 ]. { 'announce' -> c1. 'subscribe' -> c2. 'unsubscribe' -> c3 } inspect ] forkAt: Processor userBackgroundPriority [image: Images intégrées 1] 2016-09-01 9:28 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com>:
Hi Stef,
2016-09-01 8:13 GMT+02:00 stepharo <stepharo@free.fr>:
Hi thierry
I think that if we would have a tool to show us the activity I'm quite sure that we would find bugs
or mistaken behavior.
Yes. I did some checks on Morphic and AltBrowser after getting the numbers, to see if I wasn't doing something wrong with my code.
could you send the scripts you did?
Later today. I don't have access now to the laptop where I did it.
Thierry
Stef
Le 30/8/16 à 22:36, Thierry Goubier a écrit :
Numbers for the discussion:
No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute
Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute
Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute
Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders.
Tracing done with Metalinks during one minute.
Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements.
Thierry
Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier
<thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
2016-09-01 10:29 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
Hi Thierry.
2016-09-01 10:21 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com>:
(Duration minutes: 1) wait.
Just to mention our nice language: it could be written as:
1 minutes wait
Thanks. Didn't find that one when searching through the code. Recovering the userBackgroundPriority message by code browsing and search wasn't too obvious either.
Thierry
On 01 Sep 2016, at 10:21, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Stef,
here is the script I use:
| c1 c2 c3 n1 n2 n3 l1 l2 l3 | c1 := c2 := c3 := 0. n1 := (Announcer>>#announce:) ast. n2 := #(subscribe:do: subscribe:send:to: basicSubscribe:) collect: [ :e | (Announcer>>e) ast ]. n3 := #(unsubscribe: removeSubscription:) collect: [ :e | (Announcer>>e) ast ]. l1 := MetaLink new metaObject: [ c1 := c1 + 1 ]; selector: #value. l2 := MetaLink new metaObject: [ c2 := c2 + 1 ]; selector: #value. l3 := MetaLink new metaObject: [ c3 := c3 + 1 ]; selector: #value. [ n1 link: l1. n2 do: [ :e | e link: l2 ]. n3 do: [ :e | e link: l3 ]. (Duration minutes: 1) wait. n1 removeLink: l1. n2 do: [ :e | e removeLink: l2 ]. n3 do: [ :e | e removeLink: l3 ]. { 'announce' -> c1. 'subscribe' -> c2. 'unsubscribe' -> c3 } inspect ] forkAt: Processor userBackgroundPriority
Well, this is really cool code !
<image.png>
2016-09-01 9:28 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com>: Hi Stef,
2016-09-01 8:13 GMT+02:00 stepharo <stepharo@free.fr>: Hi thierry
I think that if we would have a tool to show us the activity I'm quite sure that we would find bugs
or mistaken behavior.
Yes. I did some checks on Morphic and AltBrowser after getting the numbers, to see if I wasn't doing something wrong with my code.
could you send the scripts you did?
Later today. I don't have access now to the laptop where I did it.
Thierry
Stef
Le 30/8/16 à 22:36, Thierry Goubier a écrit :
Numbers for the discussion:
No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute
Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute
Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute
Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders.
Tracing done with Metalinks during one minute.
Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements.
Thierry
Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
this is really cool :) Le 1/9/16 à 10:21, Thierry Goubier a écrit :
Hi Stef,
here is the script I use:
| c1 c2 c3 n1 n2 n3 l1 l2 l3 | c1 := c2 := c3 := 0. n1 := (Announcer>>#announce:) ast. n2 := #(subscribe:do: subscribe:send:to: basicSubscribe:) collect: [ :e | (Announcer>>e) ast ]. n3 := #(unsubscribe: removeSubscription:) collect: [ :e | (Announcer>>e) ast ]. l1 := MetaLink new metaObject: [ c1 := c1 + 1 ]; selector: #value. l2 := MetaLink new metaObject: [ c2 := c2 + 1 ]; selector: #value. l3 := MetaLink new metaObject: [ c3 := c3 + 1 ]; selector: #value. [ n1 link: l1. n2 do: [ :e | e link: l2 ]. n3 do: [ :e | e link: l3 ]. (Duration minutes: 1) wait. n1 removeLink: l1. n2 do: [ :e | e removeLink: l2 ]. n3 do: [ :e | e removeLink: l3 ]. { 'announce' -> c1. 'subscribe' -> c2. 'unsubscribe' -> c3 } inspect ] forkAt: Processor userBackgroundPriority
Images intégrées 1
2016-09-01 9:28 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com <mailto:thierry.goubier@gmail.com>>:
Hi Stef,
2016-09-01 8:13 GMT+02:00 stepharo <stepharo@free.fr <mailto:stepharo@free.fr>>:
Hi thierry
I think that if we would have a tool to show us the activity I'm quite sure that we would find bugs
or mistaken behavior.
Yes. I did some checks on Morphic and AltBrowser after getting the numbers, to see if I wasn't doing something wrong with my code.
could you send the scripts you did?
Later today. I don't have access now to the laptop where I did it.
Thierry
Stef
Le 30/8/16 à 22:36, Thierry Goubier a écrit :
Numbers for the discussion:
No activity, empty desktop: announcements 608/minute subscribe add/remove 9/minute
Activity, AltBrowser: announcements 1109/minute subscribe add/remove 207/minute
Activity, Nautilus: announcements 2488/minute subscribe add/remove 716/minute
Empirically the same processus in both environments: open two system browser, in one, find the Announcer class, browse through a few of the methods, select basicSubscribe: and ask for senders.
Tracing done with Metalinks during one minute.
Not exactly what I would have expected, especially the ratio subscribe add/remove and announcements.
Thierry
Le 30/08/2016 à 17:36, Henrik Johansen a écrit :
On 30 Aug 2016, at 5:16 , Thierry Goubier <thierry.goubier@gmail.com <mailto:thierry.goubier@gmail.com>> wrote:
I have the same concern with an Announcer optimized for certain use cases, in the fact that the announcer creator is the one choosing the 'kind of' optimisation it would target, hoping that the listeners will conform to that use case...
There simply is no silver bullet that will give unbeatable performance in all scenarios; and focusing on improving one facet of the default implementation will inevitably lead to either - the loss of some important property (general thread-safety if removing the mutex protection, ability to unsubscribe in handler if removing the copy operation and extending the delivery mutex protection, etc.) - greatly degenerated performance for another facet (like #remove for OC's).
That said, the current implementation is very geared towards decent, balanced subscribe/unsubscribe performance, at the expense of delivery speed. I've said it before, and still think, that offering at least one other implementation emphasizing delivery speed over subscription/unsubscription performance, in the same way the original implementation did (and/or changing the default Announcer to switch between the two dynamically based on heuristics) *would* be a valuable addition to the general library.
Cheers, Henry
Hi Denis, Thanks a lot for this analysis! In summary, the Bloc events are faster because: 1. they store the subscriptions in OrderedCollection instead of IdentitySet. This is likely something we can improve in Announcements. 2. they are unsafe. For 1., the only reason I can think of is to prevent the user to register twice the same object by mistake. I think this is an optimization that should be the responsibility of the user. For 2. the question is if Bloc really does not need the mutex. Cheers, Doru
On Aug 29, 2016, at 6:36 PM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
And expenses to deliver announcement caused also by protection and usage of IdentitySet. Also in announcements we first collect all subscriptions with interest to separate collection and only after we iterate and process given announcement.
But in Bloc you use #select:thenDo: method instead. It is unsafe because event handlers can be modified during delivering (by producing new subscriptions or by unsubscribing inside handlers).
So if you want to address it (and I sure you do) it will lead to same code and performance of announcements.
2016-08-29 18:05 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>: Thank's. I try tests for subscribing and what I found is:
Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense.
Another problem is access protection by mutex (semaphore) which make it 3 times slower.
So with OrderedCollection and disabled mutex performance for subscribing is same.
I pretty sure that protection is required for Block handlers too.
2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>: Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com "Every successful trip needs a suitable vehicle."
On 29 Aug 2016, at 21:37, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi Denis,
Thanks a lot for this analysis!
In summary, the Bloc events are faster because: 1. they store the subscriptions in OrderedCollection instead of IdentitySet. This is likely something we can improve in Announcements. 2. they are unsafe.
For 1., the only reason I can think of is to prevent the user to register twice the same object by mistake. I think this is an optimization that should be the responsibility of the user.
Alternatively we may use OrderedCollection and just check if it includes the subscription before adding. I think that more events are fired than clients are subscribed per a period of time Uko
For 2. the question is if Bloc really does not need the mutex.
Cheers, Doru
On Aug 29, 2016, at 6:36 PM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
And expenses to deliver announcement caused also by protection and usage of IdentitySet. Also in announcements we first collect all subscriptions with interest to separate collection and only after we iterate and process given announcement.
But in Bloc you use #select:thenDo: method instead. It is unsafe because event handlers can be modified during delivering (by producing new subscriptions or by unsubscribing inside handlers).
So if you want to address it (and I sure you do) it will lead to same code and performance of announcements.
2016-08-29 18:05 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>: Thank's. I try tests for subscribing and what I found is:
Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense.
Another problem is access protection by mutex (semaphore) which make it 3 times slower.
So with OrderedCollection and disabled mutex performance for subscribing is same.
I pretty sure that protection is required for Block handlers too.
2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>: Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com
"Every successful trip needs a suitable vehicle."
2016-08-29 21:50 GMT+02:00 Yuriy Tymchuk <yuriy.tymchuk@me.com>:
For 1., the only reason I can think of is to prevent the user to register twice the same object by mistake. I think this is an optimization that should be the responsibility of the user.
Alternatively we may use OrderedCollection and just check if it includes the subscription before adding. I think that more events are fired than clients are subscribed per a period of time
It looks quite safe to not check anything because subscription items are instances of AnnouncementSubscription which are created internally inside Announcer during subscription
On Aug 29, 2016, at 10:37 PM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2016-08-29 21:50 GMT+02:00 Yuriy Tymchuk <yuriy.tymchuk@me.com>:
For 1., the only reason I can think of is to prevent the user to register twice the same object by mistake. I think this is an optimization that should be the responsibility of the user.
Alternatively we may use OrderedCollection and just check if it includes the subscription before adding. I think that more events are fired than clients are subscribed per a period of time
It looks quite safe to not check anything because subscription items are instances of AnnouncementSubscription which are created internally inside Announcer during subscription
+1 Doru -- www.tudorgirba.com www.feenk.com "Presenting is storytelling."
On 29/08/16 22:37, Denis Kudriashov wrote:
It looks quite safe to not check anything because subscription items are instances of AnnouncementSubscription which are created internally inside Announcer during subscription
We should probably do something special for 0 and 1 subscriptions. Stephan
Am 30.08.2016 um 06:38 schrieb Stephan Eggermont <stephan@stack.nl>:
On 29/08/16 22:37, Denis Kudriashov wrote:
It looks quite safe to not check anything because subscription items are instances of AnnouncementSubscription which are created internally inside Announcer during subscription
We should probably do something special for 0 and 1 subscriptions.
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before. Announcer>>#announce: anAnnouncement | announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too. For the 1 case we could store a single subscription in the registry field. Both subscription and registry have the #deliver: method so this would work out of the box. We could just dispatch the method. So without having measured it myself it could be good if - registry is nil if no subscribers are present. With nil checks the Announcer code could go fast - registry is a subscription in case of 1 subscriber. No need for #subscriptionsHandling: because check is directly on the subscription for #handlesAnnouncement: This should save some cycles and the creation of intermediate collections. With not using the SubscriptionRegistry there is also no #critical: section saving the creation of one. - registry is SubscriptionRegistry in case of >1 subscribers my two cents, Norbert
Hi 2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name>:
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
2016-08-30 9:44 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name>:
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
Interesting is slots allow to implement such pattern? I imagine AnnouncerSlot which will generate "read code" specifically depending on following message send.
2016-08-30 9:44 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
Hi
2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name>:
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
I remember making sure this was the case for Morph instances. If no subscriber, no announcer and make sure announcing something doesn't create the announcer lazily. Thierry
Hi, From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements... But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization? -------- Original Message --------
2016-08-30 9:44 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com <mailto:dionisiydk@gmail.com>>:
Hi
2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name <mailto:norbert@hartl.name>>:
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
I remember making sure this was the case for Morph instances. If no subscriber, no announcer and make sure announcing something doesn't create the announcer lazily.
Thierry
2016-08-30 10:21 GMT+02:00 Guille Polito <guillermopolito@gmail.com>:
Hi,
From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements...
But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization?
I have similar feeling. But maybe Spec is nice example which could improved by such optimizations
2016-08-30 10:28 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-08-30 10:21 GMT+02:00 Guille Polito <guillermopolito@gmail.com>:
Hi,
From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements...
But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization?
I have similar feeling. But maybe Spec is nice example which could improved by such optimizations
No. Announcers are not the problem in Spec Thierry
One Morph <----> One Announcer sounds bad in terms of memory consumption, yes. Maybe it makes more sense something like One Widged <----> One Announcer But the problem is that today the line between Morphs and Widgets is not clearly set. I hope Bloc/Bric helps on this. -------- Original Message --------
2016-08-30 10:28 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com <mailto:dionisiydk@gmail.com>>:
2016-08-30 10:21 GMT+02:00 Guille Polito <guillermopolito@gmail.com <mailto:guillermopolito@gmail.com>>:
Hi,
From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements...
But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization?
I have similar feeling. But maybe Spec is nice example which could improved by such optimizations
No. Announcers are not the problem in Spec
Thierry
2016-08-30 10:35 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com>:
2016-08-30 10:28 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-08-30 10:21 GMT+02:00 Guille Polito <guillermopolito@gmail.com>:
Hi,
From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements...
But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization?
I have similar feeling. But maybe Spec is nice example which could improved by such optimizations
No. Announcers are not the problem in Spec
I just saw that every property in Spec model's is kind of ValueHolder with announcer instance inside. And it is huge number. But I think in case of Spec they are always used. So laziness will not helps here. But optimisation for single subscriber could.
2016-08-30 11:12 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-08-30 10:35 GMT+02:00 Thierry Goubier <thierry.goubier@gmail.com>:
2016-08-30 10:28 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>:
2016-08-30 10:21 GMT+02:00 Guille Polito <guillermopolito@gmail.com>:
Hi,
From the top of my head: I would understand that systems where there are hundreds of thousands of events per second, maybe one does not want to pay the overhead of announcements...
But, How many events are produced from a morph per second? One? Two? Five? Is it really the case of morphs the one that require optimization?
I have similar feeling. But maybe Spec is nice example which could improved by such optimizations
No. Announcers are not the problem in Spec
I just saw that every property in Spec model's is kind of ValueHolder with announcer instance inside. And it is huge number. But I think in case of Spec they are always used. So laziness will not helps here. But optimisation for single subscriber could.
You're right. But I would consider making the value holder itself lazy (leaving it nil unless the model manipulates that property) so that you don't have to subscribe to a property which is never changed (and on the fly subscribe to it if it is lazily created by the model manipulation... could be a tad complex that one). Thierry
Am 30.08.2016 um 09:44 schrieb Denis Kudriashov <dionisiydk@gmail.com>:
Hi
2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name <mailto:norbert@hartl.name>>: I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
That prevents creating an announcer until someone creates the announcer lazily. That does not mean there is a subscription, especially if there are announcements sent. Announcements are such a central component (we made it that way) that every useful optimization should be considered! Norbert
2016-08-30 10:41 GMT+02:00 Norbert Hartl <norbert@hartl.name>:
Am 30.08.2016 um 09:44 schrieb Denis Kudriashov <dionisiydk@gmail.com>:
Hi
2016-08-30 8:52 GMT+02:00 Norbert Hartl <norbert@hartl.name>:
I think for the 0 case we should think about instantiating the SubscriptionRegistry lazily. This would also mitigate the effect that if a lot of announcers are created they create only a single object instead of two until used. Of course this depends on the probability of having a lot of announcers where only a few are used. The #announce: method looks like it has been lazy before.
Announcer>>#announce: anAnnouncement
| announcement | announcement := anAnnouncement asAnnouncement. registry ifNotNil: [ registry deliver: announcement ]. ^ announcement
checks for #ifNotNil: but registry is created in #initialize. So #ifNotEmpty: would be right here but #deliver: checks that, too.
We always hide announcer instance inside owner. So it could instantiate announcer lazily if such optimization really needed.
That prevents creating an announcer until someone creates the announcer lazily. That does not mean there is a subscription, especially if there are announcements sent. Announcements are such a central component (we made it that way) that every useful optimization should be considered!
Agreed. The slightly tricky part is effectively to ensure that sending an announcement doesn't lazily create the announcer. But, overall, the code isn't that complex, so it is worth optimising for it (IMHO). The software engineering issue is that the optimisation is done in the code using the announcer object (for example, Morph) and not in the Announcer instance itself, which would make the optimisation applied everywhere. However, the benefit is that you do it only in places where you think it is necessary, not everywhere for everybody. This optimisation will have a cost for some operations (*), and so it may not be suitable everywhere. Thierry (*) I'm thinking of subscriptions removal there. Useless to optimise that for Morphs, for example (it would be very rare to see an object subscribe to a Morph, then unsubscribe). However, for the system announcer, given the number of objects subscribing / unsubscribing to it, don't try to optimise the no subscribers case!
Norbert
Le 29/8/16 à 21:37, Tudor Girba a écrit :
Hi Denis,
Thanks a lot for this analysis!
In summary, the Bloc events are faster because: 1. they store the subscriptions in OrderedCollection instead of IdentitySet. This is likely something we can improve in Announcements. 2. they are unsafe.
For 1., the only reason I can think of is to prevent the user to register twice the same object by mistake. I think this is an optimization that should be the responsibility of the user. It depends of the contract. Since we want to have a solid architecture then it should not be the responsibility of the client to me.
For 2. the question is if Bloc really does not need the mutex.
Cheers, Doru
On Aug 29, 2016, at 6:36 PM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
And expenses to deliver announcement caused also by protection and usage of IdentitySet. Also in announcements we first collect all subscriptions with interest to separate collection and only after we iterate and process given announcement.
But in Bloc you use #select:thenDo: method instead. It is unsafe because event handlers can be modified during delivering (by producing new subscriptions or by unsubscribing inside handlers).
So if you want to address it (and I sure you do) it will lead to same code and performance of announcements.
2016-08-29 18:05 GMT+02:00 Denis Kudriashov <dionisiydk@gmail.com>: Thank's. I try tests for subscribing and what I found is:
Main problem that subscriptions inside SubscriptionRegistry are managed as IdentitySet which of cause much slower for addition then OrderedCollection. We probably could use OrderedCollection here because items are always created on fly and identitySet has no sense.
Another problem is access protection by mutex (semaphore) which make it 3 times slower.
So with OrderedCollection and disabled mutex performance for subscribing is same.
I pretty sure that protection is required for Block handlers too.
2016-08-29 17:18 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>: Ha... tag doesn't work...
You can load Bloc and show the tests using this script: Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; configuration; loadDevelopment. Gofer it smalltalkhubUser: 'Pharo' project: 'Bloc'; package: 'Bloc-Tests'; load. BlAnnouncerVsBlEventRegistryTest browse
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com
"Every successful trip needs a suitable vehicle."
And could you write current repository? 2016-08-29 16:31 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>:
You can load Bloc and show the tests using this script:
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About- the-non-use-of-Announcer-in-Bloc-tp4913008p4913079.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
Hi, You can find the current Bloc image here: https://ci.inria.fr/moose/job/bloc/lastSuccessfulBuild/artifact/bloc.zip Cheers, Doru
On Aug 29, 2016, at 5:10 PM, Denis Kudriashov <dionisiydk@gmail.com> wrote:
And could you write current repository?
2016-08-29 16:31 GMT+02:00 Glenn Cavarlé <glenn.cavarle@gmail.com>: You can load Bloc and show the tests using this script:
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008p49130... Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com "To lead is not to demand things, it is to make them happen."
Thanks Glenn!!!!!!!!!!!!!!!!!!!!!!!
Hi all, Doru, Stephan, Norbert, Denis and me spoke at ESUG about the non-use of Announcer in Bloc. I made some test cases in Bloc-Tests to compare performances between Announcer and BlEventRegistry. The result is that Announcer is at least 5x slower in all tested cases. Bloc has only specific needs about event dispatching, the first one is the efficiency during event propagation. It may be interesting to investigate why Announcer is slower and also what are the uncovered cases in BlEventRegistry. So, i'm interested in continuing our discussion about that.
Regards, Glenn.
----- Glenn Cavarlé -- View this message in context: http://forum.world.st/About-the-non-use-of-Announcer-in-Bloc-tp4913008.html Sent from the Pharo Smalltalk Developers mailing list archive at Nabble.com.
participants (12)
-
Denis Kudriashov -
Glenn Cavarlé -
Guille Polito -
Henrik Johansen -
Henrik Nergaard -
Norbert Hartl -
Stephan Eggermont -
stepharo -
Sven Van Caekenberghe -
Thierry Goubier -
Tudor Girba -
Yuriy Tymchuk