On Friday, January 31, 2025 at 1:58:39 PM UTC-5 Michael Knyszek wrote:
I think I got a little overzealous with my opinions, it wasn't my intent to be particularly prescriptive. I also did not get the impression that you were suggesting the memory model should be different. Bottom line: yes, it's possible to implement but more fragile in Go due to the less-thoroughly-specified memory model. (I think we're in agreement there.) (By more fragile I mean more fragile for the caller to use in the way it's used in Java. The StampedLock implementation itself isn't fragile.) (Minor point, but I do think making the shared variables atomic is good enough for some cases (like perhaps the overly-simplified Point example). If you've got *only* reads, even atomic loads should still keep everything in a shared state across all caches, so no flushing. Especially true on amd64 with TSO where atomic loads compile down to exactly the same instruction, just with less reordering allowed at the compiler level. Writes are of course more expensive for both the reader and writer. Though yeah, if the shared state is big enough, I'd probably take a different approach altogether, same as you suggest.) RE: the race detector, I can get https://go.dev/play/p/UuIWVlV0UTN to trip the race detector if: 1. I remove the Println calls. 2. I remove the Sleep call. (The copy of `data` that's read by each reader goroutine is written to some independent global variable by each reader goroutine to satisfy the compiler.) I suspect what's happening is that the locking inside Println combined with the relatively low frequency of writes is making it so that the race detector does not actually ever observe a racy read/write. It's also a single int that's being loaded and stored, so the race window is quite small. On Fri, Jan 31, 2025 at 1:12 PM robert engels <ren...@ix.netcom.com> wrote: To clarify, my op was to understand if this could be done in Go, which Bruno showed it can, and know I’d like to know why the race detector is not complaining… I wasn’t making any attempt at creating a more specified Go memory model. It is what it is. On Jan 31, 2025, at 12:09 PM, robert engels <ren...@ix.netcom.com> wrote: Yes, Java has a very detailed and explicit memory model which makes things like this doable. Although the ideas you propose are workable, the primary purpose of this is performance based - otherwise you would simply use a mutex. The problem with making every shared variable an atomic is that you pay the atomic/fence cost on every access - read and write. Whereas in the Java case, there is no flushing of the cache at all if there has been no writer activity, and even if there is, it is a single cache flush per “bulk update”. There reason StampedLock is provided rather than modifying Point is DRY. If you need a highly concurrent shared data system (i know, don’t share data in Go…) which is often the case for HPC/HFT systems, StampedLock can be useful. Using channels and locks is simply too slow - see github.com/robaho/go-concurrency-test (old, but I think still applicable). I think a more viable performant replacement in Go is to use copy on write with an atomic object reference - which in most cases is going to be more performant than using a StampedLock. On Jan 31, 2025, at 11:55 AM, 'Michael Knyszek' via golang-nuts < golan...@googlegroups.com> wrote: This is a well-known pattern (use an atomic counter to check if anyone else modified some data in a critical section and retry or fall back if it happened), but as the extensive Java docs imply, any such abstraction is going be fairly leaky. Java has a very thoroughly defined memory model that I suspect comes into play here to make the abstraction more usable (but don't quote me on that). You *could* do a very close read of the Go memory model and try to do something similar but you end up pushing a lot of these subtleties onto the caller. This would also require //go:norace annotations on the caller to not fail in the race detector. Neither I nor the memory model docs recommend this. The pattern is still useful and possible to apply in Go, however I have two suggestions: 1. Take the abstraction one level higher. Using the Java doc's Point example, what I mean is to only provide Point, not StampedLock. It's not too hard to implement a bespoke version of this sort of thing when you need it, and I find that doing these sorts of tricks benefits from breaking down the abstraction internally anyway, since it gives you more flexibility when adding new features. 2. Read and write the optimistically-read variables using the sync/atomic package. In the Point example from the Java docs, the optimistic read path would load x and y atomically (i.e. two separate Loads) and any write paths would store x and y atomically (i.e. two separate Stores). That's going to be far easier to reason about and more portable, though it might not achieve maximum performance on your hardware. If you're doing mostly optimistic reads (which I suspect is really when you'd want to consider this anyway), the optimistic reads themselves should scale just as well and you won't need to apply any //go:norace annotations. This suggestion is in the same vein as the "don't be clever" part of the memory model documentation. On Friday, January 31, 2025 at 6:14:24 AM UTC-5 Robert Engels wrote: Hi, Do you think it is possible to implement a stamped lock in Go https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/StampedLock.html ? It would seem that the Go race detector would always report the “optimistic read” mode as a data race? (The docs state for Java that the values can be wildly inconsistent when the optimistic read fails). Ideas on how to implement in Go? -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/10e6c142-fbdb-4c8d-8270-a836ab4cecf8n%40googlegroups.com <https://groups.google.com/d/msgid/golang-nuts/10e6c142-fbdb-4c8d-8270-a836ab4cecf8n%40googlegroups.com?utm_medium=email&utm_source=footer> . -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/88dc5d27-655d-475d-9d9d-4f149acadab7n%40googlegroups.com.