[英]Using Python's reduce() to join multiple PySpark DataFrames
有誰知道為什么使用Python3的functools.reduce()
會導致加入多個PySpark DataFrame時性能更差,而不僅僅是使用for
循環迭代加入相同的DataFrame? 具體來說,這會導致大量減速,然后出現內存不足錯誤:
def join_dataframes(list_of_join_columns, left_df, right_df):
return left_df.join(right_df, on=list_of_join_columns)
joined_df = functools.reduce(
functools.partial(join_dataframes, list_of_join_columns), list_of_dataframes,
)
而這一個不是:
joined_df = list_of_dataframes[0]
joined_df.cache()
for right_df in list_of_dataframes[1:]:
joined_df = joined_df.join(right_df, on=list_of_join_columns)
任何想法將不勝感激。 謝謝!
只要您使用CPython(不同的實現可以,但實際上不應該,在這種特定情況下表現出明顯不同的行為)。 如果你看看reduce
實現,你會發現它只是一個for循環,只需要很少的異常處理。
核心完全等同於您使用的循環
for element in it:
value = function(value, element)
並且沒有證據支持任何特殊行為的主張。
此外,簡單的測試具有幀數的實際限制Spark連接(連接是 Spark 中最昂貴的操作 )
dfs = [
spark.range(10000).selectExpr(
"rand({}) AS id".format(i), "id AS value", "{} AS loop ".format(i)
)
for i in range(200)
]
直接for循環之間的時序顯示沒有顯着差異
def f(dfs):
df1 = dfs[0]
for df2 in dfs[1:]:
df1 = df1.join(df2, ["id"])
return df1
%timeit -n3 f(dfs)
## 6.25 s ± 257 ms per loop (mean ± std. dev. of 7 runs, 3 loops each)
並reduce
調用
from functools import reduce
def g(dfs):
return reduce(lambda x, y: x.join(y, ["id"]), dfs)
%timeit -n3 g(dfs)
### 6.47 s ± 455 ms per loop (mean ± std. dev. of 7 runs, 3 loops each)
類似地,整個JVM行為模式在for循環之間是可比較的
並reduce
最后兩者都生成相同的執行計划
g(dfs)._jdf.queryExecution().optimizedPlan().equals(
f(dfs)._jdf.queryExecution().optimizedPlan()
)
## True
這表明在評估計划並且可能發生OOM時沒有區別。
換句話說,您的相關性並不意味着因果關系,並且觀察到的性能問題不太可能與您用於組合DataFrames
的方法相關。
一個原因是減少或折疊通常在功能上是純粹的:每次累積操作的結果不是寫入存儲器的相同部分,而是寫入新的存儲器塊。
原則上,垃圾收集器可以在每次累積后釋放前一個塊,但如果不是,則為每個更新版本的累加器分配內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.