版本:Apache spark 1.6.0

源码:​​RDD.scala​

一、源码说明

1、map

// Transformations (return a new RDD)

/**
* Return a new RDD by applying a function to all elements of this RDD.
*/
def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}

2、mapPartitions

/**
* Return a new RDD by applying a function to each partition of this RDD.
*
* `preservesPartitioning` indicates whether the input function preserves the partitioner, which
* should be `false` unless this is a pair RDD and the input function doesn't modify the keys.
*/
def mapPartitions[U: ClassTag](
f: Iterator[T] => Iterator[U],
preservesPartitioning: Boolean = false): RDD[U] = withScope {
val cleanedF = sc.clean(f)
new MapPartitionsRDD(
this,
(context: TaskContext, index: Int, iter: Iterator[T]) => cleanedF(iter),
preservesPartitioning)
}

二、对比

1、举例

现在有10个分区,共1000条数据,假设每个分区的数据=1000/10=100条,分别使用map和mapPartition遍历。

(1)、使用map(func())遍历

现在,当我们将map(func)方法应用于rdd时,func()操作将应用于每一行,在这种情况下,func()操作将被调用1000次。即在一些时间关键的应用中会耗费时间。

(2)、使用mapPartition(func())遍历

如果我们在rdd上调用mapPartition(func)方法,则func()操作将在每个分区上而不是在每一行上调用。在这种特殊情况下,它将被称为10次(分区数)。通过这种方式,你可以在涉及时间关键的应用程序时阻止一些处理。

2、mapPrtition的优势

(1)机器学习应用程序,特别是深度学习应用程序 - 使用矢量化时,执行比简单for循环要好上百倍。mapPartitions将帮助您使用矢量化。一般来说,你的性能提高300倍+(这不是百分比,是300倍)

(2)连接创建和清理任务很昂贵,每个元素都会使代码效率低下。这适用于数据库或其他连接。但是使用mapPartitions,你可以只对整个分区执行一次init / cleanup循环。

(3)一般来说,JVM带有乱序执行(它将完全使用CPU并使你的代码运行得更快),JVM需要分析你的代码,并且必须重写你的代码。使用mapPartitions,JVM可以更好地进行分析优化(与分析调用函数相比,它可以分析/优化简单代码)

(4)对于map (),CPU需要每次调用lambda函数(以arg形式传递以进行映射),这会带来10-15ns的开销,并导致CPU寄存器刷新并再次加载(堆栈指针,基址指针和指令指针)

3、与mapPartitions相比,map有什么用处?

(1)更简单的API,易于编码和易于理解,可以直接使用为List / Array / Map编写的现有函数

(2)功能性编程遗留下来的贡献很小。


参考:

  1. ​https://www.quora.com/What-is-the-difference-between-a-map-and-mapPartitions-in-Spark​
  2. ​https://stackoverflow.com/questions/21185092/apache-spark-map-vs-mappartitions​