Skip to content

perf: add monotomic/slab allocator and improve HSA - #779

Merged
kexianda merged 2 commits into
bytedance:mainfrom
kexianda:allocator2
Oct 16, 2026
Merged

kexianda merged 2 commits into
bytedance:mainfrom
kexianda:allocator2

Conversation

@kexianda

@kexianda kexianda commented Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

  1. HashStringAllocator and memory::StlAllocator are very slow:
    add monotomic/slab allocator to accelerate them.

  2. HashStringAllocator is still used in Agg, optimize the implementation.

  3. Use SIMD to optimize common BitUtil.h

Issue Number: close #789

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 🚀 Performance improvement (optimization)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)
  • 🔨 Refactoring (no logic changes)
  • 🔧 Build/CI or Infrastructure changes
  • 📝 Documentation only

Description

Describe your changes in detail.
For complex logic, explain the "Why" and "How".

Performance Impact

  • No Impact: This change does not affect the critical path (e.g., build system, doc, error handling).

  • Positive Impact: I have run benchmarks.

    Click to view Benchmark Results
    TPC-DS 100 GB benchmark results on a development machine show a 4.8%
    performance improvement
    
  • Negative Impact: Explained below (e.g., trade-off for correctness).

Release Note

Please describe the changes in this PR

Release Note:

Release Note:
- perf: improve allocator's performance

Checklist (For Author)

  • I have added/updated unit tests (ctest).
  • I have verified the code with local build (Release/Debug).
  • I have run clang-format / linters.
  • (Optional) I have run Sanitizers (ASAN/TSAN) locally for complex C++ changes.
  • No need to test or manual test.

Breaking Changes

  • No

  • Yes (Description: ...)

    Click to view Breaking Changes
    Breaking Changes:
    - Description of the breaking change.
    - Possible solutions or workarounds.
    - Any other relevant information.
    

@kexianda
kexianda force-pushed the allocator2 branch 2 times, most recently from bfe0d45 to 2c27ab7 Compare July 24, 2026 12:39
@kexianda
kexianda force-pushed the allocator2 branch 2 times, most recently from 0140d69 to c111938 Compare August 20, 2026 08:39
@kexianda
kexianda force-pushed the allocator2 branch 2 times, most recently from 4bfd053 to cbfff28 Compare August 26, 2026 07:20
@kexianda
kexianda force-pushed the allocator2 branch 2 times, most recently from beaa72d to fc8a7b4 Compare September 4, 2026 02:03
@kexianda
kexianda force-pushed the allocator2 branch 2 times, most recently from 422f475 to 5b69100 Compare September 4, 2026 08:15
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Summary

Summary by CodeRabbit

  • New Features

    • Added an option to use monotonic string allocation for query operations such as joins, aggregations, and sorting. Strings allocated this way are reclaimed when the relevant container is cleared or destroyed.
    • Added slab-based memory resources and allocators for query and file-writing workloads.
    • Added benchmarks for string allocation, row containers, and slab allocators, with additional memory-usage reporting.
  • Bug Fixes

    • Improved bit-range queries and handling of unaligned strings.
    • Improved memory accounting and allocation behavior, including more reliable cleanup and spill handling.
    • Improved handling of long strings in joins and sorting.

The pull request adds slab and monotonic memory resources and integrates configurable monotonic string allocation into row containers, hash tables, aggregation, joins, and sorting. It updates dictionary encoders and other container consumers to use slab allocation. It also revises HashStringAllocator free-list handling, adds architecture-specific bit-range scans, and introduces allocator and query benchmarks with related tests.

Suggested reviewers: lizhen-0710

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🟡 Moderate · up to 3d20a

The new allocators change memory accounting and string storage in hot query paths, and they are enabled by default. Several open concerns could affect SVE builds, sort memory reservation, spill sizing, and memory accounting. Resolve or explicitly accept them before merging.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 36bc9403-9e6c-41d7-bce4-4c42799f1cee
📥 Commits

Reviewing files that changed from the base of the PR and between c68bc47 and 64f2d83.

