Live data from Hacker News

There's No Such Thing as “Implicitly Atomic”

belkadan.com

11–20 of 47 posts

Re: There's No Such Thing as “Implicitly Atomic”

#11
post #9
post #4

The FreeBSD kernel only runs on platforms where 32-bit sized and aligned ordinary loads and stores are atomic; this is a requirement that it demands of the hardware. This may not be (extremely) portable, or may not work for Swift, or userspace under some very weird runtime environments, but it is true of the vast majority of CPU hardware out there.

So FreeBSD can run only on simple single 32-bit CPUs with no cache (or no registers)? I would argue that outside of a few select embedded controllers there's almost no hardware out there that meets that requirement.

What do you mean?

Caches don't break atomicity.

Re: There's No Such Thing as “Implicitly Atomic”

#12

Interestingly, the Go memory model[1] does in fact guarantee atomicity of word-sized reads/writes: > Otherwise, each read of a single-word-sized or sub-word-sized memory location must observe a value actually written to that location (perhaps by a concurrent executing goroutine) and not yet overwritten. However, it allows the implementation to immediately exit and report an error as well. [1]: https://go.dev/ref/mem

While Rust won't let you share a non-atomic variable across threads without going through a lock of some form or using `unsafe`.

So for both Rust and Go you'll get atomic accesses when you need them, and for C etc you only get atomic accesses if you ask for them. Which pretty much speaks to the different programming models: Rust and Go will only compile the subset of possibly-valid programs that can be expressed by the language and proven by the compiler. C compilers will only reject code they can prove is incorrect.

Re: There's No Such Thing as “Implicitly Atomic”

#13
post #7

The Go runtime has quite a few places where it assumes atomic load/store behavior of int sized words as well as the eventual propagation of the stores. I always get a little anxious when looking at such code. But it seems to work well in practice?

Go's memory model is more constrained than C, C++, and Swift and this case is specifically addressed: https://go.dev/ref/mem#restrictions . "...each read of a single-word-sized or sub-word-sized memory location must observe a value actually written to that location"

Yeah, but there is no guarantee that a write of one goroutine will eventually become visible to other goroutines. So in practice the runtime expects stronger guarantees.

Re: There's No Such Thing as “Implicitly Atomic”

#14
post #9
post #4

The FreeBSD kernel only runs on platforms where 32-bit sized and aligned ordinary loads and stores are atomic; this is a requirement that it demands of the hardware. This may not be (extremely) portable, or may not work for Swift, or userspace under some very weird runtime environments, but it is true of the vast majority of CPU hardware out there.

So FreeBSD can run only on simple single 32-bit CPUs with no cache (or no registers)? I would argue that outside of a few select embedded controllers there's almost no hardware out there that meets that requirement.

[deleted]

Re: There's No Such Thing as “Implicitly Atomic”

#15
> Even that isn’t guaranteed. If you don’t say “atomic”, the compiler might decide to split up a store to optimize for speed or code size! This isn’t hypothetical; Greg Parker ran into this with libobjc.

Maybe I'm tired and lack imagination, but please enlighten me. How can splitting up a store speed something up or reduce the code size? Assuming it is aligned and on a 32bit native CPU.

Re: There's No Such Thing as “Implicitly Atomic”

#16
post #7

The Go runtime has quite a few places where it assumes atomic load/store behavior of int sized words as well as the eventual propagation of the stores. I always get a little anxious when looking at such code. But it seems to work well in practice?

Anyone who writes a compiler for Go can guarantee such behavior--anything else is a buggy implementation.

It works in practice because it's a requirement of the implementation.

Re: There's No Such Thing as “Implicitly Atomic”

#17

If a language is self-hosting that means it must guarantee translation of code that is not explicitly atomic in that language to instructions which are explicitly atomic in the CPU ISA to implement language explicit atomics. I don't feel that the article take is useful or informative and amounts to gatekeeping ISA parallelism.

Have you ever heard of "CMPXCHG8B"?

Re: There's No Such Thing as “Implicitly Atomic”

#18
post #9
post #4

The FreeBSD kernel only runs on platforms where 32-bit sized and aligned ordinary loads and stores are atomic; this is a requirement that it demands of the hardware. This may not be (extremely) portable, or may not work for Swift, or userspace under some very weird runtime environments, but it is true of the vast majority of CPU hardware out there.

So FreeBSD can run only on simple single 32-bit CPUs with no cache (or no registers)? I would argue that outside of a few select embedded controllers there's almost no hardware out there that meets that requirement.

You're confusing atomicity with sequential consistency. The former guarantee is perfectly compatible with caching.

Re: There's No Such Thing as “Implicitly Atomic”

#19
post #15

> Even that isn’t guaranteed. If you don’t say “atomic”, the compiler might decide to split up a store to optimize for speed or code size! This isn’t hypothetical; Greg Parker ran into this with libobjc. Maybe I'm tired and lack imagination, but please enlighten me. How can splitting up a store speed something up or reduce the code size? Assuming it is aligned and on a 32bit native CPU.

The linked thread doesn't say exactly how, but I can construct some way in my head:

Final 32-bit store effectively stores two 16-bit values or'ed together as high-16 and low-16. You can get this without any shifting and simple addition and the proper values. If these two sixteen bit values are calculated along two very different paths which have different lengths, the compiler might opt to spill one of them early, only to reload the spilled value just before the final addition.

From there it isn't too hard to see some liveness analysis determine that the reload, addition, and immediate store affect onlye bits of the final store, and you can remove parts of it.

Pure speculation on my part that this is what happened, but all of these intermediate steps are performed all the time by modern optimizing compilers.

Re: There's No Such Thing as “Implicitly Atomic”

#20
post #7

The Go runtime has quite a few places where it assumes atomic load/store behavior of int sized words as well as the eventual propagation of the stores. I always get a little anxious when looking at such code. But it seems to work well in practice?

Anyone who writes a compiler for Go can guarantee such behavior--anything else is a buggy implementation. It works in practice because it's a requirement of the implementation.

I guess I’m less worried about the atomic nature of the operation and more about the way the writes become visible. That seems to be entirely hardware dependent?
Post reply on HN