速度與壓縮比如何兼得?壓縮算法在構建部署中的優化

背景

通常而言,服務發佈平臺的構建部署的流程(鏡像部署除外)會經過構建(同步代碼 -> 編譯 -> 打包 -> 上傳)、部署(下載包 -> 解壓到目標機器 -> 重啓服務)等步驟。以美團內部的發佈平臺 Plus 爲例,最近我們發現一些發佈項在構建產物打包壓縮的過程中耗時比較久。如下圖所示的 pack 步驟,一共消耗了1分23秒。

而在平常爲用戶解答運維問題的時候我們也發現,很多用戶會習慣將一些較大的機器學習或者 NLP 相關的數據放入到倉庫中,這部分數據往往佔據幾百兆,甚至佔據幾個GB的磁盤空間,十分影響打包的速度。 Java 項目也是如此,由於 Java 服務框架繁多,依賴也多,通常這些服務打包後也要佔據百兆級別的空間,耗時也會達到十多秒。下圖是我們的 pack 步驟的中位數,基本上大部分的 Java 服務和 Node.js 服務都至少要消耗 13s 左右的時間來做壓縮打包 。

pack 作爲幾乎所有需要部署的服務必需步驟,它目前的耗時基本上僅低於編譯和構建鏡像,因此,爲了提高整體構建的效率,我們準備對 pack 打包壓縮的步驟進行一輪優化工作。

方案對比

準備場景數據

發佈項的包大小分析

爲了儘可能地模擬構建部署中的應用場景,我們將 2020 年的部分構建包數據進行了整理分析,其中壓縮後的包大小如下圖所示,鐘形曲線說明了整體的包體積呈正態分佈,並且有着較明顯的長尾效應。壓縮後體積主要在 200M 以內,壓縮前的大小大致在 516.0MB 以內。

而 99%的服務壓縮包大小會在 1GB 以內,而對於壓縮步驟而言,其實越大的項目耗時越明顯,優化的空間越大。因此,我們在針對性的方案對比測試中選擇了 1GB 左右的構建包進行壓縮測試,既能覆蓋 99% 的場景,也可以看出壓縮算法之間比較明顯的提升。

這樣選擇的主要原因如下:

  1. 數據大的情況下計算結果會比小數據誤差小很多。
  2. 能夠覆蓋絕大多數應用場景。
  3. 效果對比明顯,可以看到是否有明顯的提升。

備註:由於在相同壓縮庫相同壓縮比等配置的情況下,Compression Speed 並沒有明顯變化,因此沒有做其它包體積的批量測試和數據彙總。

本文中我們使用的測試項目爲美團內部的較大型的 C++ 項目,其中文件類型除去 C++、Python、Shell 代碼文件,還有 NLP、工具等二進制數據(不包括 .git 中存儲的提交數據),數據類型比較全面。

目錄大小爲 1.2G,也可以比較清晰地對比出不同方案之間的差距。

gzip

gzip 是基於 DEFLATE 的算法,它是 LZ77Huffman 編碼 的結合。DEFLATE 的目的是爲了取代 LZW 和其他受專利保護的數據壓縮算法,因爲這些算法在當時限制了壓縮和其他流行的存檔器的可用性(Wikipedia)。

我們通常使用 tar -czf 命令來進行打包並且壓縮的操作,z 參數正是使用 gzip 的方式來進行壓縮。DEFLATE 標準(RFC1951)是一個被廣泛使用的無損數據壓縮標準。它的壓縮數據格式由一系列塊構成,對應輸入數據的塊,每一塊通過 LZ77 (基於字典壓縮,就是將最高概率出現的字母以最短的編碼表示)算法和 Huffman 編碼進行壓縮,LZ77 算法通過查找並替換重複的字符串來減小數據體積。

文件格式

  • 一個 10 字節的報頭,包含一個魔數 (1f 8b),壓縮方法 (比如 08 用於 DEFLATE),1 字節的 header flags,4 字節的時間戳,compression flags 和操作系統 ID。
  • 可選的額外 headers,包括原始文件名、註釋字段、“extra” 字段和 header 的 CRC-32 校驗碼 lower half 。
  • DEFLATE 壓縮主體。
  • 8 字節的 footer,包含 CRC-32校驗以及原始未壓縮的數據。

我們可以看到 gzip 是主要基於 CRC 和 Huffman LZ77 的 DEFLATE 算法,這也是後面 ISA-L 庫的優化目標。

Brotli