📒 Files selected for processing (62)
  • .pre-commit-config.yaml
  • bolt/benchmarks/tpch/TpchBenchmark.cpp
  • bolt/benchmarks/unstable/CMakeLists.txt
  • bolt/benchmarks/unstable/SlabAllocatorBenchmark.cpp
  • bolt/common/base/BitUtil.h
  • bolt/common/base/tests/BitUtilTest.cpp
  • bolt/common/memory/CMakeLists.txt
  • bolt/common/memory/CompactDoubleList.h
  • bolt/common/memory/HashStringAllocator.cpp
  • bolt/common/memory/HashStringAllocator.h
  • bolt/common/memory/MallocAllocator.cpp
  • bolt/common/memory/MemoryPool.h
  • bolt/common/memory/MemoryResource.cpp
  • bolt/common/memory/MemoryResource.h
  • bolt/common/memory/tests/CMakeLists.txt
  • bolt/common/memory/tests/CompactDoubleListTest.cpp
  • bolt/common/memory/tests/HashStringAllocatorTest.cpp
  • bolt/common/memory/tests/MemoryPoolTest.cpp
  • bolt/common/memory/tests/SlabAllocatorTest.cpp
  • bolt/core/QueryConfig.h
  • bolt/core/tests/QueryConfigTest.cpp
  • bolt/dwio/dwrf/test/WriterFlushTest.cpp
  • bolt/dwio/dwrf/writer/IntegerDictionaryEncoder.h
  • bolt/dwio/dwrf/writer/StringDictionaryEncoder.h
  • bolt/exec/ContainerRow2RowSerde.cpp
  • bolt/exec/ContainerRowSerde.cpp
  • bolt/exec/GroupingSet.cpp
  • bolt/exec/HashBuild.cpp
  • bolt/exec/HashTable.cpp
  • bolt/exec/HashTable.h
  • bolt/exec/RowContainer.cpp
  • bolt/exec/RowContainer.h
  • bolt/exec/SortBuffer.cpp
  • bolt/exec/Spill.cpp
  • bolt/exec/Spill.h
  • bolt/exec/SpillFile.cpp
  • bolt/exec/SpillFile.h
  • bolt/exec/Spiller.h
  • bolt/exec/VectorHasher.cpp
  • bolt/exec/VectorHasher.h
  • bolt/exec/WindowPartition.cpp
  • bolt/exec/benchmarks/CMakeLists.txt
  • bolt/exec/benchmarks/HashAggregationStringAllocationBenchmark.cpp
  • bolt/exec/benchmarks/HashJoinStringAllocationBenchmark.cpp
  • bolt/exec/benchmarks/HashStringMonotonicAllocatorBenchmark.cpp
  • bolt/exec/benchmarks/RowContainerStoreStringBenchmark.cpp
  • bolt/exec/benchmarks/RowContainerVarcharOrderByBenchmark.cpp
  • bolt/exec/benchmarks/SortStringAllocationBenchmark.cpp
  • bolt/exec/benchmarks/StlContainerAllocatorBenchmark.cpp
  • bolt/exec/meta/MetaRowSorterApi.h
  • bolt/exec/tests/HashJoinTest.cpp
  • bolt/exec/tests/HashTableTest.cpp
  • bolt/exec/tests/OrderByTest.cpp
  • bolt/exec/tests/RowContainerTest.cpp
  • bolt/functions/lib/SubscriptUtil.h
  • bolt/jit/RowContainer/RowContainerCodeGenerator.cpp
  • bolt/jit/RowContainer/RowContainerCodeGenerator.h
  • bolt/jit/RowContainer/RowEqVectorsCodeGenerator.cpp
  • bolt/jit/tests/RowEqRowIRTest.cpp
  • bolt/jit/tests/ThrustJITv2Test.cpp
  • bolt/type/tests/StringViewTest.cpp
  • bolt/vector/ComplexVector.h

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Comment thread bolt/benchmarks/unstable/CMakeLists.txt
Comment thread bolt/common/base/BitUtil.h Outdated
Comment thread bolt/common/memory/MemoryResource.cpp Outdated
Comment thread bolt/common/memory/MemoryResource.cpp
Comment thread bolt/common/memory/MemoryResource.cpp
Comment thread bolt/common/memory/MemoryResource.cpp
Comment thread bolt/common/memory/tests/SlabAllocatorTest.cpp
Comment thread bolt/exec/RowContainer.cpp Outdated
Comment thread bolt/exec/SortBuffer.cpp
Comment thread bolt/exec/tests/OrderByTest.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments: 1
bolt/dwio/dwrf/test/WriterContextTest.cpp: 1

