5 ms·
To be fair most of the consensus-imposed[1] interface to memory ordering semantics was developed as part of the C++ memory model standardization process. GCC/c
by ajross 2y ago
To be fair most of the consensus-imposed[1] interface to memory ordering semantics was developed as part of the C++ memory model standardization process. GCC/clang have imported them as extensions for C code, etc...
[1] Which is not to say "best". My standard complaint in these threads is that the C++ ordering semantics are a mess designed to make things easier for compiler writers to target architectures with variant ordering guarantees and not for algorithm developers to create correct code. But lockless code is much harder to write than compilers! It's a bad trade. ARM had this right decades ago with simple read/write barriers, which are vastly easier to understand when reading code.
- packetlost 2y agoRust also adopted the C/C++ memory ordering semantics, for better or worse.
- ajross 2y agoEveryone has, even the hardware vendors are treating these rules as the golden standard to which they can optimize and conform. So it's too late. But it's still a shame. The acquire/release semantics make perfect sense for locks, because that's what it was designed for. Designing anything else with them is extremely difficult, and in practice such work tends to lean heavily on the full-barrier "sequentially consistent" primitive instead. The older read/write barrier instructions were much simpler. (And x86 is simpler still, with just one reordering rule to worry about and a single idea of serializing instructions to regulate it).
- gpderetta 2y agoI think it is just a generational/educational difference. I learned lock-free programming around the time c+11 was being standardized, and the acquire/release model seems very natural to me and easier to understand and model algorithms in it than the barrier based model.
- menaerus 2y agoHardware vendors adhering to C++ memory model to design their chips? I think you got it backwards or I misunderstood your point. Memory barriers exist because of how cache memory hierarchy and coherency protocols are implemented in multi-core chips. And not to "optimize and conform to C++ memory model". C++ memory model is there to make life of a programmer easier across different micro-architectures because memory model of the micro-architecture is what differs.
- tialaramex 2y agoThere is an argument, particularly from C++ people, that the CPU architectures designed after C++ 11 shipped with the Acquire/Release model have all chosen to provide features that can do Acquire/Release smoothly. After all, if the software most people execute rewards CPUs which do X and punishes CPUs which do Y instead, it's a tough sell to design your CPU to do Y, that means ordinary users will get the idea it's worse than it really is, and your sales people don't like that, it seems reasonable to argue that the reason modern x86-64 CPUs ignore the weird x87 FPU features is that nobody's programs want those weird features. I remember in the mid-1990s there was a lot of hype for high clock frequency 80486 and clones because they had very good integer performance. However, Intel's Pentium had excellent FP performance at similar or lower clocks. The video game Quake needed floating point, and so Quake on my 100 MHz Pentium was very quick, while on a friend's 100MHz 486 it was pretty nasty. Increased sales of the Pentium were I believe widely attributed to the game Quake showing off the FP performance (it's nothing to look at in today's terms, we did not have GPUs back then). I have never worked for a CPU design firm or CPU manufacturer, so I can't speak to whether this is in practice a meaningful influence.
- menaerus 2y ago> There is an argument, particularly from C++ people, that the CPU architectures designed after C++ 11 shipped with the Acquire/Release model have all chosen to provide features that can do Acquire/Release smoothly. I never heard that argument (source would be good?) but this is very different to what the parent comment said. Code does not run in vacuum - it runs on the CPU. And CPU does not exist vacuum - it's there to run that very code so they're both very inter-dependent and intertwined. As much as CPU designs are changing to accommodate new algorithmic requirements so does the code but vice-versa - to make a utility of new CPU designs. So, of course, CPU vendors will do everything that is in their control to make the new chip design appealing to their customers. This has been done forever. If that means spending extra transistors to run some C++ heavy data center code faster by 10% of course they will do it - there's a very large incentive in doing so. But that doesn't mean that CPU vendors are designing their chips to accommodate abstract programming language models. In this case, memory models. Probably one of the most easiest examples of such practices to understand, and that I could think of right now, is Jazelle - an ARM CPU circuitry designed to execute the Java bytecode directly in the CPU itself.
- immibis 2y agoC tries to be everything to everyone. It has to be able to target every architecture, within certain limits. It took them a long time to standardize on two's-complement signed integers - they could only do that after the whole industry had migrated to two's-complement signed integers as they are the technically best way to represent signed integers. The x86 is the weird architecture that tried to keep existing programs working when they moved to multicore. Most other architectures implemented whatever is easier to implement in hardware, and compilers have to target that. And the compiler can't simulate a strong memory model on top of a weak one, because that means lots of overhead for every memory operation and that's not what C does.
- tialaramex 2y agoBecause Linux pre-dates that work significantly (over a decade) and even multi-processor Linux pre-dates it by enough that it could not have waited, the Linux ordering rules are actually different from those C got from C++ One of the interesting problems for Rust-for-Linux is what to do about that, Rust has all of the C++ 11 memory ordering except the problematic (and in practice abandoned) Consume ordering, but it doesn't have a convenient way to admit a different model, yet Linus isn't exactly likely to wake up one day and declare now Linux has the C++ 11 ordering model. As to your concern, of course the compiler vendors have to worry about portability. It's all very well to say this works on ARM, but the expectation from a person writing Rust or even C++ is that a correct program should work on all of an ARM, an x86, archaic PowerPC and somebody's cheap in-order embedded CPU. If the vendor only really cares about one architecture (cough, Microsoft) then they might invent weird rules that only really make sense for that architecture and your "correct" programs either don't work, don't compile, or are very slow on every other CPU. But that's hardly a reliable way forward into the future.
- ajross 2y ago> of course the compiler vendors have to worry about portability. Yes, but the point was that "defining a language that doesn't suck" is a competing requirement. Again, C++ just dropped the ball here and solved the wrong problem. And we all suffer for it with repeated blog posts like this one trying desperately to explain exactly what "acquire" means and not how hardware actually works. And yes: Linux has clean and easy (or as easy as these things get) read/write barriers, the implementation of which has now been terribly polluted by the toolchain's incompatible choice of semantics.