繁体   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