50-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the stale comment, and shrink WideTableDictionaryMemory.

The comment on Line 89 says that per-column resources "would charge about 80 GiB". That figure assumes a 1 MiB first reservation for each resource. The first reservation is now 64 KiB (kInitialReservationBytes), so 80,000 per-column resources would charge about 5 GiB.

The test does not need 80,000 columns to detect the regression. It builds 80,000 column writers and uses up to about 512 MiB of real memory. That makes it slow, and it can fail with OOM on shared CI runners. Assume 8,000 columns and a 128 MiB root pool. Per-column resources would then need about 500 MiB and would still exhaust the pool. Expected real usage drops to about one tenth of the current value. Scale the GENERAL bound to match.

♻️ Proposed change
-  constexpr uint32_t kNumColumns = 80'000;
+  constexpr uint32_t kNumColumns = 8'000;
   constexpr int64_t kMiB = 1 << 20;
   auto root = memory::memoryManager()->addRootPool(
-      "wide_table_dictionaries", 512 * kMiB);
+      "wide_table_dictionaries", 128 * kMiB);
...
-    // Per-column slab resources would charge about 80 GiB and exhaust the
-    // 512 MiB pool before reaching this point. Only one reservation's worth
+    // Per-column slab resources would charge at least 64 KiB each (about
+    // 500 MiB) and exhaust the 128 MiB pool. Only one reservation's worth
     // of unused slab space may be charged across the entire writer.

Also lower the 384 * kMiB bound on Line 99 to about 48 * kMiB.

Also applies to: 89-91


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1d9af30b-c80a-48a4-8d20-548024e7aceb
📥 Commits

Reviewing files that changed from the base of the PR and between 64f2d83 and 13cac7d.

📒 Files selected for processing: 17
  • bolt/common/base/BitUtil.h
  • bolt/common/memory/MemoryResource.cpp
  • bolt/common/memory/MemoryResource.h
  • bolt/common/memory/tests/SlabAllocatorTest.cpp
  • bolt/dwio/dwrf/test/TestDictionaryEncodingUtils.cpp
  • bolt/dwio/dwrf/test/TestEncodingSelector.cpp
  • bolt/dwio/dwrf/test/TestIntegerDictionaryEncoder.cpp
  • bolt/dwio/dwrf/test/TestStringDictionaryEncoder.cpp
  • bolt/dwio/dwrf/test/WriterContextTest.cpp
  • bolt/dwio/dwrf/writer/ColumnWriter.cpp
  • bolt/dwio/dwrf/writer/IntegerDictionaryEncoder.h
  • bolt/dwio/dwrf/writer/StringDictionaryEncoder.h
  • bolt/dwio/dwrf/writer/WriterContext.cpp
  • bolt/dwio/dwrf/writer/WriterContext.h
  • bolt/exec/Spiller.cpp
  • bolt/exec/Spiller.h
  • bolt/vector/tests/VectorTest.cpp

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

@kexianda
kexianda force-pushed the allocator2 branch 3 times, most recently from ba23cd8 to 3d20ac9 Compare October 15, 2026 05:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Preserve the shared-allocator constructor overload. · RowContainer.h:339

bolt/exec/RowContainer.h:339
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the shared-allocator constructor overload.

Existing callers that pass a std::shared_ptr<HashStringAllocator> as the final argument cannot compile with the new RowContainerParam signature. Keep a forwarding overload for that argument while adding the new configuration overload. This preserves the stated no-breaking-changes contract.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6c4cc2d9-6665-47af-b4cb-8a7632b9e579
📥 Commits

