Skip to content

Conversation

@andygrove
Copy link
Member

@andygrove andygrove commented Jan 18, 2026

Summary

Optimizes CometColumnarToRowExec code generation to reduce allocations and improve performance when converting columnar batches to rows, particularly for complex types (arrays, maps, structs) and fixed-width primitives.

Optimizations

1. Reusable wrapper objects for complex types

Instead of creating new ColumnarArray/ColumnarMap/ColumnarRow objects per-row, we now create reusable CometColumnarArray, CometColumnarMap, and CometColumnarRow wrappers once per-batch and update their internal pointers for each row:

  • CometColumnarArray: Mutable ArrayData implementation with update(offset, length) method
  • CometColumnarMap: Mutable MapData implementation wrapping key/value arrays
  • CometColumnarRow: Mutable InternalRow implementation with update(rowId) method

2. Direct offset buffer access

For ArrayType and MapType columns backed by CometListVector/CometMapVector, we cache the offset buffer memory address per-batch and use Platform.getInt() for direct memory access instead of going through the Arrow API.

3. Direct memory access for fixed-width primitives

For fixed-width primitive types (ByteType, ShortType, IntegerType, LongType, FloatType, DoubleType, DateType, TimestampType), we cache the value buffer address per-batch and generate Platform.getXxx() calls for direct memory reads, bypassing the ColumnVector abstraction.

Files Changed

  • CometColumnarArray.java - New mutable ArrayData wrapper (spark-3.x and spark-4.0 versions)
  • CometColumnarMap.java - New mutable MapData wrapper
  • CometColumnarRow.java - New mutable InternalRow wrapper (spark-3.x and spark-4.0 versions)
  • CometPlainVector.java - Added getValueBufferAddress() for direct memory access
  • CometColumnarToRowExec.scala - Updated code generation with optimized paths

Test Plan

  • All existing tests pass (CometExecSuite, CometArrayExpressionSuite, CometHashExpressionSuite, etc.)
  • Benchmarked with benchmarks/pyspark/run_all_benchmarks.sh

🤖 Generated with Claude Code

andygrove and others added 4 commits January 16, 2026 12:48
This is a work-in-progress optimization for complex types (arrays and maps)
in CometColumnarToRowExec. The changes include:

Phase 1: Cache offset buffer addresses in Comet vectors
- CometListVector and CometMapVector now cache the offset buffer memory
  address at construction time
- getArray()/getMap() use Platform.getInt() for direct memory access
  instead of getOffsetBuffer().getInt()
- This eliminates virtual method calls per-row

Phase 2: Custom code generation for complex types
- CometColumnarToRowExec now generates optimized code for ArrayType and
  MapType columns
- Per-batch caching of offset buffer addresses and child vectors
- Uses Platform.getInt() directly in generated code for offset access

TODO:
- Phase 3: Reusable wrapper objects (MutableColumnarArray/Map)
- Benchmarking to measure performance improvements
- Additional test coverage

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Phase 3 of CometColumnarToRow optimization:

- Add CometColumnarArray: mutable ArrayData impl that allows updating
  offset/length without creating new objects
- Add CometColumnarMap: mutable MapData impl with same reusability
- Update code generation to create wrapper once per batch and reuse
  across all rows via update() calls

This eliminates object allocation per-row for array and map columns,
reducing GC pressure.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Phase 4 of CometColumnarToRow optimization:

- Add CometColumnarRow: mutable InternalRow impl that allows updating
  the rowId without creating new objects
- Update code generation to detect StructType columns and use
  CometColumnarRow wrappers created once per batch

This eliminates object allocation per-row for struct columns,
reducing GC pressure.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@codecov-commenter
Copy link

codecov-commenter commented Jan 18, 2026

Codecov Report

❌ Patch coverage is 84.31373% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.60%. Comparing base (f09f8af) to head (5a872da).
⚠️ Report is 855 commits behind head on main.

Files with missing lines Patch % Lines
...java/org/apache/comet/vector/CometColumnarMap.java 0.00% 18 Missing ⚠️
.../java/org/apache/comet/vector/CometListVector.java 0.00% 6 Missing ⚠️
...n/java/org/apache/comet/vector/CometMapVector.java 0.00% 6 Missing ⚠️
...pache/spark/sql/comet/CometColumnarToRowExec.scala 97.28% 0 Missing and 6 partials ⚠️
...java/org/apache/comet/vector/CometPlainVector.java 0.00% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #3213      +/-   ##
============================================
+ Coverage     56.12%   56.60%   +0.48%     
- Complexity      976     1376     +400     
============================================
  Files           119      171      +52     
  Lines         11743    15945    +4202     
  Branches       2251     2616     +365     
============================================
+ Hits           6591     9026    +2435     
- Misses         4012     5632    +1620     
- Partials       1140     1287     +147     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

andygrove and others added 5 commits January 17, 2026 20:30
Add getVariant() method to CometColumnarArray and CometColumnarRow
without @OverRide annotation to maintain compatibility with both
Spark 3.x (which doesn't have this method) and Spark 4.x (which
requires it).

Co-Authored-By: Claude Opus 4.5 <[email protected]>
… dirs

Move these classes to spark-3.x and spark-4.0 directories to handle
the getVariant() method difference:
- Spark 3.x: SpecializedGetters doesn't have getVariant()
- Spark 4.x: SpecializedGetters.getVariant() returns VariantVal

This fixes the CI build failures for Spark 4.x.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Use Platform.getXxx() for direct memory access to value buffers
for fixed-width primitive types (byte, short, int, long, float,
double, date, timestamp). This caches the value buffer address
per-batch and uses JIT-intrinsified memory operations.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
When accessing nested arrays, maps, or structs from within complex type
wrappers (CometColumnarArray, CometColumnarRow), reuse wrapper objects
instead of creating new ones per-element. This eliminates per-element
allocations for deeply nested types like Array(Array(Int)) or
Struct(Array, Map).

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@andygrove andygrove changed the title perf: [WIP] optimize CometColumnarToRow for complex types perf: optimize CometColumnarToRow for complex types Jan 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants