andygrove commented on code in PR #5869:
URL: https://github.com/apache/datafusion-comet/pull/5869#discussion_r4238240496
##########
spark/src/main/scala/org/apache/spark/sql/comet/execution/arrow/CachedBatchIpc.scala:
##########
@@ -524,6 +569,45 @@ private[comet] object CachedBatchIpc {
buffers.add(packed)
}
+ deltaColumns.foreach { case (index, column) =>
+ val buffer = batch.getBuffers.get(index)
+ val packed = buffers.get(index)
+ if (batch.getLength > 0) {
+ val deltas = allocator.buffer(buffer.writerIndex())
+ try {
+ var previous = 0L
+ var smallDeltas = 0
+ var row = 0
+ while (row < batch.getLength) {
+ val value = buffer.getLong(row * 8L)
+ val delta = value - previous
+ deltas.setLong(row * 8L, delta)
+ if (delta == delta.toInt.toLong) smallDeltas += 1
+ previous = value
+ row += 1
+ }
+ deltas.writerIndex(buffer.writerIndex())
+ // Skip a second compression for full-width irregular longs. The
size comparison
+ // still rejects poorly compressing deltas from narrower
distributions.
+ if (smallDeltas.toLong * 2 >= batch.getLength) {
Review Comment:
This check sends every column whose values fit in an `int` through a second
zstd pass, whether or not the deltas end up smaller. I cached a 5M-row relation
of three such `bigint` columns: uniform below 1e6, uniform below 2^31, and a 1
ms step with up to 1 ms of jitter. With the option on, none of the 512 batches
kept deltas for any column and the footprint stayed at 46.9 MiB. The build went
from 210 ms to 299 ms, median of 7, and every run with the option on was slower
than every run without it. Per 10,000-row column, uniform values below 1000
took 328 us to serialize instead of 169 us. Their deltas compress to 1.38 times
the size of the values, so they can never pass the bar.
Could the loop also compare how wide the deltas are with how wide the values
are, and only try the second pass when the deltas are clearly narrower? For
example, summing `64 - numberOfLeadingZeros` of each value's zigzag form, and
trying only when the deltas come out at least four bits narrower on average.
That skips every uniform case I tried and still tries all the sequential,
constant-step and cyclic ones.
##########
docs/source/user-guide/latest/in-memory-cache.md:
##########
@@ -68,6 +68,14 @@ A relation whose cached plan records observed metrics, from
`Dataset.observe`, i
Comet's format but is scanned by Spark's `InMemoryTableScanExec`, because
Spark collects those
metrics only through that scan.
+Set `spark.comet.exec.inMemoryCache.deltaEncoding.enabled=true` to try delta
encoding
+for top-level `bigint` columns in compressed caches. A column uses deltas only
when
+its compressed data buffer is over 25% smaller than the plain representation.
Review Comment:
The code compares the compressed deltas with the compressed values
(`encoded.writerIndex() < packed.writerIndex() * 3 / 4`), not with the plain
representation, so this sentence describes a different threshold. The
`CometConf` doc string has the same gap, since it only says the delta buffer is
over 25% smaller. Could both say the comparison is between the two compressed
forms?
It would also help to give numbers here, the way the codec table under
Storage format does, perhaps from a delta arm in `runCodecBenchmark`. That
benchmark's relation is a good case for this option. I measured 54.8 MiB
without deltas and 44.2 MiB with them. `id`, `k` and `v` went from 6.3, 0.9 and
6.5 MiB to 1.3, 0.4 and 1.5 MiB, and reads were no slower. The build cost on
columns that are not sequential is worth stating too.
##########
spark/src/test/scala/org/apache/spark/sql/comet/execution/arrow/CometCachedBatchHelper.scala:
##########
@@ -122,8 +125,9 @@ object CometCachedBatchHelper {
batch: ColumnarBatch,
codec: CompressionCodec,
allocator: BufferAllocator,
- chunkSize: Int = 1024 * 1024): ChunkedByteBuffer =
- CachedBatchIpc.serialize(batch, codec, allocator, chunkSize)._1
+ chunkSize: Int = 1024 * 1024,
+ deltaEncoding: Boolean = false): ChunkedByteBuffer =
+ CachedBatchIpc.serialize(batch, codec, allocator, chunkSize,
deltaEncoding)._1
Review Comment:
With `deltaEncoding = true` this returns the payload without its flags, and
`cachedBatch` below then builds a batch that has none. A test that round trips
through these and `load` would silently read the deltas back as values. The doc
on `cachedBatch` says it is the batch the writer would have stored, which no
longer holds. Could `serialize` return the flags as well, or the `CachedBatch`
itself?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]