Reviewing files that changed from the base of the PR and between ba23cd8 and 3d20ac9.

📒 Files selected for processing: 3
  • bolt/exec/RowContainer.cpp
  • bolt/exec/RowContainer.h
  • bolt/exec/tests/RowContainerTest.cpp

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

@kexianda kexianda changed the title feat: add monotomic/slab allocator perf: add monotomic/slab allocator and improve HSA Oct 15, 2026
Comment thread bolt/benchmarks/unstable/SlabAllocatorBenchmark.cpp Outdated
Comment thread bolt/exec/RowContainer.cpp Outdated
HashStringAllocator and memory::StlAllocator are slow for small, frequent
allocations.

Use `MonotonicMemoryResource`/`MonoAllocator` for append-only variable-width
data whose lifetime matches a batch of rows. Use
`SlabMemoryResource`/`SlabAllocator` for frequently allocated small objects
that require individual deallocation.

Microbenchmarks show that SlabAllocator is about 4x faster than
HashStringAllocator and about 40x faster than memory::StlAllocator.

```
============================================================================
[...]s/unstable/SlabAllocatorBenchmark.cpp     relative  time/iter   iters/s
============================================================================
HashStringAllocatorFifoAllocFree256K_8Threads              58.47ns    17.10M
AlignedStlAllocatorFifoAllocFree256K_8Threads   10.372%   563.70ns     1.77M
SlabAllocatorFifoAllocFree256K_8Threads         384.63%    15.20ns    65.79M
```

In the append-only scenario, the monotonic allocator is about 12x faster
than HashStringAllocator.

```
============================================================================
[...]StringMonotonicAllocatorBenchmark.cpp     relative  time/iter   iters/s
============================================================================
HashStringAllocatorAllocateNoFree                            6.22s   160.90m
MonotonicMemoryResourceAllocateNoFree           1287.9%   482.60ms      2.07
```

TPC-DS 100 GB benchmark results on a development machine show a 1.4%
performance improvement.
Optimize bitmap queries and free-list mutation on the allocator's hot paths.

- Vectorize BitUtil range queries. isAllSet(), findFirstBit(), and findLastBit() scan complete words with AVX2, Arm SVE, or RISC-V Vector instructions when available, while scalar prefix/suffix handling preserves arbitrary bit-range boundaries and prevents out-of-bounds vector loads. Add exhaustive range-query coverage for set and unset bits.

- Replace allocator-side linear bitmap scanning with a two-level bitmap. Use freeNonEmpty_ for per-list availability and freeBitmapIndx for non-empty bitmap words, with direct 64-bit register operations for bit updates. Only update the first-level index when a bitmap word transitions between empty and non-empty.

- Reduce CompactDoubleList pointer work by caching decoded links in insert() and remove(), returning the post-removal empty state from remove(), and adding a specialized popFront() for allocation. The allocation path avoids decoding the known sentinel predecessor and avoids a separate empty() check.

- Reuse the already-known free-list index while allocating, cache Header size and physical-next state before mutating the Header, and avoid redundant size, index, and Header reloads.

- Specialize freeRestOfBlock() instead of routing the remainder through the general free() path. Handle only the required forward coalescing, update counters and both bitmap levels directly, cache the final remainder size and successor Header, and write free/trailer metadata without recomputing physical neighbors.

- Extend BitUtil, CompactDoubleList, and HashStringAllocator tests for range boundaries, pop-front behavior, empty-state reporting, bitmap consistency, and allocation/free coalescing invariants.

Microbenchmark results show a 52.9% increase in throughput.

@zhangxffff zhangxffff left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@kexianda
kexianda added this pull request to the merge queue Oct 16, 2026
Merged via the queue into bytedance:main with commit 5c36d78 Oct 16, 2026
18 of 19 checks passed
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.

[Feature] Adopt new allocators for fast allocating and reduce RSS

3 participants