But hey, I guess I don't want to be judged on my mid-90s code either.
And it was a very long time before programmers (and compilers) realised just how bad of an idea loop unrolling was once we entered the era of superscalar processors with instruction caches and half-decent branch prediction.
Misprediction there means you computed one more vector or value, not a huge loss compared to a cache miss. Branch misprediction hurts a lot when it forces a cache miss mostly, whether it's for data or for instructions, or ties up a deep instruction pipeline completely with lousy abort, which most CPUs do not have. (See: Nehalem.)
My naive assumption is that they performed the majority of testing with debug code (as you would). And, since they had exactly one chance to get things right (being a physical cartridge), they couldn't afford to do all the testing again, with the new/different binaries generated with debug turned off.
I just actually shouted when he said that if you simply compiled for release mode it ran 30% faster.
Of course, it's a way worse problem that they compiled at -O0.
There was a Modern Vintage Gamer video going into this. Apparently it wasn't a mistake or misunderstanding, but a bug that existed in the original version of the compiler that caused issues when compiled with optimisation. Even then, the main library that did most of the heavy lifting was compiled with o2.
Super Mario 64 was a launch title, and they simply didn't have the time to work out the kinks in the compiler to get those few extra fps in Dire Dire Docks.
And the architectures have consolidated since then.
The most plausible explanation to me (and, IIRC, the one that Giles Goddard speculated was the case) is that they simply didn't want to take the chance that optimization could surface unknown bugs in the code, and they knew that the unoptimized version worked and played fine, so they played it safe and shipped the known-good unoptimized version. This is a perfectly understandable decision in terms of risk management, even if in retrospect it was too conservative. Less likely, but still very plausible, was that turning off optimization was just a mistake; mistakes happen when you're rushing a release for a deadline, and build deployment was not as orderly and automated back then as it is today.
Of course it "shouldn't make any difference" but who among us hasn't at some point been bitten by a "that couldn't possibly affect... ohhh. Ohhhhhhh." style bug?
When you're dealing not just with a single piece of software, but an entire hardware+software ecosystem, you don't risk jeopardizing the whole stack for an extra few FPS in some back corner.
Back in those days it had to ship as close to perfect as possible because you couldn’t just send out a patch layer.
I'm not just saying this in a theoretical sense either; I've seen much weirder bugs.
Even on today's high performance CPUs, which are far more sophisticated than the primitive 5-stage scalar in-order R4300 (not sure it even had any branch prediction actually).
I believe it didn't, with a short scaler pipeline (only 5 stages) the cost of waiting for a conditional branch to be calculated is relatively less, and MIPS of that era actually exposed the idea of a "delay slot" - rather than pausing the pipeline while calculating the jump, it still executed the next few instructions - so they were always executed no matter if the jump was actually taken or not. Filling this with NOPs makes it equivalently the same as a pipeline bubble, but I guess the intention was in some cases it could actually be used for something.
I think a few RISC architectures of that era did similar things, their entire goal was simplicity after all, the argument being that something like a branch predictor and the logic required to reverse speculatively executed instructions "should" instead be used to make the CPU smaller or higher performing in the best case (either as implementing better performing but larger logic, or higher frequency due to shorter critical paths).
I think this was dropped in later ISAs as it confused a lot of people, it wasn't well used by compilers (which is interesting as much of the MIPS ISA was explicitly designed around what would be easy for a compiler rather than handrolled asm), and improving process technology made transistors to handle the expensive superscaler speculative execution architectures relatively cheaper.