3dnow!

**3DNow!**（據說是「3D No Waiting!」的縮寫）是由 AMD 開發的一套 SIMD 多媒體指令集，支援單精度浮點數的向量運算，用於增強 x86 架構的電腦在三維圖像處理上的效能。

## 歷史

1996 年 Intel 首先推出了支援 MMX 的 Pentium 處理器，極大地提高了 CPU 處理多媒體資料的能力，被廣泛地應用於語音合成、語音辨識、音訊影片編解碼、圖像處理和串串流媒體等領域。但是 MMX 只支援整數運算，浮點數運算仍然要使用傳統的 x87 協處理器指令。由於 MMX 與 x87 的暫存器相互重疊，在 MMX 代碼中插入 x87 指令時必須先執行 _EMMS_ 指令清除 MMX 狀態，頻繁地切換狀態將嚴重影響效能。這限制了 MMX 指令在需要大量浮點運算的程式，如三維幾何變換、裁剪和投影中的應用。

另一方面，由於 x87 古怪的堆疊式暫存器結構，使得硬體上將其管線化和軟體上合理排程指令都很困難，這成為提高 x86 架構浮點效能的一個瓶頸。

為了解決以上這兩個問題，AMD 公司於 1998 年推出了包含 21 條指令的 3DNow! 指令集，並在其 K6-2 處理器中實現。K6-2 是第一個能執行浮點 SIMD 指令的 x86 處理器，也是第一個支援水平浮點暫存器模型的 x86 處理器。藉助 3DNow!，K6-2 實現了 x86 處理器上最快的浮點單元，在每個時鐘周期內最多可得到 4 個單精度浮點數結果，是傳統 x87 協處理器的 4 倍。許多遊戲廠商為 3DNow! 最佳化了程式，微軟的 DirectX 7 也為 3DNow! 做了最佳化，AMD 處理器的遊戲效能第一次超過 Intel，這大大提升了 AMD 在消費者心目中的地位。K6-2 和隨後的 K6-III 成為市場上的熱門貨。

1999 年，隨著 Athlon 處理器的推出，AMD 為 3DNow! 增加了 5 條新的指令，用於增強其在 DSP 方面的效能，它們被稱為「擴充 3DNow!」（Extended 3DNow!）。

為了對抗 3DNow!，Intel 公司於 1999 年推出了 SSE 指令集。SSE 幾乎能提供 3DNow! 的所有功能，而且能在一條指令中處理兩倍多的單精度浮點數；同時，SSE 完全支援 IEEE 754，在處理單精度浮點數時可以完全代替 x87。這迅速瓦解了 3DNow! 的優勢。

1999 年後，隨著主流作業系統和軟體都開始支援 SSE 並為 SSE 最佳化，AMD 在其 2000 年發布的代號為「Thunderbird」的 Athlon 處理器中添加了對 SSE 的完全支援（「經典」的 Athlon 或 K7 只支援 SSE 中與 MMX 有關的部分，AMD 稱之為「擴充 MMX」即 Extended MMX）。隨後，AMD 致力於 AMD64 架構的開發；在 SIMD 指令集方面，AMD 跟隨 Intel，為自己的處理器添加 SSE2 和 SSE3 支援，而不再改進 3DNow!。

2010 年八月，AMD 宣布將在新一代處理器中取消除了兩條資料預取指令之外 3DNow! 指令的支援，並鼓勵開發者將 3DNow! 代碼重新用 SSE 實現。

## 支援檢測

支援 3DNow! 的 CPU 的 CPUID 擴充功能字（EAX=80000001h 時執行 CPUID 指令得到的 EDX 的內容）的（從低位到高位）第 31 位元為 1。支援擴充 3DNow! 的 CPU 的 CPUID 擴充功能字的（從低位到高位）第 30 位為 1。

K6-2 至 K10 之間 AMD 所有的 x86 處理器都支援 3DNow!，包括 Athlon 64、Opteron 和 Sempron 處理器；AMD 將 3DNow! 從 Ryzen、AMD FX 處理器移除；Cyrix 等一些其他廠家生產的某些處理器也支援 3DNow!；但 Intel 生產的所有處理器都不支援 3DNow!。

