Proto 序列化不是規範的

解釋了序列化如何工作以及為什麼它不是規範的。

許多人希望序列化的 proto 能夠規範地表示 proto 的內容。用例包括:

  • 將序列化的 proto 用作雜湊表中的鍵
  • 對序列化的 proto 進行指紋識別或校驗和
  • 透過比較序列化的有效負載來檢查訊息相等性

不幸的是,protobuf 序列化不是(也無法是)規範的。有一些值得注意的例外,例如 MapReduce,但總的來說,您應該將 proto 序列化視為不穩定的。本頁解釋了原因。

確定性並非規範性

確定性序列化並非規範的。序列化器可能由於多種原因生成不同的輸出,包括但不限於以下變化:

  1. protobuf 模式以任何方式發生變化。
  2. 正在構建的應用程式以任何方式發生變化。
  3. 二進位制檔案使用不同的標誌(例如,最佳化版本與除錯版本)進行構建。
  4. protobuf 庫已更新。

這意味著序列化 proto 的雜湊是脆弱的,並且在時間或空間上不穩定。

序列化輸出發生變化的原因有很多。上述列表並非詳盡無遺。其中一些是問題空間中固有的困難,即使我們願意,也會使其效率低下或不可能保證規範序列化。另一些是我們有意未定義的事項,以提供最佳化機會。

穩定序列化的固有障礙

Protobuf 物件保留未知欄位,以提供前向和後向相容性。未知欄位的處理是規範序列化的主要障礙。

線上路格式中,位元組欄位和巢狀子訊息使用相同的線型別。這種模糊性使得無法正確地將儲存在未知欄位集中的訊息規範化。由於完全相同的內容可能是其中之一,因此無法知道是將其視為訊息並遞迴處理,還是不處理。

為了效率,實現通常在已知欄位之後序列化未知欄位。然而,規範序列化需要根據欄位編號將未知欄位與已知欄位交錯。這將對所有使用者(即使是不需要此功能的使用者)施加顯著的效率和程式碼大小成本。

有意未定義的事項

即使規範序列化是可行的(也就是說,如果我們能夠解決未知欄位問題),我們也會有意將序列化順序保留為未定義,以允許更多的最佳化機會:

  1. 如果我們能證明某個欄位在二進位制檔案中從未使用過,我們可以將其從模式中完全刪除,並將其作為未知欄位處理。這可以節省大量的程式碼大小和 CPU 週期。
  2. 可能有機會透過將同一欄位的向量一起序列化來進行最佳化,即使這會破壞欄位編號順序。

為了給這樣的最佳化留下空間,我們希望在某些配置中故意打亂欄位順序,以便應用程式不會不恰當地依賴於欄位順序。