PySpark:执行程序映射分区功能未释放Numpy内存(内存泄漏)

时间:2018-11-01 16:33:49

标签: python numpy apache-spark memory-leaks pyspark

我有以下最低限度的工作示例:

from pyspark import SparkContext
from pyspark.sql import SQLContext
import numpy as np

sc = SparkContext()
sqlContext = SQLContext(sc)

# Create dummy pySpark DataFrame with 1e5 rows and 16 partitions
df = sqlContext.range(0, int(1e5), numPartitions=16)

def toy_example(rdd):

    # Read in pySpark DataFrame partition
    data = list(rdd)

    # Generate random data using Numpy
    rand_data = np.random.random(int(1e7))

    # Apply the `int` function to each element of `rand_data`
    for i in range(len(rand_data)):
        e = rand_data[i]
        int(e)

    # Return a single `0` value
    return [[0]]

# Execute the above function on each partition (16 partitions)
result = df.rdd.mapPartitions(toy_example)
result = result.collect()

运行上述命令后,执行程序的Python进程的内存在每次迭代后稳步增加,这表明上一迭代的内存并未释放-即内存泄漏。如果内存超出执行程序的内存限制,则可能导致作业失败-见下文:

enter image description here

Bizarrely以下任何一项都可以防止内存泄漏:

  • 删除行data = list(rdd)
  • rand_data = list(rand_data.tolist())之后插入行rand_data = np.random.random(int(1e7))
  • 删除行int(e)

以上代码是一个大型项目的最小工作示例,该项目无法使用上述修复程序。

一些需要注意的事情:

  • 该函数中未使用rdd数据时,需要使用该行来重现泄漏。在现实世界的项目中,使用rdd数据。
  • 内存泄漏很可能是由于未释放大型Numpy数组rand_data
  • 您必须对int的每个元素执行rand_data操作才能重现泄漏

问题

您可以通过在rand_data函数的前几行或后几行插入代码来强制PySpark执行程序释放toy_example的内存吗?

已经尝试过的东西

通过在函数末尾插入来强制进行垃圾回收:

del data, rand_data
import gc
gc.collect()

通过在函数的结尾或开头插入来强制释放内存(受Pandas issue的启发):

from ctypes import cdll, CDLL
cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")
libc.malloc_trim(0)

设置,测量和版本

以下PySpark作业在具有一个m4.xlarge工作程序节点的AWS EMR集群上运行。 Numpy必须通过bootstrapping通过pip安装在工作程序节点上。

执行器的内存使用以下功能(打印在执行器的日志中)进行测量:

import resource
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss

火花提交配置:

  • spark.executor.instances = 1
  • spark.executor.cores = 1
  • spark.executor.memory = 6g
  • spark.master =纱线
  • spark.dynamicAllocation.enabled = false

版本:

  • EMR 5.12.1
  • 火花2.2.1
  • Python 2.7.13
  • Numpy 1.14.0

1 个答案:

答案 0 :(得分:0)

我们最近遇到了一个非常类似的问题,我们也无法通过更改代码来强制释放内存。但是,对我们有用的是使用以下Spark选项: spark.python.worker.reuse = False