[英]pySpark forEachPartition - Where is code executed
我在 2.3 版中使用 pySpark(在我當前的開發系統中無法更新到 2.4)並且有以下關於foreachPartition的問題。
首先是一點背景:據我了解,pySpark- UDFs
強制 Python 代碼在 Python 實例中的 Java 虛擬機 (JVM) 之外執行,從而降低性能。 由於我需要對我的數據應用一些 Python 函數並希望將開銷成本降至最低,因此我想到了至少將一組可處理的數據加載到驅動程序中並將其作為 Pandas-DataFrame 進行處理。 無論如何,這將導致 Spark 失去並行優勢。 然后我讀到foreachPartition
將函數應用於分區內的所有數據,因此允許並行處理。
我現在的問題是:
當我通過foreachPartition
應用 Python 函數時,Python 執行是否發生在驅動程序進程中(分區數據因此通過網絡傳輸到我的驅動程序)?
數據是在foreachPartition
中按行處理的(意味着每個 RDD 行都被一個一個地傳輸到 Python 實例),還是一次處理分區數據(意味着,例如,整個分區被傳輸到實例並由一個 Python 實例整體處理)?
預先感謝您的輸入!
編輯:
我之前使用的驅動程序解決方案看起來像這樣,取自 SO here :
for partition in rdd.mapPartitions(lambda partition: [list(partition)]).toLocalIterator():
# Do stuff on the partition
從文檔中可以看出, rdd.toLocalIterator()
提供了必要的功能:
返回包含此 RDD 中所有元素的迭代器。 迭代器將消耗與該 RDD 中最大分區一樣多的內存。
幸運的是,我偶然發現了 Mrinal 對mapPartitions
的精彩解釋( 在此處回答)。
mapPartitions
在 RDD 的每個分區上應用一個函數。 因此,如果分區分布在不同的節點上,則可以使用並行化。 在這些節點上創建處理 Python 函數所必需的相應 Python 實例。 雖然foreachPartition
僅應用一個函數(例如,將您的數據寫入 a.csv 文件), mapPartitions
還返回一個新的 RDD。 因此,使用foreachPartition
對我來說是錯誤的選擇。
為了回答我的第二個問題:像map
或UDFs
這樣的函數會創建一個新的 Python 實例,並逐行傳遞來自 DataFrame/RDD 的數據,從而導致大量開銷。 foreachPartition
和mapPartitions
(均為 RDD 函數)將整個分區傳輸到 Python 實例。
此外,使用生成器還減少了迭代此傳輸的分區數據所需的內存量(分區作為迭代器對象處理,而每一行然后通過迭代此對象來處理)。
一個示例可能如下所示:
def generator(partition):
"""
Function yielding some result created by some function applied to each row of a partition (in this case lower-casing a string)
@partition: iterator-object of partition
"""
for row in partition:
yield [word.lower() for word in row["text"]]
df = spark.createDataFrame([(["TESTA"], ), (["TESTB"], )], ["text"])
df = df.repartition(2)
df.rdd.mapPartitions(generator).toDF(["text"]).show()
#Result:
+-----+
| text|
+-----+
|testa|
|testb|
+-----+
希望這可以幫助面臨類似問題的人:)
pySpark UDF 在執行器附近執行——即在一個獨立的 python 實例中,每個執行器並排運行並在 spark 引擎 (scala) 和 python 解釋器之間來回傳遞數據。
對於在 foreachPartition 中調用 udfs 也是如此
編輯 - 查看示例代碼后
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.