JingsongLi opened a new pull request, #10007:
URL: https://github.com/apache/paimon/pull/10007
### Purpose
Reduce traversal overhead when scalar indexes return many row IDs,
particularly for Data Evolution tables with column-group reads. This PR changes
two shared traversal helpers and their regression tests only; it does not add a
memory-budget option, scan fallback, cost model, or storage/split-format change.
#### 1. Seek within the selection instead of rescanning its prefix for every
batch
`FileRecordIterator.selection` previously created a fresh iterator over the
whole file selection for each batch. A batch starting late in the file walked
all earlier selected positions again. With many batches and a large selection,
this repeated-prefix work dominates the actual read.
Use `RoaringBitmap32.nextValue` to seek directly to the current
file-relative position and cache the next selected position. Both `next()` and
`skip()` use the same check; unselected gaps do not trigger a fresh seek for
every row. Batch release, selection ownership, and unsigned 32-bit position
boundaries are covered by tests.
#### 2. Avoid the select-based bitmap-to-ranges path for dense but short runs
`RoaringNavigableMap64` previously sampled adjacent values at the middle and
tail. A pattern such as nine matching rows followed by one gap can look dense
to those samples, yet create many short ranges. Repeated rank-based `select`
calls are expensive for that shape.
Probe longer contiguous windows before selecting that path. Split each
window into two halves so that an isolated gap inside a long run does not force
a full value-by-value traversal. The existing exact range construction is
unchanged: gaps are never filled or approximated.
### Performance
Local synthetic benchmark: JDK 8, RoaringBitmap 1.2.1, default Parquet
configuration, Data Evolution column groups (`key`/`flag` and a separate
payload group). BTree V1 and V2 were tested separately; V2 was explicitly
enabled. The payload was checked against the key while fully consuming the
results.
Each variant was warmed up once, followed by three rotated rounds; the table
reports median planning + full-read latency for **1.2 million rows**, excluding
table creation, writes, and index construction.
| BTree version | Result shape | Before (ms) | After (ms) | Before / after |
|---|---|---:|---:|---:|
| V1 | 90% matches, 120,000 nine-row ranges | 4,379.6 | 212.7 | 20.6x |
| V2 | 90% matches, 120,000 nine-row ranges | 4,341.8 | 190.7 | 22.8x |
| V1 | 10% matches, 120,000 singleton ranges | 538.9 | 63.3 | 8.5x |
| V2 | 10% matches, 120,000 singleton ranges | 569.1 | 63.8 | 8.9x |
| V1 | 90% matches, one contiguous range | 3,516.2 | 180.2 | 19.5x |
| V2 | 90% matches, one contiguous range | 3,548.8 | 250.9 | 14.1x |
How the measurements relate to the changes:
- **Read-side selection seeking:** V1's single-contiguous-range read phase
fell from **3,491.2 ms to 145.0 ms**. Singleton-range planning was
approximately unchanged (8.2 ms to 7.7 ms), while its read phase fell from
**530.6 ms to 55.4 ms**.
- **Bitmap-to-ranges path selection:** V1's nine-row-range planning phase
fell from **541.7 ms to 18.5 ms**. In a conversion-only probe over a
1.2-million-position domain, the nine-hit/one-gap shape took **2.8 ms** with
the new path choice versus **495.3 ms** when forcing the existing select path.
A fully contiguous bitmap retained the select fast path (approximately **0.01
ms**, versus 2.9 ms with the iterator). This probe used three warm-ups and five
alternating measured rounds.
- **Selective-query sanity check:** single-row lookups remained around 7–8
ms for both versions; narrow-AND-wide queries returning one row remained around
16 ms for V1 and 7 ms for V2 in the 1.2-million-row benchmark.
Measurement scope and limitations:
- These are development-time measurements based on `2f16b3872b`, comparing
the old and retained new traversal implementations. At that time an
experimental range-memory guard was present but **disabled on both sides**, so
these numbers do not include fallback-to-scan gains. That guard and its
configuration are absent from this PR. These are not newly measured timings of
the final rebased commit.
- Compared variants within each run used the same snapshot. Before/after
code runs rebuilt equivalent synthetic data with the same generation rules,
rather than sharing one physical snapshot across revisions.
- Timings cover the Paimon API, not distributed Spark/Flink SQL planning or
scheduling. They are not production speedup claims. The machine was not
isolated; JVM/GC and system-load noise remain. Phase medians need not sum to
the total median.
- This is not a general index-versus-scan policy: broad indexed reads can
still be slower than a plain scan. The heuristic also does not optimize every
run length (for example, 256-row runs still preferred select in the probe).
### Tests
Regression coverage avoids wall-clock thresholds:
- Count bitmap accesses to prove that a late batch does not walk 100,000
preceding selected positions and that gaps reuse the cached next match. The
same late-batch test fails against the original iterator (100,000 accesses,
expected at most 4).
- Check cross-batch and skipped-page positions, mixed `next`/`skip`,
non-materializing `skip`, release forwarding, an unchanged shared selection,
and unsigned 32-bit boundaries.
- Check exact ranges for run lengths 1/9/31/64/256 across high-32-bit
boundaries, and preserve the select path for large ranges with isolated 1- or
17-row gaps near the beginning, middle, and tail.
After rebasing onto master `54d8596ce7`, the focused regression suite passed
**290 tests**: Common 29, Parquet 14, Core 227, and Lance 20. Coverage includes
BTree V1/V2, bitmap/multivalue indexes, vector/full-text filtering, indexed
splits, column-group reads, and deletion vectors.
```bash
mvn -pl paimon-lance -am -Pfast-build \
-DfailIfNoTests=false -DwildcardSuites=none \
-Dtest=FileRecordIteratorTest,RoaringNavigableMap64Test,RowRangeIndexTest,BtreeGlobalIndexTableTest,BitmapGlobalIndexTableTest,MultiValueGlobalIndexTableTest,VectorSearchBuilderTest,FullTextSearchBuilderTest,IndexedSplitTest,IndexedSplitRecordReaderTest,DataEvolutionReadTest,DataEvolutionSplitReadTest,PrimaryKeyIndexedSplitReadTest,DataEvolutionFileIndexTest,DataEvolutionDeletionVectorTest,ParquetFormatReadWriteTest,LanceBTreeGlobalIndexTest
\
test
```
On the local JDK 8 environment, Mockito's dynamic attach was unavailable, so
the test command additionally preloaded the project's Byte Buddy 1.10.13 agent
via `-DextraJavaTestArgs=-javaagent:<local-agent-jar>`. No dependency or
build-file changes were needed.
Non-fast-build verification also passed:
```bash
mvn -pl paimon-api,paimon-common,paimon-core -DskipTests compile
git diff --check
```
--
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]