簡體   English   中英

使用Python的reduce()連接多個PySpark DataFrame

[英]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循環之間是可比較的

用於循環CPU和內存使用 - VisualVM

reduce

減少CPU和內存使用 - VisualVM

最后兩者都生成相同的執行計划

g(dfs)._jdf.queryExecution().optimizedPlan().equals( 
    f(dfs)._jdf.queryExecution().optimizedPlan()
)
## True

這表明在評估計划並且可能發生OOM時沒有區別。

換句話說,您的相關性並不意味着因果關系,並且觀察到的性能問題不太可能與您用於組合DataFrames的方法相關。

一個原因是減少或折疊通常在功能上是純粹的:每次累積操作的結果不是寫入存儲器的相同部分,而是寫入新的存儲器塊。

原則上,垃圾收集器可以在每次累積后釋放前一個塊,但如果不是,則為每個更新版本的累加器分配內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM