guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] Add atomic-box-update! function to (ice-9 atomic)


From: Maxime Devos
Subject: Re: [PATCH v2] Add atomic-box-update! function to (ice-9 atomic)
Date: Tue, 22 Aug 2023 19:51:45 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.13.0



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.

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))))

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]

Best regards,
Maxime Devos.

Attachment: OpenPGP_0x49E3EE22191725EE.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]