MMX(MultiMedia eXtensions,多媒体扩展)是Intel在1996年推出的一个重要的指令集扩展,首次集成在Pentium MMX处理器(也称为Pentium with MMX Technology,核心代号为P55C)中,它的设计初衷是为了极大地提升处理器在处理多媒体数据和通信应用时的性能。

为什么需要MMX?—— 背景与动机
在MMX出现之前,计算机处理多媒体任务(如音频、视频、图像)的方式非常低效,这些任务通常有以下特点:
- 数据量大:一张图片或一段音频包含成千上万个像素或采样点。
- 类型简单:主要是8位或16位的整数数据,如颜色的RGB值、音频的采样值。
- 操作重复:需要对大量数据进行相同的操作,例如将所有像素的亮度增加20%,或者对音频信号进行滤波。
在MMX之前,CPU需要通过标准的通用寄存器(如EAX, EBX)和复杂的循环指令来处理这些数据,这种方式不仅慢,而且会占用宝贵的浮点单元(FPU)资源,影响科学计算等应用。
MMX的解决方案:引入一套专门为“数据并行处理”(Data Parallelism)设计的SIMD(Single Instruction, Multiple Data,单指令多数据)指令。
MMX的核心技术特性
SIMD(单指令多数据)
这是MMX的核心思想,一条MMX指令可以同时处理多个数据,一条PADDUSB(无符号饱和加法)指令可以一次性对8个8位整数或4个16位整数进行加法运算,而标准指令一次只能处理一个,这带来了巨大的性能提升。

新增8个64位寄存器
MMX引入了8个新的、专用的64位寄存器,命名为 MM0 到 MM7。
- 特性:这些寄存器是“别名寄存器”(Aliased Registers),它们实际上是浮点寄存器(ST0-ST7)的低64位,这种设计是为了在不增加硬件成本的情况下引入新功能。
- 影响:由于MMX寄存器和FPU寄存器物理上重叠,CPU无法同时处于MMX和FPU状态,这意味着,当程序需要执行浮点运算时,必须先“保存”MMX寄存器的状态,然后再使用FPU;反之亦然,这个切换过程有一定的开销,是早期MMX编程需要注意的一点。
64位数据类型
MMX指令集支持三种新的 packed(打包)数据类型,它们都存放在64位的MMX寄存器中:
*. Packed Byte (字节):8个8位整数,格式:B7 B6 B5 B4 B3 B2 B1 B0
- Packed Word (字):4个16位整数,格式:
W3 W2 W1 W0 - Packed Doubleword (双字):2个32位整数,格式:
D1 D0
饱和算术
这是多媒体处理中一个非常关键和有用的特性。
- 标准算术:当计算结果超过数据类型的表示范围时,会发生“回绕”(Wrap Around),一个8位无符号整数最大是255,
255 + 1结果会变成0。 - 饱和算术:当计算结果超过范围时,结果会“钳位”(Clamp)到该类型能表示的最大值或最小值,而不会回绕。
- 无符号饱和:
255 + 1结果是255。 - 有符号饱和:
127 + 1(8位有符号最大值是127) 结果是127;-128 + (-1)结果是-128。
- 无符号饱和:
- 应用场景:图像处理中的亮度/对比度调整、颜色混合等操作,使用饱和算法可以防止产生错误的颜色(如负数颜色或超出范围的亮度),使结果更符合预期。
54条新指令
MMX引入了54条全新的指令,主要分为以下几类:

- 算术指令:
PADD(加法),PSUB(减法),PMUL(乘法) 等,都支持饱和运算。 - 比较指令:
PCMPEQ(相等比较),PCMPGT(大于比较),比较结果不是0或1,而是生成全1(真)或全0(假)的掩码,常用于后续的逻辑操作。 - 逻辑/移位指令:
PAND(与),POR(或),PXOR(异或),PSRL(逻辑右移),PSLL(逻辑左移),PSRA(算术右移)。 - 数据类型转换/打包指令:
PACKSS(将高位字转换为低位字节,并执行饱和),PACKUSWB(将字打包为无符号字节,并执行饱和)。 - 状态管理指令:
EMMS(Empty MMX State):至关重要! 当MMX代码执行完毕,准备切换回浮点运算时,必须执行此指令,它会清空FPU标签字,告诉CPU现在可以使用FPU了,忘记使用EMMS是导致早期MMX程序出现浮点运算错误的常见原因。
MMX指令示例
假设我们要将两个8位像素数组相加,并使用饱和运算,防止结果溢出。
-
标准x86代码(低效):
mov ecx, 256 ; 循环256次(处理32个像素,每次4字节) mov esi, array1_ptr mov edi, array2_ptr mov ebx, result_ptr loop_start: mov al, [esi] ; 加载一个字节 mov bl, [edi] ; 加载另一个字节 add al, bl ; 相加 mov [ebx], al ; 存储结果 inc esi inc edi inc ebx dec ecx jnz loop_start这段代码需要256条
ADD指令和大量的内存访问和循环控制指令。 -
MMX代码(高效):
mov ecx, 32 ; 循环32次(因为一次处理8个字节) mov esi, array1_ptr mov edi, array2_ptr mov ebx, result_ptr ; 保存FPU状态(如果之前在使用FPU) fxsave [fpu_state] loop_start_mmx: movq mm0, [esi] ; 从array1加载64位数据到mm0 (8个字节) movq mm1, [edi] ; 从array2加载64位数据到mm1 (8个字节) paddusb mm0, mm1 ; 无符号饱和加法:mm0 = mm0 + mm1 (8个字节同时相加) movq [ebx], mm0 ; 将结果写回内存 add esi, 8 add edi, 8 add ebx, 8 dec ecx jnz loop_start_mmx ; 恢复FPU状态 fxrstor [fpu_state] emms ; 清空MMX状态,使FPU可用这段代码只需要32条
PADDUSB指令,效率提升了约8倍,并且减少了循环开销。
影响与局限性
积极影响
- 性能革命:MMX极大地提升了PC在多媒体方面的性能,使得流畅播放VCD、运行简单的3D游戏和图像处理软件成为可能。
- 推动多媒体普及:它降低了软件开发多媒体应用的门槛,为后来Windows 98等操作系统的多媒体特性奠定了硬件基础。
- SIMD的奠基石:MMX是Intel SIMD战略的开端,后续的SSE、AVX等指令集都是在MMX的基础上发展起来的,不断扩展寄存器宽度和数量,引入新的数据类型和功能。
局限性
- 与FPU的冲突:MMX和FPU寄存器物理重叠,频繁切换会有性能开销。
- 仅支持整数:MMX只处理整数,无法用于浮点数运算,这对于需要浮点运算的多媒体应用(如某些音频效果)是一个限制。
- 寄存器数量少:只有8个寄存器,在复杂的函数调用中,保存和恢复这些寄存器的开销较大。
- 缺乏缓存控制:MMX指令本身没有提供数据预取或缓存控制的指令,这在处理大数据量时可能成为瓶颈。
Pentium MMX的MMX指令集是一次里程碑式的创新。 它首次将SIMD思想引入主流x86处理器,通过引入专用的64位寄存器和针对多媒体优化的指令,特别是饱和算术,极大地提升了整数密集型多媒体任务的性能。
虽然它有其固有的局限性(如与FPU的冲突),但它成功地开启了CPU向并行计算演进的道路,为后来SSE、AVX等更强大、更现代的指令集铺平了道路,是现代CPU高性能计算能力的重要起源。
