com.nvidia.spark.rapids.limit.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rapids-4-spark_2.12 Show documentation
Show all versions of rapids-4-spark_2.12 Show documentation
Creates the distribution package of the RAPIDS plugin for Apache Spark
/*
* Copyright (c) 2020, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nvidia.spark.rapids
import scala.collection.mutable.ArrayBuffer
import ai.rapids.cudf.Table
import com.nvidia.spark.rapids.GpuMetricNames._
import com.nvidia.spark.rapids.RapidsPluginImplicits._
import org.apache.spark.rdd.RDD
import org.apache.spark.serializer.Serializer
import org.apache.spark.sql.catalyst.InternalRow
import org.apache.spark.sql.catalyst.expressions.{Attribute, SortOrder}
import org.apache.spark.sql.catalyst.plans.physical.{AllTuples, Distribution, Partitioning, SinglePartition}
import org.apache.spark.sql.execution.{CollectLimitExec, LimitExec, ShuffledRowRDD, SparkPlan, UnaryExecNode, UnsafeRowSerializer}
import org.apache.spark.sql.execution.metric.{SQLMetric, SQLMetrics, SQLShuffleReadMetricsReporter, SQLShuffleWriteMetricsReporter}
import org.apache.spark.sql.rapids.execution.ShuffledBatchRDD
import org.apache.spark.sql.vectorized.ColumnarBatch
/**
* Helper trait which defines methods that are shared by both
* [[GpuLocalLimitExec]] and [[GpuGlobalLimitExec]].
*/
trait GpuBaseLimitExec extends LimitExec with GpuExec {
override def output: Seq[Attribute] = child.output
override def outputPartitioning: Partitioning = child.outputPartitioning
override def outputOrdering: Seq[SortOrder] = child.outputOrdering
protected override def doExecute(): RDD[InternalRow] =
throw new IllegalStateException(s"Row-based execution should not occur for $this")
override def doExecuteColumnar(): RDD[ColumnarBatch] = {
val crdd = child.executeColumnar()
crdd.mapPartitions { cbIter =>
new Iterator[ColumnarBatch] {
var remainingLimit = limit
override def hasNext: Boolean = remainingLimit > 0 && cbIter.hasNext
override def next(): ColumnarBatch = {
val batch = cbIter.next()
val result = if (batch.numRows() > remainingLimit) {
sliceBatch(batch)
} else {
batch
}
remainingLimit -= result.numRows()
result
}
def sliceBatch(batch: ColumnarBatch): ColumnarBatch = {
val numColumns = batch.numCols()
val resultCVs = new ArrayBuffer[GpuColumnVector](numColumns)
var exception: Throwable = null
var table: Table = null
try {
if (numColumns > 0) {
table = GpuColumnVector.from(batch)
(0 until numColumns).foreach(i => {
val subVector = table.getColumn(i).subVector(0, remainingLimit)
assert(subVector != null)
resultCVs.append(GpuColumnVector.from(subVector))
assert(subVector.getRowCount == remainingLimit,
s"result rowcount ${subVector.getRowCount} is not equal to the " +
s"remainingLimit $remainingLimit")
})
}
new ColumnarBatch(resultCVs.toArray, remainingLimit)
} catch {
case e: Throwable => exception = e
throw e
} finally {
if (exception != null) {
resultCVs.foreach(gpuVector => gpuVector.safeClose(exception))
}
if (table != null) {
table.safeClose(exception)
}
batch.safeClose(exception)
}
}
}
}
}
}
/**
* Take the first `limit` elements of each child partition, but do not collect or shuffle them.
*/
case class GpuLocalLimitExec(limit: Int, child: SparkPlan) extends GpuBaseLimitExec
/**
* Take the first `limit` elements of the child's single output partition.
*/
case class GpuGlobalLimitExec(limit: Int, child: SparkPlan) extends GpuBaseLimitExec {
override def requiredChildDistribution: List[Distribution] = AllTuples :: Nil
}
class GpuCollectLimitMeta(
collectLimit: CollectLimitExec,
conf: RapidsConf,
parent: Option[RapidsMeta[_, _, _]],
rule: ConfKeysAndIncompat)
extends SparkPlanMeta[CollectLimitExec](collectLimit, conf, parent, rule) {
override val childParts: scala.Seq[PartMeta[_]] =
Seq(GpuOverrides.wrapPart(collectLimit.outputPartitioning, conf, Some(this)))
override def convertToGpu(): GpuExec =
GpuCollectLimitExec(collectLimit.limit, childParts(0).convertToGpu(),
GpuLocalLimitExec(collectLimit.limit,
GpuShuffleExchangeExec(GpuSinglePartitioning(Seq.empty), childPlans(0).convertIfNeeded())))
}
case class GpuCollectLimitExec(
limit: Int, partitioning: GpuPartitioning,
child: SparkPlan) extends GpuBaseLimitExec {
private val serializer: Serializer = new GpuColumnarBatchSerializer(child.output.size)
private lazy val writeMetrics =
SQLShuffleWriteMetricsReporter.createShuffleWriteMetrics(sparkContext)
private lazy val readMetrics =
SQLShuffleReadMetricsReporter.createShuffleReadMetrics(sparkContext)
lazy val shuffleMetrics = readMetrics ++ writeMetrics
override def doExecuteColumnar(): RDD[ColumnarBatch] = {
val locallyLimited: RDD[ColumnarBatch] = super.doExecuteColumnar()
val shuffleDependency = GpuShuffleExchangeExec.prepareBatchShuffleDependency(
locallyLimited,
child.output,
partitioning,
serializer,
metrics ++ shuffleMetrics,
metrics ++ writeMetrics)
val shuffled = new ShuffledBatchRDD(shuffleDependency, metrics ++ shuffleMetrics, None)
shuffled.mapPartitions(_.take(limit))
}
}