The x86 architecture is the weirdo (2004)
devblogs.microsoft.com
devblogs.microsoft.com
> The x86 has a small number (8) of general-purpose registers
x86-64 added more general-purpose registers.
> The x86 uses the stack to pass function parameters; the others use registers.
OS vendors switched to registers for x86-64.
> The x86 forgives access to unaligned data, silently fixing up the misalignment.
Now ubiquitous on application processors.
> The x86 has variable-sized instructions. The others use fixed-sized instructions.
ARM introduced Thumb-2, with a mix of 2-byte and 4-byte instructions, in 2003. PowerPC and RISC-V also added some form of variable-length instruction support. On the other hand, ARM turned around and dropped variable-length instructions with its 64-bit architecture released in 2011.
> The x86 has a strict memory model … The others have weak memory models
Still x86-only.
> The x86 supports atomic load-modify-store operations. None of the others do.
As opposed to load-linked/store-conditional, which is a different way to express the same basic idea? Or is he claiming that other processors didn't support any form of atomic instructions, which definitely isn't true?
At any rate, ARM previously had load-linked/store-conditional but recently added a native compare-and-swap instruction with ARMv8.1.
> The x86 passes function return addresses on the stack. The others use a link register.
Still x86-only.
When SPARC got new microcode supporting unaligned access, it turned out to be a big performance win, as the alignment padding had made for a bigger cache footprint. That was an embarrassment for the whole RISC industry. Nobody today would field a chip that enforced alignment.
The alignment penalty might have been smaller back when clock rates were closer to memory latency, but caches were radically smaller then, too, so even more affected by inflated footprint.
I argues with some of the Rust compiler members the other day about wanting to just ditch almost all alighnment restrictions because I of this exact thing. They laughed and basically told me i didnt know what i was talking about. I remember about 15 years ago when i worked at market making firm and we test this and it was a great gain - we started packing almost all our structs after that.
Now, at another MM shop, and we're trying to push the same thing but having to fight these areguments again (the only alignmets I want to keep are for AVX and hardware accessed buffers).
I don't know, because nobody's actually posting a link to it.
Of course C and C++ themselves have to keep to machine ABI layout rules for backward compatibility to code built when those rules were (still) thought a good idea. Compilers offer annotations to dictate packing for specified types, and the Rust compiler certainly also offers such a choice. So, maybe such annotations should just be used a lot more in Rust, C, and C++.
This is not unlike the need to write "const" everywhere in C and C++ because the inherited default (from before it existed) was arguably wrong. We just need to get used to ignoring the annotation clutter.
But there is no doubt there are lots of people who think padding to alignment boundaries is faster. And, there can be other reasons to align even more strictly than the machine ABI says, even knowing what it costs.
Anyway, same policy applies: annotate your structs "packed" anywhere performance matters and bitwise compatibility with other stuff doesn't.
But... when you are structure-blasting, you are probably also already paying generous attention to layout issues.
You can take a pointer to an i32 and pass that pointer to C code as int32_t. This means it has to have the same alignment in Rust that it has in C.
There are embedded targets that still enforce alignment restrictions, but you are even less likely to pass pointers between code compiled with different compilers, there.
Compilers can rely on alignment even if the CPU doesn't. LLVM does, which is why older versions of rustc had segfaults when repr(packed) used to allow taking references. While it would be pretty easy to get rustc to stop emitting aligned loads, getting Clang and GCC to stop emitting aligned loads might be trickier. https://github.com/rust-lang/rust/issues/27060
Most people are not used to facts having a half-life, but many facts do, or, rather, much knowledge, does.
We feel very secure in knowing what we know, and the reality is that we need to be willing to question a lot of things, like authority, including our very own. Now, we can't be questioning everything all the time because that way madness lies, but we can't never question anything we think we know either!
Epistemology is hard. I want a doll that says that when you pull the cord.
It's certainly true that in the tech industry things are CONSTANTLY shifting.
However, talk physics and you'll find that things rarely change, especially the physics that most college graduates learn.
The last time I worked on a btree-based data store, changing the nodes from ~1024 bytes to ~1000 delivered something like a 10% throughput improvement. This was done by reducing the number of entries in each node, and not by changing padding or packing.
Caches on modern chips can handle keeping up to some small fixed number, often 4, objects all in cache whose addresses are at the same offset into a page, but performance may collapse if that number is exceeded. It is quite hard to tune to avoid this, but by making things not line up on power-of-two boundaries, we can avoid out-and-out inviting it.
[1]: https://blog.yiningkarlli.com/2021/07/porting-takua-to-arm-p...
It may be obvious, but I think it bears repeating. This blog entry should not reflect badly on Raymond C as he was reporting on the architecture at this time.
x86 is still the weirdo. Both Thumb-2 and the RISC-V C extension (I don't know about PowerPC) have only 2-byte and 4-byte instructions, aligned to 2 bytes; x86 instructions can vary from 1 to 15 bytes, with no alignment requirement.
Any losses from having to do more decoding work are probably offset by having sufficiently deep pipelines and enough decoders.
I can imagine ways to deal with this, but x86 doesn't have them.
Supposedly, "MIPS processors [also] have a MIPS-16 mode."
It's been a while, so I'm going to define my understanding of the ABA problem in case I misunderstood it:
x86 only supplies cmpxchange instructions on which will update a value only if it matches the passed in previous value. There's a class of concurrency bugs where the value is modified away from it's initial value and then modified again back to it's value. cmpxchange can't detect that condition, so if it's a meaningful difference often the 128-bit cmpxchange will be used with a counter in the second 64-bits that is incremented on each write to catch this case.
LL/SC will trigger on any write, rather than comparing the value, providing the stronger guarantee.
(Please correct me if this is inaccurate, it's been a hit minute since I learned this and I'd love to be more current on it).
> Still x86-only.
SPARC supported (eventually exclusively) TSO well before x86 committed to it. For a while Intel claimed to support some form of Processor Ordering which I understand is slightly weaker than TSO, although no Intel CPU ever took advantage of the weakened constraints.
> As opposed to load-linked/store-conditional, which is a different way to express the same basic idea?
Perhaps that x86 has atomic addition, where other architectures must use a cas/ll+sc loop?
It refers specifically to things like fetch_and_add. Supported by risc-v and i64.
x86 doesn't really impose sequential consistency between cores/threads. It imposes a Total Store Order (TSO) in which stores are always in order to each other but a store can be reordered after a load.
SPARC had TSO on later chips whereas earlier chips had weaker models. MIPS developed the other way: with older versions having stronger memory ordering and later getting relaxed memory ordering.
RISC-V chips can optionally support TSO but it seems that the motivation is programs ported from x86. IBM's z/Architecture (with lineage back to IBM/360) is still alive and also has TSO. BTW. The Mill is supposed to offer sequential consistency, but it remains to be seen if that will be a performance bottleneck.
To be more pedantic (and hoping I remember this correctly): TSO is indistinguishable in software from full sequential consistency. Any code to detect the difference must by definition be subject to race conditions (or must be an atomic read/write operation that on x86 would be serializing anyway). So x86 in fact does provide SC semantics "between cores/threads". It does have visible reordering artifacts from the perspective of hardware designs (e.g. MMIO registers) where a load has side effects.
MFENCE was added for convenience, but the same effect can be had with any locked instruction on a dummy memory location. In fact XCHG is often still faster than MFENCE.
In fact the x86 memory model has been strengthened in the last couple of decades as some reordering that were theoretically possible (but were never implemented in practice in any hardware) have been finally documented to be impossible since TSO has been embraced.
https://www.joelonsoftware.com/2004/06/13/how-microsoft-lost...
This also leads to situations where many instructions in x86 asm will run faster than fewer complex instructions which the decoder may not decode to its most optimal micro-operations sequence.
Which is to me, essentially, VLIW. Those architectures didn't work out very well. There's a lot of memory pressure just to move instructions around and the costs didn't seem to outweigh the benefits.
You can see instruction latencies at https://www.agner.org/optimize/.
Yes, it's a weirdo.
I do find it sad that we are stuck in an 80 year old paradigm that is a horrible fit for how CPUs are actually implemented in 2022, but the inertia is a strong in this one.
Kinda like gas engines and fuel pumps. Or wall socket formats. Inertia.
So yes, we can overcome inertia, it just take a lot of energy.
Motorola 68K is still around too.
I don't think RISC is a very useful term.
Notes from the future are always a risky business. Now we're in the future and we call them x86 and x64 - which makes far less sense than x86-32 and x86-64, but I guess is shorter and doesn't require you to learn a new name for an old concept.
I guess the actual future always turns out a bit more messy than the science fiction version of it.
While x86-32 is AFAIK a backronym which would only be invented later, the original name of the AMD64 architecture is x86-64, and has been since that architecture was first revealed at the turn of the millennium. If you weren't following these developments back then, you can still see it for yourself in the Internet Archive: https://web.archive.org/web/20000817014037/http://www.x86-64... is the earliest snapshot of the official page for the x86-64 Linux porting effort, which links to https://web.archive.org/web/20000817071303/http://www.amd.co... which is a snapshot of a press release dated 2000-08-15 announcing that porting effort. Quoting from that press release: "AMD publicly released its 64-bit architecture specification, the x86-64™ Architecture Programmers Overview, last week to enable the industry to begin incorporating x86-64 technology support into their operating systems, applications, drivers, and development tools."
That is, the x86-64 name was first used publicly, as the official name of that architecture, no later than early August of the year 2000.