> Am 30.08.2016 um 06:38 schrieb Stephan Eggermont <[hidden email]>: > > 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 <[hidden email]>:
|
2016-08-30 9:44 GMT+02:00 Denis Kudriashov <[hidden email]>:
Interesting is slots allow to implement such pattern? I imagine AnnouncerSlot which will generate "read code" specifically depending on following message send. |
In reply to this post by Denis Kudriashov
2016-08-30 9:44 GMT+02:00 Denis Kudriashov <[hidden email]>:
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 <[hidden email] > <mailto:[hidden email]>>: > > Hi > > 2016-08-30 8:52 GMT+02:00 Norbert Hartl <[hidden email] > <mailto:[hidden email]>>: > > 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 <[hidden email]>:
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 <[hidden email]>:
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 <[hidden email] > <mailto:[hidden email]>>: > > > 2016-08-30 10:21 GMT+02:00 Guille Polito > <[hidden email] <mailto:[hidden email]>>: > > 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 > |
In reply to this post by Denis Kudriashov
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 <[hidden email]>:
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!
|
In reply to this post by Thierry Goubier
2016-08-30 10:35 GMT+02:00 Thierry Goubier <[hidden email]>:
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 <[hidden email]>:
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 |
In reply to this post by Denis Kudriashov
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 signature.asc (859 bytes) Download Attachment |
2016-08-30 16:01 GMT+02:00 Henrik Johansen <[hidden email]>:
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
|
> On 30 Aug 2016, at 5:16 , Thierry Goubier <[hidden email]> 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 signature.asc (859 bytes) Download Attachment |
2016-08-30 17:36 GMT+02:00 Henrik Johansen <[hidden email]>:
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
|
In reply to this post by Henrik Sperre Johansen
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 >> <[hidden email]> 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é
|
2016-08-31 10:10 GMT+02:00 Glenn Cavarlé <[hidden email]>:
Hi Glenn
Problem that concurrent modification of OrderedCollection could just fail. You will got debugger.
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?
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 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.
|
Hi Denis,
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). 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. 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. 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. 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é
|
Free forum by Nabble | Edit this page |