-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-16189][SQL] Add ExternalRDD logical plan for input with RDD to have a chance to eliminate serialize/deserialize. #13890
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,12 +20,12 @@ package org.apache.spark.sql.execution | |
| import org.apache.commons.lang.StringUtils | ||
|
|
||
| import org.apache.spark.rdd.RDD | ||
| import org.apache.spark.sql.{AnalysisException, Row, SparkSession, SQLContext} | ||
| import org.apache.spark.sql.{AnalysisException, Encoder, Row, SparkSession, SQLContext} | ||
| import org.apache.spark.sql.catalyst.{CatalystTypeConverters, InternalRow, TableIdentifier} | ||
| import org.apache.spark.sql.catalyst.analysis.MultiInstanceRelation | ||
| import org.apache.spark.sql.catalyst.expressions._ | ||
| import org.apache.spark.sql.catalyst.expressions.codegen.{CodegenContext, ExprCode} | ||
| import org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Statistics} | ||
| import org.apache.spark.sql.catalyst.plans.logical._ | ||
| import org.apache.spark.sql.catalyst.plans.physical.{HashPartitioning, Partitioning, UnknownPartitioning} | ||
| import org.apache.spark.sql.execution.datasources.HadoopFsRelation | ||
| import org.apache.spark.sql.execution.datasources.parquet.{ParquetFileFormat => ParquetSource} | ||
|
|
@@ -74,13 +74,71 @@ object RDDConversions { | |
| } | ||
| } | ||
|
|
||
| private[sql] object ExistingRDD { | ||
|
|
||
| def apply[T: Encoder](rdd: RDD[T])(session: SparkSession): LogicalPlan = { | ||
| val exisitingRdd = ExistingRDD(CatalystSerde.generateObjAttr[T], rdd)(session) | ||
| CatalystSerde.serialize[T](exisitingRdd) | ||
| } | ||
| } | ||
|
|
||
| /** Logical plan node for scanning data from an RDD. */ | ||
| private[sql] case class ExistingRDD[T]( | ||
| outputObjAttr: Attribute, | ||
| rdd: RDD[T])(session: SparkSession) | ||
| extends LeafNode with ObjectProducer with MultiInstanceRelation { | ||
|
|
||
| override protected final def otherCopyArgs: Seq[AnyRef] = session :: Nil | ||
|
|
||
| override def newInstance(): ExistingRDD.this.type = | ||
| ExistingRDD(outputObjAttr.newInstance(), rdd)(session).asInstanceOf[this.type] | ||
|
|
||
| override def sameResult(plan: LogicalPlan): Boolean = { | ||
| plan.canonicalized match { | ||
| case ExistingRDD(_, otherRDD) => rdd.id == otherRDD.id | ||
| case _ => false | ||
| } | ||
| } | ||
|
|
||
| override protected def stringArgs: Iterator[Any] = Iterator(output) | ||
|
|
||
| @transient override lazy val statistics: Statistics = Statistics( | ||
| // TODO: Instead of returning a default value here, find a way to return a meaningful size | ||
| // estimate for RDDs. See PR 1238 for more discussions. | ||
| sizeInBytes = BigInt(session.sessionState.conf.defaultSizeInBytes) | ||
| ) | ||
| } | ||
|
|
||
| /** Physical plan node for scanning data from an RDD. */ | ||
| private[sql] case class ExistingRDDScanExec[T]( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the name it's hard to tell what's the difference between this one and
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about renaming
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a good idea here, cc @yhuai
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about ExternalRDDScan? |
||
| outputObjAttr: Attribute, | ||
| rdd: RDD[T]) extends LeafExecNode with ObjectProducerExec { | ||
|
|
||
| private[sql] override lazy val metrics = Map( | ||
| "numOutputRows" -> SQLMetrics.createMetric(sparkContext, "number of output rows")) | ||
|
|
||
| protected override def doExecute(): RDD[InternalRow] = { | ||
| val numOutputRows = longMetric("numOutputRows") | ||
| val outputDataType = outputObjAttr.dataType | ||
| rdd.mapPartitionsInternal { iter => | ||
| val outputObject = ObjectOperator.wrapObjectToRow(outputDataType) | ||
| iter.map { value => | ||
| numOutputRows += 1 | ||
| outputObject(value) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override def simpleString: String = { | ||
| s"Scan $nodeName${output.mkString("[", ",", "]")}" | ||
| } | ||
| } | ||
|
|
||
| /** Logical plan node for scanning data from an RDD of InternalRow. */ | ||
| private[sql] case class LogicalRDD( | ||
| output: Seq[Attribute], | ||
| rdd: RDD[InternalRow])(session: SparkSession) | ||
| extends LogicalPlan with MultiInstanceRelation { | ||
|
|
||
| override def children: Seq[LogicalPlan] = Nil | ||
| extends LeafNode with MultiInstanceRelation { | ||
|
|
||
| override protected final def otherCopyArgs: Seq[AnyRef] = session :: Nil | ||
|
|
||
|
|
@@ -96,16 +154,14 @@ private[sql] case class LogicalRDD( | |
|
|
||
| override protected def stringArgs: Iterator[Any] = Iterator(output) | ||
|
|
||
| override def producedAttributes: AttributeSet = outputSet | ||
|
|
||
| @transient override lazy val statistics: Statistics = Statistics( | ||
| // TODO: Instead of returning a default value here, find a way to return a meaningful size | ||
| // estimate for RDDs. See PR 1238 for more discussions. | ||
| sizeInBytes = BigInt(session.sessionState.conf.defaultSizeInBytes) | ||
| ) | ||
| } | ||
|
|
||
| /** Physical plan node for scanning data from an RDD. */ | ||
| /** Physical plan node for scanning data from an RDD of InternalRow. */ | ||
| private[sql] case class RDDScanExec( | ||
| output: Seq[Attribute], | ||
| rdd: RDD[InternalRow], | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why curry here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I wanted to make the signature similar to the case class constructor.
Should I uncurry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I'm not sure why we curry the constructor either. Since
RDDScandoes it, it's ok we follow it. But for this applymethod, I don't see the value of doing it. cc @yhuaiUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is probably no reason to use multiple parameter lists here. We sometimes use it for case classes so that arguments that should not effect equality are not included in the generated
equalsmethod.