Alakuijala 和 Szabadka 在 2013-2016 年完成了 Brotli 規範,該數據格式旨在進一步提高壓縮比,它在優化網站速度上有大量應用。Brotli 規範的正式驗證是由 Mark Adler 獨立實現的。Brotli 是一個用於數據流壓縮的數據格式規範,它使用了通用的 LZ77 無損壓縮算法、Huffman 編碼和二階上下文建模(2nd order context modelling)的特定組合。大家可以參考這篇論文​查看其實現原理。

因爲語言本身的特性,基於上下文的建模方法 (Context Modeling)可以得到更好的壓縮比,但由於它的性能問題很難普及。當前比較流行的突破算法有兩種:

  • ANS:Zstd, lzfse
  • Context Modeling:Brotli, bz2

具體測試數據見下文。

Zstd

Zstd 全稱叫 Zstandard,是一個提供高壓縮比的快速壓縮算法,主要實現的編程語言爲 C,是 Facebook 的 Yann Collet 於2016年發佈的,Zstd 採用了有限狀態熵(Finite State Entropy,縮寫爲FSE)編碼器。該編碼器是由Jarek Duda 基於ANS 理論開發的一種新型熵編碼器,提供了非常強大的壓縮速度/壓縮率的折中方案(事實上也的確做到了“魚”和“熊掌”兼得)。Zstd 在其最大壓縮級別上提供的壓縮比接近 lzma、lzham 和 ppmx,並且性能優於 lza 或 bzip2。Zstandard 達到了 Pareto frontier(資源分配最佳的理想狀態),因爲它解壓縮速度快於任何其他當前可用的算法,但壓縮比類似或更好。

對於小數據,它還特別提供一個載入預置詞典的方法優化速度,詞典可以通過對目標數據進行訓練從而生成。

官方 Benchmark 數據對比

壓縮級別可以通過 --fast 指定,提供更快的壓縮和解壓縮速度,相比級別 1 會導致壓縮比率的一些損失,如上表所示。Zstd 可以用壓縮速度換取更強的壓縮比。它是可配置的小增量,在所有設置下,解壓縮速度都保持不變,這是大多數 LZ 壓縮算法(如 zlib 或 lzma)共享的特性。

  • 我們採用 Zstd 默認的參數進行了測試,壓縮時間 8.471s 僅爲原來的 11.266%,提升了 88.733%。
  • 解壓時間 3.211 僅爲原來的 29.83%,提升約爲 70.169%。
  • 同時壓縮率也從 2.548 提升到了 2.621。

LZ4

LZ4是一種無損壓縮算法,每核提供大於 500 MB/s的壓縮速度(大於0.15 Bytes/cycle)。它的特點是解碼速度極快,每核速度爲多 GB/s( 約1 Bytes/cycle )。

從上面的 Zstd 的 Benchmark 對比中,我們看到了 LZ4 算法效果十分出衆,因此我們也對 LZ4 進行了對比,LZ4 更加側重壓縮解壓速度,尤其是解壓縮的速度,壓縮比並不是它的強項,它默認支持 1-9 的壓縮參數,我們分別進行了測試。

LZ4 使用默認參數壓縮速度十分優秀,比 Zstd 快很多,但是壓縮比並不高,比 Zstd 壓縮後多了 206 MB,足足多了 46%,這就意味着更多的數據傳輸時間和磁盤空間佔用。即使是最大的壓縮比也並不高,僅僅從 1.79 提升到了 2.11,但是耗時卻從 5s 提升到了 51s。通過對比,LZ4 的確在壓縮率上並不是最優秀的方案,在 2.x 級別壓縮率上基本上時間優勢蕩然無存,而且還有一點,就是 LZ4 目前官方並沒有對多核 CPU 並行壓縮的支持,所以在後續的對比中,LZ4 喪失了壓縮解壓縮速度的優勢。

Pigz

Pigz 的作者 Mark Adler,同時也是 Info-ZIP 的 zip 和 unzip、GNU 的 gzip 和 zlib 壓縮庫的共同作者,並且是 PNG 圖像格式開發工作的參與者。

Pigz 是 gzip 的並行實現的縮寫,它主要思想就是利用多個處理器和核。它將輸入分成 128 KB 的塊,每個塊都被並行壓縮。每個塊的單個校驗值也是並行計算的。它的實現直接使用了 zlib 和 pthread 庫,比較易讀,而且重要的是兼容 gzip 的格式。Pigz 使用一個線程(主線程)進行解壓縮,但可以創建另外三個線程進行讀、寫和檢查計算,所以在某些情況下可以加速解壓縮。

