Build environment archaeology like this matters more than people realize. Modern CI assumes containers solve reproducibility, but compiler version differences, libc variants, and even CPU instruction sets can silently change binary output. The detail about needing to reinstall Windows NT just to add a second CPU shows how tightly coupled OS and hardware were — there was no abstraction layer pretending otherwise. Exact toolchain reproduction isn't nostalgia; it's the only way to validate that a specific binary came from specific source.
> The detail about needing to reinstall Windows NT just to add a second CPU shows how tightly coupled OS and hardware were — there was no abstraction layer pretending otherwise.
In this case there was: the reason you need to reinstall to go from uniprocessor to SMP was because NT shipped with two HALs (Hardware Abstarction Layer): one supporting just a single processor, and one supporting more than one.
The SMP one had all the code for things like CPU synchronization and interrupt routing, while the UP one did not.
If they'd packed everything into one HAL, single-processor systems would have to take the performance hit of all the synchronization code even though it wasn't necessary. Memory usage would be higher too. I expect that you probably could run the SMP HAL on a UP system (unless Microsoft put extra code in to make it not let you), but you wouldn't really want to do that, as it would be slower and require more RAM.
So it wasn't that those abstraction layers didn't exist back then. It was that abstraction layers can be expensive. This is still true today, of course, but we have the cycles and memory to spare, more or less, which was very much not the case then.
> If they'd packed everything into one HAL, single-processor systems would have to take the performance hit of all the synchronization code even though it wasn't necessary. Memory usage would be higher too.
Linux also used to be like this, but these days has unified MP/UP kernels; on single-CPU systems (or if you give nosmp), the extra code is patched away at boot time. It wouldn't have been an unheard of technique at the time.
I actually would love this to be built in to a language/compiler. A lot of times when I’m building a single-threaded program but I’m using libraries written by other people. These libraries don’t know whether they are being incorporated into programs with single thread or not. So they either take the performance penalty of assuming multi-threaded (the approach by std::shared_ptr) or they give callers choice by making two implementations (Rust Arc and Rc). But the latter doesn’t actually work because this needs to be a global setting, not just a decision made at a local call site. It won’t work if such a library is a transitive dependency.
Zig supports this. If you compile with -fsingle-threaded, operations on mutexes turn into nops, atomics become simple loads/stores, etc.
Glibc has a bunch of tests throughout the codebase where it checks if there have been any threads started besides the main one. I don’t really know how effective they are from a performance perspective. (In principle, turning fgetc into getc_unlocked, for instance, could be quite beneficial.) Microsoft used to have a single-threaded C runtime, but it was done away with some time ago, I’m guessing because they started putting things into the platform that would start and manage random threads outside the programmer’s control.
Linux also used to have separate SMP kernels back when multi processor systems were rare.
I’m pretty sure that the SMP kernel would boot on UP and vice versa, though.
They could have shipped both HALs. Or made it easy to switch which one was in use without reinstalling.
CDs were around and hard drives weren’t that small at the time. (Or maybe the really early SMP versions predated widespread availability of CD-ROMs, but I remember dealing with this nonsense and reinstalling from an MSDN CD set.)
With NT4, I'm pretty sure both HALs were on the CD-ROM (unless you had an exotic system with a custom HAL, which came with its own install media). Keep in mind your use case is approximately nobody, you either had a SMP system or you didn't.
It was really not that rare to want to move a disk from one system to another. Except that there was an obnoxiously high chance that Windows would refuse to boot.
Man, I feel like this is the only type of comment I'm leaving these days, but is this account just posting AI generated comments?
there is something to be said about old windows installation CDs being essentially modern-day equivalents of immutable docker layers - i don't think one could say that about modern windows, but then i'm not super clued in into ms stuff.