Cloud TPU 效能指南
排解 TPU 效能問題的第一步是剖析模型。如要進一步瞭解如何擷取效能設定檔,請參閱「在 Cloud TPU 上剖析模型」。
TPU 模型效能
本節說明可能導致模型效能降低的一般問題,以及解決方法。
模型受輸入內容限制
TPU 的運算速度非常快,為確保 TPU 不會閒置,請務必確保有穩定的資料串流載入 TPU。實際做法取決於您載入及前處理資料集的方式。 舉例來說,您可以使用 tf.data.TFRecordset() 和
num_parallel_reads
參數,平行讀取資料檔案。由於分片 (在核心之間分割批次),批次大小過小
TPU 執行階段會將批次作業分割到 TPU 裝置的所有 8 個核心 (例如 v2-8 或 v3-8)。如果您指定全域批次大小為 128,則每個核心會收到批次大小為 16 (128 / 8)。
如要達到最佳記憶體用量,請使用可容納於 TPU 記憶體中的最大批次大小。每個 TPU 核心都會使用二維 8 X 128 向量暫存器,處理矩陣乘法。一般而言,批量大小應可被 8 或 128 整除。
記憶體管理調整
您可以使用
TPU_PREMAPPED_BUFFER_SIZE
環境變數,微調低階執行階段行為。
說明:
TPU_PREMAPPED_BUFFER_SIZE
設定主機記憶體緩衝區的大小 (以位元組為單位),這個緩衝區會預先對應並固定,供 TPU 執行階段用於資料傳輸 (例如 DMA)。預設值為 4294967296 個位元組。 該值必須是 2^12 的倍數 (4KB = 4 * 1024 Bytes = 4096 = 2^12)。以下是有效的 TPU_PRE_MAPPED_BUFFER_SIZE 值。
17179869184 = 2^34 = 2^22 * 2^12 (2^22 4KB pages will be premapped). 40000000000 = 5^10 * 2^12 = (5^10 4KB pages will be premapped).
影響:增加這個大小可能會提升主機與 TPU 裝置之間的資料傳輸效能,特別是針對具有大型張量或頻繁主機與裝置通訊的工作負載。不過,這也會增加釘選的主機記憶體量,減少其他程序可用的記憶體。
緩衝區大小
如果預先對應的緩衝區不夠大,無法在程式執行階段分配記憶體,工作負載就會失敗,並傳回類似以下的
RESOURCE_EXHAUSTED
錯誤:「Allocating buffer from premmaped region failed with:
RESOURCE_EXHAUSTED
: Attempting to allocateallocation_size
. 這是不可能的。有available_size
免費。」如果緩衝區過大,TPU 初始化時間可能會大幅延長 (可能超過 15 秒),導致 TPU 似乎卡住。
如要診斷這個問題,請檢查 TPU 執行階段記錄。這些記錄檔會詳細說明執行的作業,包括緩衝區的預先對應。您可以在 /tmp/tpu_logs/tpu_driver.INFO 中找到記錄,也可以設定環境變數 TPU_STDERR_LOG_LEVEL=0,直接將記錄列印到控制台。這項設定會產生類似以下的輸出內容:
I0604 12:45:24.926233 62136 tpu_hal.cc:214] Starting premapped memory manager initialization... I0604 12:45:29.411218 62136 system.cc:1059] tpu::System initialized, current host id: 0, logical device ids: 0 I0604 12:45:29.411244 61600 tfrt_tpu_system_state.cc:216] CreateTpuSystemState: TPU initialization is successful and it took 5.583190661s I0604 12:45:29.411267 61600 tfrt_tpu_system_state.cc:220] CreateTpuSystemState: using TPU host premapped buffer of size: 4294967296 ``` This output will tell you how long it took to initialize the TPU and the size of the premapped buffer.
用法:如果預先對應的緩衝區太小或太大,可以使用下列環境變數手動設定緩衝區大小。
TPU_PREMAPPED_BUFFER_SIZE: Sets the total size (in bytes) of the pre-mapped buffer region. TPU_PREMAPPED_BUFFER_TRANSFER_THRESHOLD_BYTES: Sets the maximum size of a single buffer that can be allocated from the pre-mapped region.
例如,您可以:
export TPU_PREMAPPED_BUFFER_SIZE=4294967296
設定緩衝區大小,並:
export TPU_PREMAPPED_BUFFER_TRANSFER_THRESHOLD_BYTES ``` to enable it. This export sets the size to the default.
指引:如果懷疑主機與裝置間的資料傳輸是瓶頸,請調整 TPU_PREMAPPED_BUFFER_SIZE 的值。監控主機記憶體用量和模型效能,找出最佳平衡點。在大多數情況下,預設值已足夠。
XLA 編譯器最佳化
XLA 是機器學習的編譯器,可為 TPU、CPU、GPU 和其他平台產生二進位檔。XLA 是標準 TensorFlow 程式碼庫的一部分,但也可以用於 PyTorch 和 JAX 模型。Cloud TPU 的模型會轉譯為 XLA 圖表,然後由 XLA 編譯為 TPU 可執行檔。如要進一步瞭解 XLA,請參閱「XLA:機器學習的最佳化編譯器」。
填充
如要有效運用 TPU 記憶體,請將資料結構化,以便將資料分塊為 128 x 8 的大小。如果矩陣計算的資料未填滿整個 128 x 8 區塊,XLA 編譯器會填補張量。填充有兩項缺點:
- 填充張量會使 TPU 核心使用率過低。
- 填充會增加張量所需的晶載記憶體儲存量,可能會造成記憶體不足的錯誤。
雖然填充是由 XLA 編譯器於必要時自動執行,但您可利用記憶體檢視器工具決定執行的填充量。您可以選擇適合 TPU 的張量維度,避免填補。
張量維度
如要達到尖峰 FLOPS,矩陣乘積的維度應大於所用 TPU 版本的 MXU 大小。MXU 大小為 256 x 256 (適用於 v6e),以及 128 x 128 (適用於 v6e 之前的版本)。詳情請參閱「Cloud TPU 系統架構」。
批量
XLA 編譯器會將儲存在 TPU HBM 記憶體中的張量大小向上取整,以便更有效率地執行運算。這項填補作業會在硬體層級以透明方式進行,不會影響結果。但在某些情況下,填充將造成記憶體用量和執行時間大幅增加。
TPU 執行階段會在記憶體中配置張量,盡量提高運算效率並減少填充。如要將記憶體負擔降至最低,並同時獲得最高的運算效率,就必須滿足下列其中一項條件:
總批量應為 64 的倍數 (每個 TPU 核心為 8),且特徵維度大小應為 128 的倍數。
總批量應為 1024 的倍數 (每個 TPU 核心為 128),且特徵維度大小應為 8 的倍數。
批量為 1024 且特徵維度為 128 的倍數時,將可獲得最佳效率。不過這樣的配置可能不適用於所有模型。
Fusion
融合是 XLA 編譯器用來最佳化程式的一般技術。融合作業是多個組成作業的組合,這些作業會一併執行。
例如請考量以下的運算系列:
tmp = tf.add(x, y)
result = tf.multiply(tmp, z)
此程式碼大致等同於下列虛擬程式碼:
for (i = 0; i < element_count; i++) {
tmp[i] = x[i] + y[i];
}
for (i = 0; i < element_count; i++) {
result[i] = tmp[i] * z[i];
}
若進行融合,陣列存取就會同時發生:
for (i = 0; i < element_count; i++) {
result[i] = (x[i] + y[i]) * z[i];
}
在這個範例中,記憶體往返次數減少,XLA 也不需要為「tmp」分配任何空間。
融合是關鍵最佳化,以多種方式讓 Cloud TPU 受益:
- 不需要在緩慢的主記憶體儲存中繼結果,減少記憶體傳輸。
- 可提升未利用硬體單元的使用率。
- 可減少模型記憶體使用率,原因是同時間需要上線的緩衝區數量減少。
廣播
如果兩個張量結合,其中各具有不同但相容的形狀,就會隱含產生廣播。
舉例來說,tf.add(vector, matrix)
需要將向量廣播至矩陣形狀。運算結果的形狀與矩陣相同。詳情請參閱廣播陣列指南。
雖然廣播通常可以與消費者融合,但強制廣播可能會導致效能不佳和記憶體用量增加。
在下列範例中,廣播在新增向量情況下隱含,而矩陣無法與 argmax 融合,造成具體化的廣播:
`tf.argmax(tf.add(vector, zero_matrix), axis=0)`