On Jan 20, 2013, at 10:27 AM, Ben Coman wrote:

> 
> I am considering a use case where a complex UI rendering loop is linked to an 
> Announcement, which is being fired rapidly (eg < 1ms), and overall system 
> performance suffers. However the UI rendering loop really only needs to 
> execute every 100ms since that is sufficient for a user to perceive an 
> immediate response to their action.  So I have rolled-my-own way to achieve 
> this for which I was looking for some feedback...
> 
> 1. Is there any existing feature in Pharo I have missed that can limit the 
> firing rate of an announcement ?

not that I know :)

> 2. Would this be useful to others as a feature to ship with Pharo ? (so I 
> don't have to roll-my-own, and it gets more experienced eyes to ensure its 
> right :) )

> 3. General comments on my approach, improvements, alternatives.
> 
> By way of a use case using Workspace, execute...
> -------------
> | announcer mainCount  uiCount |
> announcer := Announcer new. "RateLimitingAnnouncer new."
> mainCount := uiCount := 0.
> Transcript crShow: 'ui' ; tab; show: 'main' .
> announcer subscribe: AnnouncementMockA do:
> [     uiCount := uiCount + 1.
>   "Imagine a longer duration UI rendering loop here"
>   Transcript crShow: uiCount asString , '       ' , mainCount asString.
> ].
> [    10000 timesRepeat:
>   [    mainCount := mainCount + 1.
>       "(Delay forMilliseconds: 1 ) wait."
>        announcer announce: AnnouncementMockA new.       ]
> ] timeToRun .
> -------------
> With 'Announcer' this takes 10 seconds to execute with the last lines being...
> 9999       9999
> 10000       10000
> 
> Replacing 'Announcer' with my own 'RateLimitingAnnouncer' listed below, this 
> takes 20 milliseconds with the last lines being...
> ui    main
> 1       1
> 2       10000
> 
> Uncommenting the "Delay" takes 30 seconds for Announcer - and 15 seconds for 
> RateLimitingAnnouncer with last lines of...
> 289       9935
> 290       10000
> 
> 
> Here is my code wrapping #announce: and #initialize...
> -----
> Announcer subclass: #RateLimitingAnnouncer
>   instanceVariableNames: 'maxRateMilliSeconds queuedCounts'
>   classVariableNames: ''
>   poolDictionaries: ''
>   category: 'LEKtrek-Core'
> -----
> RateLimitingAnnouncer >>initialize
>   super initialize .
>   maxRateMilliSeconds := 100.
>   queuedCounts := Dictionary new.
> -----
> RateLimitingAnnouncer >>announce: anAnnouncement
>   | announcementClass |
> 
>   "Track how many announcements fired since reset at end of forked Delay"
>   announcementClass := anAnnouncement class.
>   queuedCounts at: announcementClass
>       ifPresent: [ :count | queuedCounts at: announcementClass put: count + 1 
> ]
>       ifAbsent: [ queuedCounts at: announcementClass put: 1 ].
> 
>   "At first announcement since Delay'ed reset, forward this one and set up 
> Delay to condense subsequent ones. "       ( (queuedCounts at: 
> announcementClass) = 1 ) ifTrue:
>   [          [    "fire one announcement only for any announcement arriving 
> within delay period."
>           (Delay forMilliseconds: maxRateMilliSeconds) wait.              [ 
> (queuedCounts at: announcementClass) > 1 ] whileTrue:            [    "At 
> least one announcement arrived before end of Delay. Forward one announcement 
> only and repeat"                  queuedCounts at: announcementClass put: 1.
>               super announce: anAnnouncement.
>               (Delay forMilliseconds: maxRateMilliSeconds) wait.              
> ].
>           queuedCounts at: announcementClass put: 0.
>       ] fork.          ^ super announce: anAnnouncement.          ].
> -----
> 
> cheers -ben
> 
> 


Reply via email to