4 ms·
If you are assigning something that fits in a single register, how would you get tearing? (Probably shouldn't have double in that list, in case it's in the spe
by coryrc 7y ago
If you are assigning something that fits in a single register, how would you get tearing? (Probably shouldn't have double in that list, in case it's in the special FPU registers)
- slx26 7y agoyeah, 64-bit types might cause problems (I found Rust's atomic module reference educative https://doc.rust-lang.org/std/sync/atomic/ https://doc.rust-lang.org/std/sync/atomic/ (the source code doesn't do anything too special either)), but the post said "modern processors", so I guess it's rather accurate
- cesarb 7y agoThe compiler is smarter than you think. Suppose one thread alternates between writing 32 and 33 to a 4-byte variable, while another thread alternates between writing 1024 and 1025 to the same 4-byte variable. You might think that the only possible values are 32, 33, 1024, 1025; but then you read the variable and find a 1056 or 1057 there. Why? Because the compiler noticed that the first thread, after its initial write of 32 or 33, only modifies the least significant byte of the value; so it generated a single-byte write to overwrite only the modified part of the value.
- joosters 7y agoI’d love to see a godbolt example of this optimisation, I’ve never heard of a compiler doing this sort of thing.
- vnorilo 7y agoThat one would be more of a pessimisation, I think.
- meheleventyone 7y agoWhich compilers exhibit this behaviour?
- PeCaN 7y agoIt's possible, if unlikely, that a compiler targeting the DEC Alpha could exhibit something similar (but with 16-bit values instead of bytes). See my reply to reitzensteinm. (I have no idea if any Alpha compilers did this in the wild.)
- meheleventyone 7y agoThat’s pretty fascinating, thank you!
- reitzensteinm 7y agoThere's zero benefit to just writing one byte. I'm having a hard time believing that a compiler capable of such analysis would use it for this purpose.
- nitrogen 7y agoI think the point is that unless the language guarantees atomicity (and the compiler implements the guarantee correctly), counterintuitive behavior is permissible and somewhat common, and compiler behavior varies across and within CPU architectures.
- reitzensteinm 7y agoActually I agree with the general point, OP is right that you should use volatile. I was just a bit taken back by the specific example above. However, as PeCaN has pointed out, I'm probably suffering from tunnel vision from x86 monoculture.
- PeCaN 7y agoFor the record I think the specific example from the OP is rather silly and of course never an optimization on x86(_64), but I'd never pass up a chance to talk about more fun architectures ;-)
- lalaland1125 7y agoFor reference, volatile is still the wrong choice for thread safety. If you need your race condition semantics to be well defined, you must use atomics (do note that those atomics might compile down to simple assignments if the platform supports that, but only the compiler is allowed to do that).
- vnorilo 7y agoVolatile is, well, volatile. You can basically only rely on the fact that the compiler respects source order and keeps all reads and writes (instead of hoisting and reordering). I believe msvc used to put in memory barriers. Not sure if it still does. These days you are better off ignoring the entire keyword and using the properly specified atomic stuff.
- de_watcher 7y agoCompiler may just reorder or optimize out some assignments. Caches may not sync between processors.