一些博客在 i7 4790K 這樣的家用 PC 平臺中測試 Pigz 的壓縮性能時,並沒有十分高的速度,但在我們真機驗證的數據中提升要明顯很多。通過測試,它的壓縮時間執行速度只用了 1.768s,充分發揮了我們平臺物理機的性能,User 時間(CPU 時間之和)一共使用了 1m30.569s,這和前面的使用 gzip 單線程的方式速度幾乎是一個級別。壓縮率 2.5488 和正常使用 tar -czf 幾乎相差不多。

ISA-L Acceleration Version

ISA-L 是一套在 IA 架構上加速算法執行的開源函數庫,目的在於解決存儲系統的計算需求。 ISA-L 使用的是 BSD-3-Clause License ,因此在商業上同樣可以使用。

使用過 SPDK(Storage Performance Development Kit )或者 DPDK(Data Plane Development Kit)應該也聽說過 ISA-L ,前者使用了 ISA-L 的 CRC 部分,後者使用了它的壓縮優化。ISA-L 通過使用高效的 SIMD (Single Instruction, Multiple Data)指令和專用指令,最大化的利用 CPU 的微架構來加速存儲算法的計算過程。ISA-L底層函數都是使用手工彙編代碼編寫,並在很多細節上做了調優(現在經常要考慮 ARM 平臺,本文中所提及的部分指令在該平臺支持度不高甚至是不支持)。

ISA-L 對壓縮算法主要做了 CRC、DEFLATE 和 Huffman 編碼的優化實現,官方的數據指出 ISA-L 相比 zlib-1 有 5 倍的速度提升。

舉例來說,不少底層的存儲開源軟件實現的 CRC 都使用了查表法,代碼中存儲或者生成了一個 CRC value 的表格,然後計算過程中查詢值,ISA-L 的彙編代碼中包含了無進位乘法指令 PCLMULQDQ 對兩個64位數做無進位乘法,最大化 intel PCLMULQDQ 指令的吞吐量來優化 CRC 的性能。更好的情況是 CPU 支持 AVX-512,就可以使用 VPCLMULQDQ(PCLMULQDQ 在 EVEX 編碼的 512 bit 版本實現)等其它優化指令集(查看是否支持的方式見“附錄”)。

備註:截圖來自 crc32_ieee_by16_10.asm

使用

ISA-L 實現的壓縮優化級別支持 [0,3],3 爲壓縮比最大的版本,綜合考慮我們採用了最大的壓縮比,雖然壓縮比 2.346 略低於 gzip 不過影響不大。在 2019 年 6 月發佈的 v2.27 版本里,ISA-L 加了多線程的 Feature,因此在後續的測試中,我們採用了多線程併發的參數,效果提升比較顯著。

由於我們構建機器的系統爲 Centos7 需要自行編譯 ISA-L 的依賴,比如 nasm 等庫,所以在安裝配置上會比較複雜,ISA-L 支持多種優化參數比如,IGZIP_HIST_SIZE(壓縮過程中加大滑動窗口),LONGER_HUFFTABLES,更大的 Huffman 編碼表,這些編譯參數也會對庫有很大提升。

壓縮時間

real  0m1.372s
user  0m5.512s
sys 0m1.791s

測試後的效果相當驚人,是目前對比方案中最爲快速的,時間上節省了 98.09%。

由於和 gzip 格式兼容,因此同樣可以使用 tar -xf 命令進行解壓,後續的解壓縮測試過程中,我們使用的仍然是 ISA-L 提供的解壓方式。

Pzstd

通過 Pigz 的測試,我們就在想,是否 Zstd 這樣優秀的算法也可以支持並行呢,在官方的 Repo 中,我們十分驚喜地發現了一個“寶藏”。

Pzstd 是 C++11 實現的並行版本的 Zstandard (Zstd 也在這之後加入了多線程的支持),類似於 Pigz 的工具。 它提供了與 Zstandard 格式兼容的壓縮和解壓縮功能,可以利用多個 CPU 核心。 它將輸入分成相等大小的塊,並將每個塊獨立壓縮爲 Zstandard 幀。 然後將這些幀連接在一起以產生最終的壓縮輸出。 Pzstd 同樣支持文件的並行解壓縮。 解壓縮使用 Zstandard 壓縮的文件時,PZstandard 在一個線程中執行 IO,而在另一個線程中進行解壓縮。

下圖是和 Pigz 的壓縮和解壓縮速度對比,來自官方 Github 倉庫(機器配置爲:Intel Core i7 @ 3.1 GHz, 4 threads),效果比 Pigz 還要出色,具體對比數據見下文:

Pied Piper (Middle-out compression)

