@@ -26,7 +26,6 @@ import com.nvidia.spark.rapids._
2626import com .nvidia .spark .rapids .Arm .{closeOnExcept , withResource }
2727import com .nvidia .spark .rapids .GpuMetric ._
2828import com .nvidia .spark .rapids .RapidsPluginImplicits ._
29- import com .nvidia .spark .rapids .delta .RapidsDeletionVectorRowCountUtils
3029import com .nvidia .spark .rapids .jni .fileio .RapidsFileIO
3130import com .nvidia .spark .rapids .parquet ._
3231import org .apache .hadoop .conf .Configuration
@@ -215,7 +214,7 @@ class GpuDeltaParquetFileFormatBase2(
215214 filters : Array [Filter ],
216215 @ transient rapidsConf : RapidsConf ,
217216 metrics : Map [String , GpuMetric ],
218- params : Map [String , String ]
217+ @ transient params : Map [String , String ]
219218 ) extends GpuParquetPartitionReaderFactoryBase (sqlConf, broadcastedConf, dataSchema,
220219 readDataSchema, partitionSchema, rapidsConf, metrics = metrics, params = params) {
221220
@@ -280,12 +279,10 @@ class GpuDeltaParquetFileFormatBase2(
280279 } else {
281280 val scalaBitmap = RapidsDeletionVectors .loadScalaBitmap(
282281 conf, dvDescriptorOpt, filterTypeOpt, tablePath.get)
283- RapidsDeletionVectorRowCountUtils .computeNumRowsAlive(
284- totalNumRows, scalaBitmap.cardinality, chunkedBlocks) { countDeletedRow =>
285- scalaBitmap.forEach { deletedIndex : Long =>
286- countDeletedRow(deletedIndex)
287- }
288- }
282+ val (rowGroupOffsets, rowGroupNumRows) =
283+ RapidsDeletionVectors .getRowGroupMetadata(chunkedBlocks)
284+ Math .toIntExact(RapidsDeletionVectors .computeNumRowsAlive(
285+ totalNumRows, scalaBitmap, filterTypeOpt, rowGroupOffsets, rowGroupNumRows))
289286 }
290287 }
291288 }
@@ -313,7 +310,8 @@ class GpuDeltaParquetFileFormatBase2(
313310 RapidsDeletionVectors .getRowGroupMetadata(chunkedBlocks)
314311 val maybeDvInfo = maybeSerializedDV.map(serializedDV =>
315312 new DeletionVector .DeletionVectorInfo (serializedDV,
316- false , rowGroupOffsets, rowGroupNumRows))
313+ RapidsDeletionVectors .isIfNotContainedRowIndexFilter(filterTypeOpt),
314+ rowGroupOffsets, rowGroupNumRows))
317315
318316 val hostBuf = dataBuffer.getDataHostBuffer()
319317 // Duplicate request is ok, and start to use the GPU just after the host
@@ -364,8 +362,9 @@ class GpuDeltaParquetFileFormatBase2(
364362 */
365363 case class SpillableDeletionVectorInfo (
366364 serializedBitmap : SpillableHostBuffer ,
367- // Pre-computed number of deleted rows across all row groups.
368- numRowsDeleted : Long ,
365+ filterTypeOpt : Option [RowIndexFilterType ],
366+ // Pre-computed number of rows remaining after applying the deletion vector.
367+ numRowsAlive : Long ,
369368 // The offsets and numRows below are the original row group offsets and row counts
370369 // in the file. The combining process in multi-threaded reader involves re-organizing
371370 // row groups across files, but the offsets and numRows here are not changed even
@@ -384,16 +383,19 @@ class GpuDeltaParquetFileFormatBase2(
384383 def apply (
385384 serializedBitmap : HostMemoryBuffer ,
386385 scalaBitmap : RoaringBitmapArray ,
386+ filterTypeOpt : Option [RowIndexFilterType ],
387387 rowGroupOffsets : Array [Long ],
388388 rowGroupNumRows : Array [Int ]): SpillableDeletionVectorInfo = {
389- val numRowsDeleted =
390- RapidsDeletionVectors .countDeletedRows(scalaBitmap, rowGroupOffsets, rowGroupNumRows)
389+ val totalNumRows = rowGroupNumRows.map(_.toLong).sum
390+ val numRowsAlive = RapidsDeletionVectors .computeNumRowsAlive(
391+ totalNumRows, scalaBitmap, filterTypeOpt, rowGroupOffsets, rowGroupNumRows)
391392 new SpillableDeletionVectorInfo (
392393 SpillableHostBuffer (
393394 serializedBitmap,
394395 serializedBitmap.getLength(),
395396 SpillPriorities .ACTIVE_BATCHING_PRIORITY ),
396- numRowsDeleted,
397+ filterTypeOpt,
398+ numRowsAlive,
397399 rowGroupOffsets,
398400 rowGroupNumRows)
399401 }
@@ -428,8 +430,9 @@ class GpuDeltaParquetFileFormatBase2(
428430 timestampRebaseMode : DateTimeRebaseMode ,
429431 hasInt96Timestamps : Boolean ,
430432 // Base64-encoded DV descriptor string for this block's source file. None if no DV.
431- // The filter type is always RowIndexFilterType.IF_CONTAINED.
432433 val dvDescriptor : Option [String ],
434+ // Controls whether bitmap-marked rows are removed or retained. None if no DV.
435+ val filterTypeOpt : Option [RowIndexFilterType ],
433436 // Within-file row-index ordinal of this row group's first row.
434437 // Captured from BlockMetaData before any merging; invariant to computeBlockMetaData().
435438 val rowGroupOffset : Long ,
@@ -440,12 +443,14 @@ class GpuDeltaParquetFileFormatBase2(
440443 * Per-file DV entry assembled during [[augmentChunkMeta ]].
441444 *
442445 * @param dvDescriptor base64-encoded DV descriptor for this file; None if no DV
446+ * @param filterTypeOpt controls whether bitmap-marked rows are removed or retained; None if no DV
443447 * @param rowGroupOffsets within-file row-index ordinals of each row group's first row
444448 * @param rowGroupNumRows number of rows in each row group
445449 * @param partitionIndex index into rowsPerPartition / allPartValues this file contributes to
446450 */
447451 case class PerFileDVEntry (
448452 dvDescriptor : Option [String ],
453+ filterTypeOpt : Option [RowIndexFilterType ],
449454 rowGroupOffsets : Array [Long ],
450455 rowGroupNumRows : Array [Int ],
451456 partitionIndex : Int )
@@ -454,7 +459,7 @@ class GpuDeltaParquetFileFormatBase2(
454459 * Per-file DV load result produced during [[prepareForDecode ]].
455460 *
456461 * @param gpuBitmap serialized roaring bitmap buffer for the file's deletion vector
457- * @param aliveCount number of alive (non-deleted) rows in the file
462+ * @param aliveCount number of rows remaining after applying the deletion vector
458463 */
459464 case class SerializedRoaringBitmap (gpuBitmap : SpillableHostBuffer , aliveCount : Long )
460465
@@ -577,7 +582,9 @@ class GpuDeltaParquetFileFormatBase2(
577582 val filterTypeOpt = metaAndFile.file.otherConstantMetadataColumnValues
578583 .get(FILE_ROW_INDEX_FILTER_TYPE ).asInstanceOf [Option [RowIndexFilterType ]]
579584 filterTypeOpt.foreach { ft =>
580- require(ft == RowIndexFilterType .IF_CONTAINED ,
585+ require(
586+ ft == RowIndexFilterType .IF_CONTAINED ||
587+ ft == RowIndexFilterType .IF_NOT_CONTAINED ,
581588 s " Unexpected DV filter type for coalescing reader: $ft" )
582589 }
583590 val singleFileInfo = metaAndFile.meta
@@ -596,6 +603,7 @@ class GpuDeltaParquetFileFormatBase2(
596603 singleFileInfo.timestampRebaseMode,
597604 singleFileInfo.hasInt96Timestamps,
598605 dvDescriptorOpt,
606+ filterTypeOpt,
599607 rowGroupOffsets(i),
600608 rowGroupNumRows(i)))
601609 }
@@ -687,7 +695,8 @@ class GpuDeltaParquetFileFormatBase2(
687695 .map(spillableDvInfo =>
688696 new DeletionVector .DeletionVectorInfo (
689697 spillableDvInfo.serializedBitmap.getDataHostBuffer(),
690- false ,
698+ RapidsDeletionVectors .isIfNotContainedRowIndexFilter(
699+ spillableDvInfo.filterTypeOpt),
691700 spillableDvInfo.rowGroupOffsets,
692701 spillableDvInfo.rowGroupNumRows
693702 ))
@@ -725,7 +734,7 @@ class GpuDeltaParquetFileFormatBase2(
725734
726735 if (allPartValues.isDefined) {
727736 val allPartInternalRows = allPartValues.get.map(_._2)
728- // rowsPerPartition has been adjusted already to account only the alive rows .
737+ // rowsPerPartition has been adjusted to account for the deletion vectors .
729738 val rowsPerPartition = allPartValues.get.map(_._1)
730739 new GpuColumnarBatchWithPartitionValuesIterator (batchIter, allPartInternalRows,
731740 rowsPerPartition, partitionSchema, maxGpuColumnSizeBytes)
@@ -748,6 +757,7 @@ class GpuDeltaParquetFileFormatBase2(
748757 * Deletion vector metadata for a single host memory buffer containing a part of data.
749758 */
750759 private case class SingleBufferDVMetadata (
760+ // maybeDvInfo is None only when the tablePath is not defined.
751761 maybeDvInfo : Option [SpillableDeletionVectorInfo ]
752762 )
753763
@@ -824,7 +834,7 @@ class GpuDeltaParquetFileFormatBase2(
824834 numRows : Long ,
825835 blocks : collection.Seq [BlockMetaData ]
826836 ): HostMemoryEmptyMetaData = {
827- val (maybeSerializedDV, maybeScalaBitmap) = if (numRows > 0 ) {
837+ val (maybeSerializedDV, maybeScalaBitmap, filterTypeOpt ) = if (numRows > 0 ) {
828838 // numRows == 0 means the data is empty because of an empty file,
829839 // file not found, or a corrupted file. In all these cases, we don't
830840 // need to load deletion vectors.
@@ -839,9 +849,9 @@ class GpuDeltaParquetFileFormatBase2(
839849 // clause.
840850 val maybeSerializedDV = tablePath.map(tp =>
841851 RapidsDeletionVectors .loadDeletionVector(fileIO, dvDescriptorOpt, filterTypeOpt, tp))
842- (maybeSerializedDV, maybeScalaBitmap)
852+ (maybeSerializedDV, maybeScalaBitmap, filterTypeOpt )
843853 } else {
844- (None , None )
854+ (None , None , None )
845855 }
846856
847857 closeOnExcept(maybeSerializedDV) { _ =>
@@ -852,6 +862,7 @@ class GpuDeltaParquetFileFormatBase2(
852862 SpillableDeletionVectorInfo (
853863 serializedDV,
854864 maybeScalaBitmap.get,
865+ filterTypeOpt,
855866 rowGroupOffsets,
856867 rowGroupNumRows)}
857868 )
@@ -920,6 +931,7 @@ class GpuDeltaParquetFileFormatBase2(
920931 SpillableDeletionVectorInfo (
921932 serializedDV,
922933 maybeScalaBitmap.get,
934+ filterTypeOpt,
923935 rowGroupOffsets,
924936 rowGroupNumRows)
925937 })
@@ -974,20 +986,23 @@ class GpuDeltaParquetFileFormatBase2(
974986 return 0
975987 }
976988
977- val numDeletedRows = metadata match {
989+ val dvInfos = metadata match {
978990 case emptyMeta : DeltaParquetHostMemoryEmptyMetaData =>
979991 emptyMeta.dvMetadata.flatMap(_.metadatas).flatMap(_.maybeDvInfo)
980- .map(_.numRowsDeleted).sum
981992 case buffersMeta : DeltaParquetHostMemoryBuffersWithMetaData =>
982993 buffersMeta.dvMetadata.flatMap(_.metadatas).flatMap(_.maybeDvInfo)
983- .map(_.numRowsDeleted).sum
984994 case _ =>
985995 throw new IllegalArgumentException (s " Unexpected metadata type ${metadata.getClass()}" )
986996 }
987997
988- require(numDeletedRows <= totalNumRows,
989- s " Deletion vector cardinality ( $numDeletedRows) exceeds file row count ( $totalNumRows) " )
990- Math .toIntExact(totalNumRows - numDeletedRows)
998+ val numRowsAlive = if (dvInfos.isEmpty) {
999+ totalNumRows
1000+ } else {
1001+ dvInfos.map(_.numRowsAlive).sum
1002+ }
1003+ require(numRowsAlive <= totalNumRows,
1004+ s " Alive row count ( $numRowsAlive) exceeds file row count ( $totalNumRows) " )
1005+ Math .toIntExact(numRowsAlive)
9911006 }
9921007 }
9931008
@@ -1060,7 +1075,8 @@ class GpuDeltaParquetFileFormatBase2(
10601075 * - collect per-block DV descriptors during batch assembly ([[augmentChunkMeta ]])
10611076 * - load DV bitmaps concurrently after the copy phase ([[prepareForDecode ]])
10621077 * - pass DV info to the cuDF Parquet reader ([[readBufferToTablesAndClose ]])
1063- * - substitute DV-filtered alive row counts for partition routing ([[getRowsPerPartition ]])
1078+ * - substitute DV-filtered alive row counts for partition routing
1079+ * ([[getRowsPerPartition ]])
10641080 */
10651081 class MultiFileDeltaCoalescingParquetPartitionReader (
10661082 fileIO : RapidsFileIO ,
@@ -1103,6 +1119,7 @@ class GpuDeltaParquetFileFormatBase2(
11031119 require(group.blocks.nonEmpty, s " File group must contain blocks: ${group.filePath}" )
11041120 val firstExtra = group.blocks.head.extraInfo.asInstanceOf [DeltaParquetExtraInfo ]
11051121 val fileDesc = firstExtra.dvDescriptor
1122+ val filterTypeOpt = firstExtra.filterTypeOpt
11061123 val fileOffsets = ArrayBuffer [Long ]()
11071124 val fileNumRows = ArrayBuffer [Int ]()
11081125
@@ -1115,7 +1132,12 @@ class GpuDeltaParquetFileFormatBase2(
11151132 fileNumRows += extra.rowGroupNumRows
11161133 }
11171134
1118- PerFileDVEntry (fileDesc, fileOffsets.toArray, fileNumRows.toArray, partitionIndex)
1135+ PerFileDVEntry (
1136+ fileDesc,
1137+ filterTypeOpt,
1138+ fileOffsets.toArray,
1139+ fileNumRows.toArray,
1140+ partitionIndex)
11191141 }.toSeq
11201142
11211143 val batchExtra = new DeltaBatchExtraInfo (
@@ -1126,7 +1148,8 @@ class GpuDeltaParquetFileFormatBase2(
11261148
11271149 /**
11281150 * Loads DV bitmaps for all files in the batch concurrently after the copy phase.
1129- * Also computes per-file alive row counts (used later by [[getRowsPerPartition ]]).
1151+ * Also computes the rows remaining in each file after applying its deletion vector
1152+ * (used later by [[getRowsPerPartition ]]).
11301153 * Fast path: if no file in the batch has a DV, returns meta unchanged.
11311154 */
11321155 override protected def prepareForDecode (meta : CurrentChunkMeta ): CurrentChunkMeta = {
@@ -1143,26 +1166,19 @@ class GpuDeltaParquetFileFormatBase2(
11431166 threadPool.submit(new Callable [SerializedRoaringBitmap ] {
11441167 override def call (): SerializedRoaringBitmap = {
11451168 val rawBitmap = RapidsDeletionVectors .loadDeletionVector(
1146- fileIO, entry.dvDescriptor, tp)
1169+ fileIO, entry.dvDescriptor, entry.filterTypeOpt, tp)
11471170 // DeltaBatchExtraInfo.close() releases the SpillableHostBuffer when the decode
11481171 // phase completes (via withRetryNoSplit in readBatchData).
11491172 val gpuBitmap = SpillableHostBuffer (rawBitmap, rawBitmap.getLength,
11501173 SpillPriorities .ACTIVE_BATCHING_PRIORITY )
11511174 closeOnExcept(gpuBitmap) { _ =>
1152- val filterTypeOpt = entry.dvDescriptor.map(_ => RowIndexFilterType .IF_CONTAINED )
11531175 val totalRows = entry.rowGroupNumRows.map(_.toLong).sum
1154- val numDeleted = if (entry.dvDescriptor.isEmpty) {
1155- 0L
1156- } else {
1157- val scalaBitmap = RapidsDeletionVectors .loadScalaBitmap(
1158- conf, entry.dvDescriptor, filterTypeOpt, tp)
1159- RapidsDeletionVectors .countDeletedRows(
1160- scalaBitmap, entry.rowGroupOffsets, entry.rowGroupNumRows)
1161- }
1162- require(numDeleted <= totalRows,
1163- s " Deletion vector cardinality ( $numDeleted) exceeds " +
1164- s " file row count ( $totalRows) " )
1165- SerializedRoaringBitmap (gpuBitmap, totalRows - numDeleted)
1176+ val scalaBitmap = RapidsDeletionVectors .loadScalaBitmap(
1177+ conf, entry.dvDescriptor, entry.filterTypeOpt, tp)
1178+ val aliveCount = RapidsDeletionVectors .computeNumRowsAlive(
1179+ totalRows, scalaBitmap, entry.filterTypeOpt,
1180+ entry.rowGroupOffsets, entry.rowGroupNumRows)
1181+ SerializedRoaringBitmap (gpuBitmap, aliveCount)
11661182 }
11671183 }
11681184 })
@@ -1196,7 +1212,8 @@ class GpuDeltaParquetFileFormatBase2(
11961212 .map { case (loaded, entry) =>
11971213 new DeletionVector .DeletionVectorInfo (
11981214 loaded.gpuBitmap.getDataHostBuffer(),
1199- false , entry.rowGroupOffsets, entry.rowGroupNumRows)
1215+ RapidsDeletionVectors .isIfNotContainedRowIndexFilter(entry.filterTypeOpt),
1216+ entry.rowGroupOffsets, entry.rowGroupNumRows)
12001217 }.toArray
12011218 // MakeParquetTableWithDVProducer closes the dataBuffer and the bitmaps in dvInfos.
12021219 MakeParquetTableWithDVProducer (useChunkedReader, maxChunkedReaderMemoryUsageSizeBytes,
0 commit comments