perf: upgrade roaring to 0.11 and improve bitmap/range conversions#5961
perf: upgrade roaring to 0.11 and improve bitmap/range conversions#5961westonpace merged 2 commits intolance-format:mainfrom
Conversation
Use roaring 0.11's `next_range()` in `bitmap_to_ranges` to extract contiguous ranges directly instead of iterating bit-by-bit. Add `ranges_to_bitmap` with an adaptive strategy that picks `from_sorted_iter` for short ranges and `insert_range` for long ranges.
Code ReviewP0: Breaking Change - Wire Format CompatibilityThe PR description notes:
This is a breaking change that needs careful consideration:
At minimum, this should be documented in release notes and potentially in a migration guide. P1: Potential Panic on Division by ZeroIn let sample_size = ranges.len().min(10);
let avg_len: u64 = ranges
.iter()
.take(sample_size)
.map(|r| r.end - r.start)
.sum::<u64>()
/ sample_size as u64;If all sampled ranges have P1: Missing DocumentationThe new
Minor Notes
Overall the implementation looks correct and the performance gains are impressive. The main concerns are around the breaking wire format change and the missing documentation. |
|
ACTION NEEDED The PR title and description are used as the merge commit message. Please update your PR title and description to match the specification. For details on the error please inspect the "PR Title Check" action. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
before serialize the data, can call |
|
Upgrade to Roaring 0.11 gives performance benefits but breaks serialization/forward compatibility (older lance code cannot read lance file created by newer lance code) Roaring 0.11 introduces These serialized bitmaps are persisted across multiple locations in the Lance format:
For RoaringBitmap, we can call @westonpace @wjones127 Would like to hear your opinion of dealing with such scenario. Any chance when next time we upgrade to a newer lance version, and we can have this change goes in as well? NEVER MIND. Validated that roaring starting from 0.10.2 can deserialize all of roaring 0.11+. |
|
Validated that roaring starting from 0.10.2 can deserialize all of roaring 0.11+. |
| while let Some(r) = iter.next_range() { | ||
| ranges.push(*r.start() as u64..(*r.end() as u64 + 1)); | ||
| } | ||
| ranges |
| // from_sorted_iter appends each value in O(1) but must visit every u32. | ||
| // insert_range bulk-fills containers but does a binary search per call. | ||
| // Crossover is ~6: below that, iterating all values is cheaper. | ||
| if avg_len <= 6 { |
Use roaring 0.11's
next_range()inbitmap_to_rangesto extract contiguous ranges directly instead of iterating bit-by-bit. Addranges_to_bitmapwith an adaptive strategy that picksfrom_sorted_iterfor short ranges andinsert_rangefor long ranges.This improve 10M bitmap <-> vec roundtrip from
33ms - 158ms rangeto0.004ms to 53msdepending on the selected rate.validated that this does not have forward compatibility issue, the serialized code created by roaring 0.11+ can be read by roaring >= 0.10.2 and lance never shipped with roaring version < 0.10.2, and roaring is always backward compatible. Verified that no backward/forward compatibility issue introduce by this change.
=== 1% selected ===
Ranges: 100,000 | Bitmap: 197 KB | Vec: 1.5 MB
Vec -> Bitmap: 0.428 ms
Bitmap -> Vec: 3.068 ms
Roundtrip: 3.488 ms
=== 5% selected ===
Ranges: 500,000 | Bitmap: 978 KB | Vec: 7.6 MB
Vec -> Bitmap: 1.909 ms
Bitmap -> Vec: 19.384 ms
Roundtrip: 21.297 ms
=== 10% selected ===
Ranges: 1,000,000 | Bitmap: 1.2 MB | Vec: 15.3 MB
Vec -> Bitmap: 5.061 ms
Bitmap -> Vec: 5.802 ms
Roundtrip: 11.050 ms
=== 30% selected ===
Ranges: 3,000,000 | Bitmap: 1.1 MB | Vec: 45.8 MB
Vec -> Bitmap: 13.044 ms
Bitmap -> Vec: 19.142 ms
Roundtrip: 32.100 ms
=== 50% selected ===
Ranges: 5,000,000 | Bitmap: 1.2 MB | Vec: 76.3 MB
Vec -> Bitmap: 21.009 ms
Bitmap -> Vec: 31.781 ms
Roundtrip: 53.135 ms
=== 70% selected ===
Ranges: 3,000,001 | Bitmap: 1.2 MB | Vec: 45.8 MB
Vec -> Bitmap: 28.787 ms
Bitmap -> Vec: 19.244 ms
Roundtrip: 48.007 ms
=== 90% selected ===
Ranges: 1,000,000 | Bitmap: 3.3 MB | Vec: 15.3 MB
Vec -> Bitmap: 26.929 ms
Bitmap -> Vec: 4.847 ms
Roundtrip: 31.887 ms
=== 95% selected ===
Ranges: 500,000 | Bitmap: 1.9 MB | Vec: 7.6 MB
Vec -> Bitmap: 14.380 ms
Bitmap -> Vec: 2.332 ms
Roundtrip: 16.514 ms
=== 99% selected ===
Ranges: 100,000 | Bitmap: 393 KB | Vec: 1.5 MB
Vec -> Bitmap: 2.497 ms
Bitmap -> Vec: 0.455 ms
Roundtrip: 3.005 ms
=== 100% selected ===
Ranges: 1 | Bitmap: 2 KB | Vec: 16 B
Vec -> Bitmap: 0.003 ms
Bitmap -> Vec: 0.001 ms
Roundtrip: 0.004 ms