## 執行環境

3DNow! 指令的執行環境與 MMX 一樣，都是將 8 個 x87 暫存器 ST0～ST7 的低 64 位元重新命名為 MMX 暫存器 MM0～MM7，並依平坦模式進行操作（即指令可以任意存取這 8 個暫存器中的任何一個而不必使用堆疊）。

由於 3DNow! 使用的暫存器與 x87 暫存器重疊，工作切換時，儲存 x87 暫存器狀態的同時也儲存了 3DNow! 的狀態，所以 3DNow! 不需要作業系統的額外支援。只要 CPU 支援 3DNow!，含有 3DNow! 代碼的程式可以在只考慮到 x87 狀態的原有的作業系統上不加修改地執行。

由於 3DNow! 依平坦模式存取暫存器，對 3DNow! 浮點單元的管線化變得容易，這也利於編譯器生成高效的浮點代碼。

## 3DNow! 指令集

3DNow! 和擴充 3DNow! 的 26 條指令從功能上可以分為以下五類。

### 單精度浮點運算指令

此類指令的運算元均為 64 位元，其高 32 位元和低 32 位元分別是 IEEE 754 格式的單精度浮點數。大部分指令一次可接受兩個這樣的運算元，並得到兩個單精度浮點數的結果。它們的組合語言輔助記憶碼都以 _PF_ 開頭。

3DNow! 還包含有計算單精度倒數和開方倒數的指令，並可以依程式需要，得到 12 位元精度和 24 位元精度的結果。這些指令一次只能處理一個單精度浮點數。

3DNow! 的一個特色是可以將同一暫存器內的 64 位元運算元中的兩個單精度浮點數相加或相乘，這在複數運算和內積運算中非常有用。Intel 直到最近才在 SSE3 指令集中增加了這項功能，稱之為「_水平操作_」。

為了保證與舊有作業系統的相容性，與 MMX 指令一樣，3DNow! 指令不引發任何算術異常。3DNow! 指令不會生成也不能正確處理 NaN 和非規格化數，也不支援指定捨入模式。因此 3DNow! 並不是 IEEE 754 的一個完整實現，即使是只涉及單精度浮點數時也不能完全代替 x87。

### 增強的 MMX 指令

_PAVGUSB_ 用於求 64 位元緊縮位元組（8×8 位元位元組）的平均值，可用於影片編碼中的像素平均和圖像縮放等。可能是意識到這個功能的重要性，Intel 在 SSE 中添加了功能完全相同的 _PAVGB_ 指令。

_PMULHRW_ 則用來補充 MMX 指令 _PMULHW_ 的不足，在緊縮字（4×16 位元字）相乘時可以得到比後者更準確的結果。Intel 直到最近才在 SSSE3 中增加了功能相似的指令 _PMULHRSW_。

_PSWAPD_ 指令用於交換緊縮雙字（2×32 位元字）中兩個雙字資料的位置。

### 資料類型轉換指令

_PF2ID_、_PI2FD_ 等 4 條指令用於完成整數和單精度浮點數之間的相互轉換。

### 資料預取指令

_PREFETCH/PREFETCHW_ 指令用於把將要使用到的資料從主記憶體提前載入快取中，以減少存取主記憶體的指令執行時的延遲。Intel 在 SSE 中添加了類似的 _PREFETCHTx_ 指令

### 快速登出 MMX 狀態指令

_FEMMS_ 指令與 MMX 中的 _EMMS_ 功能相同，用於登出 MMX 狀態。在 K6-2 和 K6-III 處理器中，_FEMMS_ 比 _EMMS_ 更快；在 Athlon 及更新的處理器中，_FEMMS_ 等同於 _EMMS_。

## 外部連結

* AMD 3DNow! Instruction Porting Guide（頁面存檔備份，存於網際網路檔案館）
* 3DNow!™Technology Manual（頁面存檔備份，存於網際網路檔案館）
* AMD Extensions to the 3DNow!™ and MMX™ Instruction Sets Manual（頁面存檔備份，存於網際網路檔案館）