On 2023-08-22 19:51, Maxime Devos wrote: > Op 22-08-2023 om 12:59 schreef Andrew Tropin: >> >> * module/ice-9/atomic.scm (atomic-box-update!): New variable. >> --- >> Changes since v1. Use single-argument proc to avoid potential perfomance >> problems cause of call to apply. >> >> module/ice-9/atomic.scm | 14 +++++++++++++- >> 1 file changed, 13 insertions(+), 1 deletion(-) >> >> diff --git a/module/ice-9/atomic.scm b/module/ice-9/atomic.scm >> index 2a8af901d..6bfa2e8ee 100644 >> --- a/module/ice-9/atomic.scm >> +++ b/module/ice-9/atomic.scm >> @@ -25,7 +25,8 @@ >> atomic-box-ref >> atomic-box-set! >> atomic-box-swap! >> - atomic-box-compare-and-swap!)) >> + atomic-box-compare-and-swap! >> + atomic-box-update!)) >> >> (eval-when (expand load eval) >> (load-extension (string-append "libguile-" (effective-version)) >> @@ -36,3 +37,14 @@ >> (add-interesting-primitive! 'atomic-box-set!) >> (add-interesting-primitive! 'atomic-box-swap!) >> (add-interesting-primitive! 'atomic-box-compare-and-swap!)) >> + >> +(define (atomic-box-update! box proc) >> + "Atomically updates value of BOX to (PROC BOX-VALUE), returns new >> +value. > > * , returns new value -> and returns the new value > > While descriptive works, for consistency with other atomics > documentation, this imperative procedure needs to be documented in the > imperative mood: > > Atomically update the value of BOX to (PROC BOX-VALUE) and return the > new value. Went this way, thank you. > > Alternatively, you can adjust the other documentation of atomics to the > indicative mood and preserve the original docstring (except for the > grammar correction mentioned in the beginning). > > PROC may be called multiple times, and thus PROC should be >> +free of side effects." > + (let loop () >> + (let* ((old-value (atomic-box-ref box)) >> + (new-value (proc old-value))) >> + (if (eq? old-value (atomic-box-compare-and-swap! box old-value new-value)) >> + new-value >> + (loop))))) > > This can be optimised, by using the return value of CAS as the new old > value instead of calling atomic-box-ref again: > > (let loop ((old-value (atomic-box-ref box))) > (let* ((new-value (proc new-value)) > (new-old-value (atomic-box-compare-and-swap! box old-value > new-value))) > (if (eq? new-old-value old-value) > new-value > (loop new-old-value)))) Make sense, updated the implementation. > Maybe there is some concurrency weirdness that can cause slower > iterations to reduce the number of iterations (*); I'm assuming this > isn't the case here. But if it is, in fact, the case here, and the goal > is to exploit this effect, I would think it's better to explicitly > implement the back-off. > > (I haven't benchmarked any of this, I'm purely going by the number of > operations.) > > (*) E.g., from Wikipedia: > https://en.wikipedia.org/w/index.php?title=Compare-and-swap&oldid=1141385700 > > [...] Instead of immediately retrying after a CAS operation fails, > researchers have found that total system performance can be improved in > multiprocessor systems—where many threads constantly update some > particular shared variable—if threads that see their CAS fail use > exponential backoff—in other words, wait a little before retrying the > CAS.[4] Sent v3. -- Best regards, Andrew Tropin