How Intel's MMX Brought SIMD to the PC
SIMD in the 90s: Programming Intel's Pentium MMX
In 1997, Intel introduced MMX, a set of 57 SIMD instructions that let a Pentium process multiple data points with a single instruction. This article looks back at MMX's design—reusing the x87 floating-point registers—and its impact on multimedia software. It explains packed integer data, saturating arithmetic, and the EMMS instruction, and shows how MMX enabled optimizations like processing eight pixels at once. The piece also clarifies the difference between SIMD and vector processors, noting that SIMD predates MMX by decades.
It wasn't the invention of SIMD, but it was one of the moments when SIMD crossed over from specialized/high-performance computing into ordinary desktop software development.
- theandrewbailey
> Each MMX register is 64 bits wide. Internally, the MMX registers were aliases of the x87 floating-point registers.
Due to the way the first Pentium 3 CPUs (Katmai) were built, they likewise aliased the x87 (and thus, MMX) registers to the XMM SSE registers, but this was hidden from programs. It wasn't until at least the Coppermine revision that they were separate registers again.
- gustavopezzi
Author here. Thanks for sharing.
- Const-me
I would add than SSE1 and SSE2 are now required parts of AMD64 instruction set. All 64-bit PC processors are required to support them both. For that reason, modern compilers are ignoring x87 FPU when building 64-bit binaries. Instead, they compile all float and double arithmetic into SSE1 and SSE2 instructions, respectively.
- nojokepoke
What’s often overlooked is that adoption of MMX was slooooow. Intel compiler were the only intrinsic data types for years. The big win was DirectX 3 audio drivers that used premade Intel libraries. It took at least five to ten years for SIMD to catch on, but the never stopped Intel from evolving it. Then they lost the GPU wars lol rip larabeee.
- ack_complete
I did extensive MMX and SSE2 optimization of audio and video codecs in the 2000s. MMX made a large difference, but it was a pain.
MMX optimization practically required assembly language. The Pentium MMX was an in-order dual pipe CPU, and while compilers supported MMX intrinsics, their code generation for it was abysmal. Visual C++ 6, for instance, would emit code that was 2/3rds register-to-register moves, with values being unnecessarily moved between two registers between each ALU op. This was also a problem with SSE/SSE2 intrinsics. The worst case I saw was the _mm_set_epi8() intrinsic, which was used to construct a 128-bit vector from 16 inputs. When used with all constants, it should have generated a single 128-bit constant load; instead, Visual Studio 2008 generated ~80 instructions to compute it from byte loads. Microsoft didn't fix it until VS2010.
The latency of MMX instructions combined with the in-order dual pipe architecture also made asm loops messy. Simple ops were single-cycle, but multiplies had 3 cycle latency, stores required data an additional cycle in advance, and computed load/store addresses were also needed a cycle in advance. Simply running 2-4 iterations in parallel wasn't an option as there were only 8 vector registers and you'd still get bottlenecks on functional units. Getting peak performance thus often required interleaving loop iterations with special entry and exit code around the loop.
The issue with EMMS is understated. When the CPU switched t […]