Middle-out Compression 最初是在美劇中提到的一個概念,不過現在已經有了一個真正的實現 middle-out,該算法目前小範圍應用在壓縮時序數據中,由於缺乏成熟地理論支撐以及數據對比,沒有正式進入方案的對標。

備註:Pied Piper 是美劇《硅谷》 中虛擬出來的公司和算法。

兼容性

本文中調研所提及的對比方案,都是統一在構建集羣中的機器中進行測試,由於構建系統 在線上的機器集羣配置高度統一(包括硬件平臺和系統版本),所以兼容性表現和測試數據也高度吻合。

選型

實際上,部分官方的測試機器的配置和美團構建平臺的物理機配置並不一致,場景可能也有區別,上面引用的測試結果中所使用的CPU以及平臺架構和編譯環境和我們所用的也有些出入,而且大多數還是家用的硬件比如 i7-4790K 或者 i9-9900K,這也是需要使用構建平臺的物理機和具體的構建包壓縮場景來進行測試的原因,因爲這樣才能得出最接近我們使用場景的數據。

對比數據

幾個方案的數據對比如下表格(在本文中的時間數據選擇是通過多次運行後,選擇結果的中位數):

壓縮時間對比

從整個構建後的壓縮構建包的時間可視化圖中可以看出,最初版本的 gzip 壓縮相當耗時,而採取 Pzstd 是最快速的方案,ISA-L 稍慢,Pigz 略微慢一點,這三者都可以達到從 1m11s 到 1s 左右的優化,最快可以節省 98.51% 的壓縮時間

解壓縮時間對比

解壓縮的時間上並沒有像壓縮效率相差很多,在 GB 級別的項目中壓縮比 2.5-2.6 範圍內時,時間都在 11s 以內。而且爲了最大兼容已有的實現和保持穩定性,解壓方案優先考慮兼容 gzip 格式的策略,這樣對部署目標機器的侵入性最小,即可以使用 tar -xf 解壓的方案優先。

而在後面的方案實施中,由於部署需要穩定可靠的環境,所以我們暫時沒有對部署機器做環境改造。

下面的時間對比是分別使用各自的解壓方案的對比:

  • Pzstd 解壓速度最快,相比 Gzip 節省了 86.241% 的時間。
  • Zstd 算法的解壓縮效率其次,大約可以節省 70.169% 的解壓時間。
  • ISA-L 可節省 61.9658% 的時間。
  • Pigz 可節省 43.357% 的解壓時間。
  • Brotli 解壓可以節省 29.02% 的時間。

壓縮比的對比

壓縮比的對比中 Zstd 和 Pzstd 有一些優勢,其中 Brotli 和 LZ4 由於支持的參數限制,比較難測試同級別壓縮比下的速度,因此選擇了壓縮比稍低的參數,但是效率仍然距離 Pigz 和 Pzstd 存在一些差距。

而 ISA-L 的實現在壓縮比上有一些犧牲,不過並沒有差距很大。

優劣分析總結

在本文開始階段,我們介紹了構建部署流程,因此我們此次優化的目標時間大致計算公式如下:

在測試案例對比中,時間耗時的順序爲 Pzstd < ISA-L < Pigz < LZ4 < Zstd < Brotli < Gzip (排名越靠前越好),其中壓縮和解壓縮的時間在整體的耗時上佔比較大,因此備選策略爲 Pzstd、ISA-L、Pigz。

當然,這其中還存在磁盤空間的成本優化問題,即壓縮比可能對磁盤空間產生優化,但是對構建的耗時會產生負優化,不過由於目前時間維度是我們優化的主要目標,比磁盤成本和上傳帶寬成本要重要,因此壓縮比採用了較爲普遍或者默認最優的壓縮比方案,即在 2.3 - 2.6 範圍內。不過在一些內存型數據庫等存儲介質成本較爲高的場景中,也許要綜合多個方面需要更多考量,請大家知悉。

評分策略

比較於 gzip,新算法都具有想當不錯的速度,但是由於壓縮格式的向前不兼容,加上需要客戶端(部署目標機器)的支持,可能方案實施週期會較長。

而對於部署來說,可能收益並不是十分明顯,反而加重了一些維護和運維成本,所以我們暫時沒有把 Pzstd 的方案放到較高的優先級。

