DataSet 相对于 RDD 的性能优势
Performance benefits of DataSet over RDD
在阅读了几篇关于 Spark 的 DataSet 的精彩文章 (this, and this) 之后,我总结了下一个 DataSet 相对于 RDD 的性能优势:
- 逻辑和物理计划优化;
- 严格类型化;
- 矢量化操作;
- 低级内存管理。
问题:
- Spark 的 RDD 也构建物理计划,并且可以 combine/optimize 在同一阶段进行多次转换。那么DataSet相对于RDD的优势是什么?
- 从the first link可以看到
RDD[Person]
的例子。 DataSet 有高级类型化吗?
- "vectorized operations"是什么意思?
- 据我了解,DataSet 的低内存管理 = 高级序列化。这意味着可序列化对象的堆外存储,您可以在其中仅读取对象的一个字段而无需反序列化。 但是当你有
IN_MEMORY_ONLY
持久性策略时情况如何? DataSet 会在任何情况下序列化所有内容吗?它会比 RDD 有任何性能优势吗?
Spark's RDD also builds physical plan and can combine/optimize multiple transformations at the same stage. Than what is the benefit of DataSet over RDD?
使用 RDD 时,所写即所得。虽然某些转换通过链接进行了优化,但执行计划是 DAG 的直接翻译。例如:
rdd.mapPartitions(f).mapPartitions(g).mapPartitions(h).shuffle()
其中 shuffle
是任意洗牌转换(*byKey
、repartition
等)所有三个 mapPartitions
(map
、flatMap
, filter
) 将在不创建中间对象的情况下链接,但不能重新排列。
与 Datasets
相比,使用明显更严格的编程模型,但可以使用多种技术优化执行,包括:
选择 (filter
) 下推。例如,如果您有:
df.withColumn("foo", col("bar") + 1).where(col("bar").isNotNull())
可以执行为:
df.where(col("bar").isNotNull()).withColumn("foo", col("bar") + 1)
早期预测 (select
) 和消除。例如:
df.withColumn("foo", col("bar") + 1).select("foo", "bar")
可以改写为:
df.select("foo", "bar").withColumn("foo", col("bar") + 1)
以避免获取和传递过时的数据。在极端情况下它可以完全消除特定的转换:
df.withColumn("foo", col("bar") + 1).select("bar")
可以优化为
df.select("bar")
这些优化之所以可行,有两个原因:
- 限制性数据模型,无需复杂且不可靠的静态代码分析即可进行依赖性分析。
- 清晰的运算符语义。运算符没有副作用,我们清楚地区分确定性和非确定性运算符。
为了清楚起见,假设我们有以下数据模型:
case class Person(name: String, surname: String, age: Int)
val people: RDD[Person] = ???
而我们要检索所有21岁以上的人的姓氏。用RDD
可以表示为:
people
.map(p => (p.surname, p.age)) // f
.filter { case (_, age) => age > 21 } // g
现在让我们问自己几个问题:
f
中的输入age
与g
中的age
变量有什么关系?
f
然后g
和g
然后f
是一样的吗?
f
和 g
没有副作用吗?
虽然答案对于人类来说是显而易见的 reader 但对于假设的优化器而言却并非如此。与 Dataframe
版本相比:
people.toDF
.select(col("surname"), col("age")) // f'
.where(col("age") > 21) // g'
优化器和人类的答案都很明确 reader。
这在使用静态类型 Datasets
() 时会产生一些进一步的后果。
Have DataSet got more advanced typization?
- 否 - 如果您关心优化。最高级的优化仅限于
Dataset[Row]
,目前无法对复杂类型层次结构进行编码。
- 也许 - 如果您接受 Kryo 或 Java 编码器的开销。
What does they mean by "vectorized operations"?
在优化的上下文中,我们通常指的是循环矢量化/循环展开。 Spark SQL 使用代码生成来创建高级转换的编译器友好版本,可以进一步优化以利用矢量化指令集。
As I understand, DataSet's low memory management = advanced serialization.
不完全是。使用本机分配的最大优势是逃避垃圾收集器循环。由于垃圾收集通常是 Spark 中的一个限制因素,这是一个巨大的改进,特别是在需要大型数据结构的上下文中(比如准备洗牌)。
另一个重要方面是列式存储,它支持有效压缩(可能减少内存占用)和优化压缩数据操作。
一般来说 您可以在纯 RDDs
上使用手工编写的代码应用完全相同类型的优化。毕竟 Datasets
得到了 RDDs
的支持。不同的只是需要付出多少努力。
- 手工制作的执行计划优化实现起来相对简单。
- 使代码编译器友好需要一些更深入的知识,并且容易出错且冗长。
- 使用
sun.misc.Unsafe
和本机内存分配不适合胆小的人。
尽管有很多优点 Dataset
API 并不通用。虽然某些类型的常见任务可以在许多情况下受益于它的优化,但与等效的 RDD 相比,您可能没有任何改进甚至性能下降。
在阅读了几篇关于 Spark 的 DataSet 的精彩文章 (this,
- 逻辑和物理计划优化;
- 严格类型化;
- 矢量化操作;
- 低级内存管理。
问题:
- Spark 的 RDD 也构建物理计划,并且可以 combine/optimize 在同一阶段进行多次转换。那么DataSet相对于RDD的优势是什么?
- 从the first link可以看到
RDD[Person]
的例子。 DataSet 有高级类型化吗? - "vectorized operations"是什么意思?
- 据我了解,DataSet 的低内存管理 = 高级序列化。这意味着可序列化对象的堆外存储,您可以在其中仅读取对象的一个字段而无需反序列化。 但是当你有
IN_MEMORY_ONLY
持久性策略时情况如何? DataSet 会在任何情况下序列化所有内容吗?它会比 RDD 有任何性能优势吗?
Spark's RDD also builds physical plan and can combine/optimize multiple transformations at the same stage. Than what is the benefit of DataSet over RDD?
使用 RDD 时,所写即所得。虽然某些转换通过链接进行了优化,但执行计划是 DAG 的直接翻译。例如:
rdd.mapPartitions(f).mapPartitions(g).mapPartitions(h).shuffle()
其中 shuffle
是任意洗牌转换(*byKey
、repartition
等)所有三个 mapPartitions
(map
、flatMap
, filter
) 将在不创建中间对象的情况下链接,但不能重新排列。
与 Datasets
相比,使用明显更严格的编程模型,但可以使用多种技术优化执行,包括:
选择 (
filter
) 下推。例如,如果您有:df.withColumn("foo", col("bar") + 1).where(col("bar").isNotNull())
可以执行为:
df.where(col("bar").isNotNull()).withColumn("foo", col("bar") + 1)
早期预测 (
select
) 和消除。例如:df.withColumn("foo", col("bar") + 1).select("foo", "bar")
可以改写为:
df.select("foo", "bar").withColumn("foo", col("bar") + 1)
以避免获取和传递过时的数据。在极端情况下它可以完全消除特定的转换:
df.withColumn("foo", col("bar") + 1).select("bar")
可以优化为
df.select("bar")
这些优化之所以可行,有两个原因:
- 限制性数据模型,无需复杂且不可靠的静态代码分析即可进行依赖性分析。
- 清晰的运算符语义。运算符没有副作用,我们清楚地区分确定性和非确定性运算符。
为了清楚起见,假设我们有以下数据模型:
case class Person(name: String, surname: String, age: Int)
val people: RDD[Person] = ???
而我们要检索所有21岁以上的人的姓氏。用RDD
可以表示为:
people
.map(p => (p.surname, p.age)) // f
.filter { case (_, age) => age > 21 } // g
现在让我们问自己几个问题:
f
中的输入age
与g
中的age
变量有什么关系?f
然后g
和g
然后f
是一样的吗?f
和g
没有副作用吗?
虽然答案对于人类来说是显而易见的 reader 但对于假设的优化器而言却并非如此。与 Dataframe
版本相比:
people.toDF
.select(col("surname"), col("age")) // f'
.where(col("age") > 21) // g'
优化器和人类的答案都很明确 reader。
这在使用静态类型 Datasets
(
Have DataSet got more advanced typization?
- 否 - 如果您关心优化。最高级的优化仅限于
Dataset[Row]
,目前无法对复杂类型层次结构进行编码。 - 也许 - 如果您接受 Kryo 或 Java 编码器的开销。
What does they mean by "vectorized operations"?
在优化的上下文中,我们通常指的是循环矢量化/循环展开。 Spark SQL 使用代码生成来创建高级转换的编译器友好版本,可以进一步优化以利用矢量化指令集。
As I understand, DataSet's low memory management = advanced serialization.
不完全是。使用本机分配的最大优势是逃避垃圾收集器循环。由于垃圾收集通常是 Spark 中的一个限制因素,这是一个巨大的改进,特别是在需要大型数据结构的上下文中(比如准备洗牌)。
另一个重要方面是列式存储,它支持有效压缩(可能减少内存占用)和优化压缩数据操作。
一般来说 您可以在纯 RDDs
上使用手工编写的代码应用完全相同类型的优化。毕竟 Datasets
得到了 RDDs
的支持。不同的只是需要付出多少努力。
- 手工制作的执行计划优化实现起来相对简单。
- 使代码编译器友好需要一些更深入的知识,并且容易出错且冗长。
- 使用
sun.misc.Unsafe
和本机内存分配不适合胆小的人。
尽管有很多优点 Dataset
API 并不通用。虽然某些类型的常见任务可以在许多情况下受益于它的优化,但与等效的 RDD 相比,您可能没有任何改进甚至性能下降。