Polars 2.0 正式發布
Release of Polars 2.0
Polars 2.0 正式發布,新增 out‑of‑core 支援、Map dtype 以及對 SQL 的全面最佳化。
- out‑of‑core:預設 80% RAM 會自動 spill‑to‑disk,支援 sort、window、expressions 等。
Polars 2.0 以 SQL 為首席,提供 out-of-core 支援與 Map dtype,對資料工程師提升工作效率與 AI 迭代速度。
今天我們正式釋出 Polars 2.0。先前的公告文章已說明版本提升的原因,這篇文章將討論 2.0 帶來的功能。雖然我們並未打算推出大幅功能更新,但它仍包含許多值得興奮的內容。
讓我們一起看看此版本的重點:
- 我們已啟用初始版本的 out-of-core(寫入磁碟)支援,
- 大量核心效能提升,
- 一流的 SQL 支援,結合效能提升,Polars 在 TPC-H 與 TPC-DS1 基準測試中領先 DataFusion 與 DuckDB,
- 新增
Map資料型別,且 - Polars 對資料型別更嚴格且更明確,帶來更快的回饋與 AI 迭代速度。
效能與 SQL 作為首要公民
Polars 2.0 將成為我們將 SQL 作為首要公民的分水嶺。Polars SQL 的覆蓋範圍在過去幾個月顯著增長。我們知道過去幾年已建立了堅實的引擎。在 Polars 2.0 中,我們希望將其擴充套件到更多工作負載,包括 SQL。為了提升效能,我們釋出了許多最佳化器和引擎的改進。此處的重點包括 join reordering、更加優秀的 common-subplan-elimination 以及動態 predicate / bloom filter。
為了了解我們在典型 SQL 基準測試中的表現,我們在 TPC-H 與 TPC-DS1 產生的資料上執行 Polars SQL,並將其與最新的 DuckDB 釋出 (1.5.6)、DuckDB 2.0 alpha (2.0.0.dev2610011535) 以及最新的 DataFusion 釋出 (54.0.0) 進行比較,測試環境為 c7a.4xlarge (16 vCPU, 32GB RAM) 與 c7a.metal (192 vCPU, 384GB RAM)。每個查詢以熱設定執行 5 次,並為每個查詢啟動獨立程序,設定 60 秒超時。每次引擎/基準之間都會清除檔案快取(非每個查詢之間)。對於每個查詢,我們採取 5 次執行中的最佳結果,並在總時間與幾何平均時間上比較各引擎。
資料是使用 tpcgen-cli parquet 從原始碼在提交 99bedae 編譯而成。我們檢查了 tpcgen-cli 的預設行組大小,並確認其大致與 Polars scan_csv 透過 sink_parquet 以及 DuckDB COPY 產生的結果相似。SQL 查詢是使用 DuckDB 1.5.6 的 tpch_queries() 與 tpcds_queries() 產生的。資料存放於 EBS。
下列圖表顯示各引擎在秒為單位的執行時間(越低越好),按機器分組。
c7a.4xlarge (16 vCPU, 32 GB)
c7a.metal (192 vCPU, 384 GB)
Polars 與兩個 DuckDB 版本完成了所有查詢。DataFusion 在 TPC-DS q72(以及一次 q67)上逾時,並在 c7a.4xlarge 上的 TPC-H q18 記憶體耗盡;這些查詢已從所有引擎的結果中排除。
我們觀察到預設 Polars 在除一項基準外均最快。Polars 在擴充套件至 192 執行緒時會產生固定開銷,影響小資料查詢。實際上,將 Polars 限制為 32 核心時,在所有基準中都具競爭力甚至領先。我們已在內部診斷原因,並希望在下一版修復此問題。更多基準資訊可參閱 附錄。我們鼓勵您重現我們的結果,並已在此處分享該基準的儲存庫:https://github.com/pola-rs/polars-2.0-benchmark。
串流引擎與 OOC 成為預設
這是 2.0 版本中最具影響力的改變之一。對 LazyFrame 呼叫 collect 現在預設使用串流引擎,從而在大多數查詢中帶來巨大的記憶體與效能提升。之所以需要升級到主要版本,是因為串流引擎在某些操作(join、group_by、unpivot 等)預設不保證列順序。如果您需要在這些操作中觀察到列順序,可以透過設定 maintain_order=True 來選擇。
Out-of-core(溢寫至磁碟)現在預設已啟用。它會在 RAM 約 80% 時開始溢寫(可能需要調整)。目前支援 out-of-core 的操作(排序、視窗函式、多數表示式)現在可以開始溢寫到磁碟以完成查詢。預設磁碟預算為 64GB。未來我們也將為連線(join)和分組(group‑by)啟用 out-of-core。
這兩項改變將使 Polars 在高記憶體工作負載下對於一般資料實務者更具韌性。並且隨著 out-of-core join 與 group-by 進入我們的規劃,這種韌性將進一步提升。
新增 Map 資料型別
Polars 現在直接支援 Arrow MapType 作為 Polars Map 資料型別。您可以將 Map 視為 Python 字典,將鍵對映到值。2.0 之前,Arrow MapType 在 Polars 中被讀取為 List(Struct({"key": ..., "value": ...})).
df = pl.DataFrame(
{
"user": ["alice", "bob", "carol"],
"scores": pl.Series(
[{"math": 90, "art": 75}, {"math": 60}, {}],
dtype=pl.Map(pl.String, pl.Int64),
),
"subject": ["art", "art", "math"],
}
)shape: (3, 3)
┌───────┬─────────────────────────┬─────────┐
│ user ┆ scores ┆ subject │
│ --- ┆ --- ┆ --- │
│ str ┆ map[str, i64] ┆ str │
╞═══════╪═════════════════════════╪═════════╡
│ alice ┆ {"math": 90, "art": 75} ┆ art │
│ bob ┆ {"math": 60} ┆ art │
│ carol ┆ {} ┆ math │
└───────┴─────────────────────────┴─────────┘# Key lookups and dictionary-like methods:
df.select(
"user",
pl.col("scores").map.get("math").alias("math"), # fixed key
pl.col("scores").map.get(pl.col("subject")).alias("by_subject"), # key from another column
pl.col("scores").map.contains_key("art").alias("has_art"),
pl.col("scores").map.len().alias("n"),
pl.col("scores").map.keys().alias("keys"),
pl.col("scores").map.values().alias("values"),
)┌───────┬──────┬────────────┬─────────┬─────┬─────────────────┬───────────┐
│ user ┆ math ┆ by_subject ┆ has_art ┆ n ┆ keys ┆ values │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 ┆ bool ┆ u32 ┆ list[str] ┆ list[i64] │
╞═══════╪══════╪════════════╪═════════╪═════╪═════════════════╪═══════════╡
│ alice ┆ 90 ┆ 75 ┆ true ┆ 2 ┆ ["math", "art"] ┆ [90, 75] │
│ bob ┆ 60 ┆ null ┆ false ┆ 1 ┆ ["math"] ┆ [60] │
│ carol ┆ null ┆ null ┆ false ┆ 0 ┆ [] ┆ [] │
└───────┴──────┴────────────┴─────────┴─────┴─────────────────┴───────────┘作為受支援的資料型別,map 型別現在將擁有專屬的表示式,例如鍵查詢、遍歷值以及其他類似字典的方法。
更嚴格的 Polars
Polars 旨在嚴格且快速失敗。錯誤應該理想上即時丟擲,而不是在流水線進行 20 分鐘後才報錯。對資料不匹配的隱式行為應該是可選的,而非預設,因為這些不匹配可能會隱藏錯誤。隨著 AI 驅動開發的興起,這種嚴格性變得更有價值。代理程式可透過呼叫 collect_schema() 早期驗證查詢結構,該方法會解析型別並在不實際載入任何資料的情況下捕捉架構層級的不匹配。這確保了快速回饋,使代理程式與人類能更快迭代。並非所有錯誤都能在查詢計畫編譯時捕捉,有些取決於資料。在這些情況下,Polars 會預設採用更嚴格的行為,以確保不一致被捕捉,而不是靜默產生不同結果。參見 previous posts 以及其中 Polars 變得更嚴格的一些範例。
最後的話
我們非常興奮 Polars 2.0 已經發布。未來幾個月,我們將在已經在的路徑上進一步改進。更好的 out-of-core、更好的大 CPU 數量擴充套件,以及在 Polars Cloud 上,我們的目標是成為最快的分散式引擎。我們也開始開發 GeoPolars,希望能很快帶來更多相關訊息。 若您在新版本中發現任何問題,請開啟 issue:https://github.com/pola-rs/polars/issues。最後,為協助您升級到 2.0,我們已發布 migration guide。
基準測試附錄
以下為絕對數值(單位為秒)。粗體數字代表每行最快的引擎;顏色越深表示相對於最快引擎越慢。Polars 使用 32 執行緒僅在 c7a.metal 上執行。
查詢時間總和
查詢時間幾何平均值
Polars 也在更大的資料上隨著核心數量增加而擴充套件良好。在 SF100 上從 16 到 192 vCPU 的移動,使 Polars 在 TPC‑H 上快 3.8 倍,在 TPC‑DS(按總和)上快 2.2 倍,與 DuckDB 1.5.6 的 3.2 倍和 1.9 倍、DuckDB 2.0 alpha 的 2.2 倍和 1.5 倍,以及 DataFusion 的 1.7 倍和 1.0 倍相比。 在 SF10 上,額外的核心對 Polars 的預設設定沒有幫助:它在 TPC‑H 上速度相同,在 TPC‑DS 上慢 1.8 倍,而 DuckDB 1.5.6 仍然快 1.8 倍和 1.3 倍。Polars 以 32 執行緒僅在 c7a.metal 上執行,因此不屬於此比較。
腳註
來源:hackernews100 · pola.rs