選型的策略主要有基於如下原則:

  • 整體耗時優化提升最大,這也是整體優化方案的出發點。
  • 保證最大兼容性,爲了讓接入構建平臺的業務和平臺減少改動成本,需要保持方案的兼容性(優先考慮最大兼容的策略,即兼容 gzip 的方案優先)。
  • 保證部署目標機器環境的穩定和可靠,選擇對部署機器侵入最小的方案,這樣無需安裝客戶端或者庫。
  • 壓縮場景在真機模擬測試中完全契合美團構建平臺的場景,即在我們現有的物理機平臺和目標壓縮場景中對比數據效果良好。
  • 其實本問題更全面的評分角度有很多維度,比如對象存儲的磁盤成本、帶寬成本、任務耗時,甚至是機器成本,不過爲了簡化整體方案的選型,我們省略了一些計算,同時壓縮比的對比選擇上也選擇了各自官方推薦的範圍。

綜合以上幾點,決定一期採取 ISA-L 的方式加速,可以最穩定並且較高速地提升構建平臺的效率,未來可能會實現 Pzstd 的方案,下面的數據爲一期的結果。

優化效果

爲了方便結果的展示,我們過濾出了部分打包時間較長的發佈項展示出來(這些耗時很久的項目往往十分影響用戶的使用體驗,而且總體的佔比在 10% 左右),這部分任務優化的時間從 27s 到 72s 不等,過去越是項目大的項目壓縮時間越長,如今壓縮時間都可以穩定在 10s 以內,而且是在我們的機器同時執行多個任務的情況下。

而後我們將優化前的 Pack 步驟(壓縮+上傳)部分打點數據,以及優化後的部分打點數據做了彙總,得出了平均的優化效果對比,數據如下:

  1. 在我們之前的一個構建包的統計中,多數的構建包壓縮後在 100MB 左右,壓縮前大概是在 250MB,按照 gzip 算法的壓縮速度的確會在 10s 左右的級別。
  2. 由於構建的物理機可能同時運行多個任務,所以實際壓縮效果會比測試中稍微耗時多一點。

壓縮平均節省了 90% 的時間。

寫在後面

  • 由於文中提到的一些方案涉及到具體平臺環境的 CPU 指令集,甚至庫的編譯環境,編譯參數也會影響具體的效果,所以推薦在方案實施的時候對集羣環境保持統一,也可爲集羣環境做特殊的定製優化。
  • 爲 Centos 打包 RPM 文件的時候尤其需要注意下編譯環境的配置,否則可能效果和測試會有出入。
  • Java 的 Jar 包 和 War 包也可能進行壓縮,針對這種場景,壓縮率的確提升不大,但是速度依舊有提升。

作者簡介

宏達,美團基礎技術部研發工程師。

團隊簡介

基礎技術部-研發質量及效率部-代碼倉庫和構建組,團隊旨在建設代碼倉庫管理、協作及代碼構建能力,完善多維度的工作流執行引擎及構建工具鏈,與公司其他研發工具打通,提高業務整體的開發、交付效率。

附錄

機器環境

文中的測試統一在如下物理機中進行,測試中使用相同的目標文件。測試 機使用的是非 PCIE SSD 磁盤。

inxi -Fx 省略部分數據輸出如下,其中一些並行指令集在優化中可能會使用到。其中 flags 中可以看到支持 avx avx2 指令集,並不支持 avx-512 ,不過仍然有很大性能提升。
System:   Host: ****** Kernel: ****** bits: 64 compiler: gcc v: 4.8.5 Console: tty 7 Distro: CentOS Linux release 7.1.1503 (Core)

CPU:       Info: 2x 16-Core model: Intel Xeon Gold 5218 bits: 64 type: MT MCP SMP arch: Cascade Lake rev: 7 L2 cache: 44.0 MiB

           flags: avx avx2 lm nx pae sse sse2 sse3 sse4_1 sse4_2 ssse3 vmx bogomips: 293978 Speed: 2300 MHz min/max: N/A 

Info:        Processes: 764  Memory: 187.19 GiB used: 15.05 GiB (8.0%) Init: systemd runlevel: 3 Compilers: gcc: 4.8.5


/proc/cpuinfo 文件中也可以查看 CPU 支持的指令集

flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt ssbd mba ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts pku ospke spec_ctrl intel_stibp flush_l1d arch_capabilities

參考文獻

招聘信息

美團研發質量及效率部 - 研發平臺團隊,致力於建設業界一流的持續交付平臺,現招聘構建與製品庫、代碼倉庫、服務發佈、流水線等多個方向的工程師,座標北京/上海。歡迎感興趣的同學加入。可投遞簡歷至:[email protected](郵件主題請註明:美團研發質量及效率部 - 研發平臺)

| 想閱讀更多技術文章,請關注美團技術團隊(meituantech)官方微信公衆號。

| 在公衆號菜單欄回覆【2019年貨】、【2018年貨】、【2017年貨】、【算法】等關鍵詞,可查看美團技術團隊歷年技術文章合集。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章