Skip to content

Conversation

@vinniefalco
Copy link
Member

@vinniefalco vinniefalco commented Feb 12, 2026

Summary by CodeRabbit

  • Documentation

    • Added a detailed design rationale clarifying thread-local allocator behavior, return semantics, and null handling.
  • New Features / Examples

    • Added an allocation benchmark example and accompanying build files to demonstrate allocator performance.
  • Performance / Bug Fixes

    • Safer allocator metadata handling and new fast-path allocation/deallocation paths to reduce overhead.
  • Refactor

    • Reworked allocation control flow to separate fast and slow paths for clearer, more efficient allocation handling.

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Warning

Rate limit exceeded

@vinniefalco has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Introduces fast-path allocator APIs and thread-local pool access, switches frame/trampoline storage of std::pmr::memory_resource* to memcpy-based footer storage, adds a new allocation benchmark example and build files, and adds a documentation comment in one header. No public API removals.

Changes

Cohort / File(s) Summary
Design comment
include/boost/capy/ex/frame_allocator.hpp
Added a design-rationale comment block describing the thread-local frame allocator accessor semantics, exact return/null behavior, and avoidance of dynamic TLS initialization. No functional changes.
Frame / trampoline storage
include/boost/capy/ex/run_async.hpp, include/boost/capy/ex/io_awaitable_support.hpp
Switched storage of std::pmr::memory_resource* in allocated frame footers from direct pointer writes to std::memcpy; adjusted allocation sizes and branching to select recycling allocator fast-path vs. normal allocate/deallocate; added <cstring> include where needed.
Recycling allocator implementation
include/boost/capy/ex/recycling_memory_resource.hpp, src/ex/recycling_memory_resource.cpp
Made per-thread pool accessor inline with thread_local pool; added public allocate_fast/deallocate_fast and slow-path allocate_slow/deallocate_slow; refactored do_allocate/do_deallocate to route through fast-path wrappers; added/defaulted destructor and removed separate TLS accessor in .cpp.
Examples & build
example/CMakeLists.txt, example/allocation/CMakeLists.txt, example/allocation/Jamfile, example/allocation/allocation.cpp
Added new allocation example target (CMake + Jamfile) and a benchmark program that compares the recycling allocator vs std::allocator using deep coroutine chains and timing output.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Frame as Frame::operator new / delete
    participant MemoryResource as memory_resource
    participant Footer as FrameFooter (memcpy)

    Caller->>Frame: allocate(size)
    Frame->>MemoryResource: allocate(size + footer)
    MemoryResource-->>Frame: block (footer reserved)
    Frame->>Footer: store memory_resource* via std::memcpy
    Caller->>Frame: use frame...
    Caller->>Frame: delete / deallocate
    Frame->>Footer: retrieve memory_resource* via std::memcpy
    Frame->>MemoryResource: deallocate(total size) / deallocate_fast
    MemoryResource-->>Frame: freed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hide a tiny pointer in the tail so neat,
Threads keep their pools where they quietly meet,
memcpy tucks memory down safe and small,
A benchmark hop, I cheer the call!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The pull request title 'stuff' is vague and generic, providing no meaningful information about the changeset despite substantial changes across multiple files. Use a descriptive title that reflects the main purpose, such as 'Add recycling memory resource optimization for coroutine allocation' or 'Implement fast-path allocation in recycling_memory_resource'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 12, 2026

An automated preview of the documentation is available at https://156.capy.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-02-12 22:55:46 UTC

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 12, 2026

GCOVR code coverage report https://156.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://156.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://156.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-12 23:04:41 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@example/allocation/allocation.cpp`:
- Around line 10-17: The header comment incorrectly says "20 million" while the
iterations variable (iterations) is set to 2000000 (2 million); update the
header text to "2 million" to match the actual value used (or alternatively
change the iterations variable to 20000000 if 20 million was intended) — locate
the descriptive comment at the top of allocation.cpp mentioning "20 million" and
either edit it to "2 million" or set the iterations variable named iterations to
20000000 so the comment and code agree.
🧹 Nitpick comments (3)
example/allocation/allocation.cpp (2)

30-30: counter is incremented but never read.

The global counter atomic is stored-to and fetch_add'd but its value is never printed or asserted. If it's meant to prevent the coroutine chain from being optimized away, a benchmark::DoNotOptimize-style sink or a final print would make the intent clearer. Otherwise it's dead code.

Also applies to: 37-41


67-114: Benchmark runs may be biased by ordering.

The recycling allocator always runs first, potentially paying cold-cache and JIT-like warm-up costs. Consider adding a short warm-up iteration (or running each benchmark twice and discarding the first) to reduce noise. This is an optional improvement for a benchmark example.

include/boost/capy/ex/recycling_memory_resource.hpp (1)

126-166: Fast-path allocation/deallocation logic is correct.

The size-class computation, bucket pop/push, and fallback to ::operator new/delete for oversized allocations are all sound. The unnamed alignment parameter is intentionally unused since power-of-2 size classes from ::operator new are naturally aligned.

One minor note: both methods (and allocate_slow/deallocate_slow) only access static/thread-local state, never this. They could be static member functions, which would make the API contract clearer and avoid the need for callers to downcast to call them. That said, keeping them as non-static is fine if you anticipate per-instance state in the future.

@vinniefalco vinniefalco force-pushed the develop branch 3 times, most recently from e806140 to d133786 Compare February 12, 2026 22:42
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/ex/recycling_memory_resource.cpp`:
- Around line 55-65: do_allocate currently forwards the alignment to
allocate_fast which ignores it, meaning over-aligned requests can get only
default alignment; update the fallback/slow allocation path to honor requested
alignment by calling ::operator new(rounded, std::align_val_t{alignment}) when
alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__, and ensure allocate_fast is
changed to accept and propagate the alignment parameter (or have do_allocate
handle alignment and call a new allocate_slow(aligned) path); alternatively, if
you decide not to support over-alignment, add clear documentation on
recycling_memory_resource::do_allocate / allocate_fast that only default
alignment is guaranteed.
🧹 Nitpick comments (1)
src/ex/recycling_memory_resource.cpp (1)

10-14: Consider adding a high-level implementation overview comment.

This file contains non-trivial allocation logic (thread-local + global two-tier pool, Peter Dimov bucket transfer, two-level virtual dispatch). A brief /* */ block comment after the include would help future maintainers quickly orient themselves.

As per coding guidelines, "Files containing non-trivial implementation logic should include a /* */ block comment after the includes that provides a high-level overview of how the implementation works."

Comment on lines +55 to +65
void*
recycling_memory_resource::do_allocate(std::size_t bytes, std::size_t alignment)
{
return allocate_fast(bytes, alignment);
}

void
recycling_memory_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment)
{
deallocate_fast(p, bytes, alignment);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Alignment parameter is accepted but effectively ignored.

do_allocate forwards to allocate_fast, which (per the header) takes the alignment parameter as unnamed and discards it. Since allocations are served from ::operator new(rounded) in the slow path, the returned pointer is only guaranteed to satisfy __STDCPP_DEFAULT_NEW_ALIGNMENT__ (typically 16 bytes). Callers requesting over-aligned allocations through the standard std::pmr::memory_resource::allocate(bytes, alignment) interface may receive under-aligned memory.

This is pre-existing in allocate_fast, but the new do_allocate wrapper now exposes it through the standard PMR interface where callers are more likely to rely on alignment guarantees.

Consider either:

  • Documenting that this resource only supports default alignment, or
  • Using ::operator new(rounded, std::align_val_t{alignment}) on the fallback path when alignment exceeds the default.
🤖 Prompt for AI Agents
In `@src/ex/recycling_memory_resource.cpp` around lines 55 - 65, do_allocate
currently forwards the alignment to allocate_fast which ignores it, meaning
over-aligned requests can get only default alignment; update the fallback/slow
allocation path to honor requested alignment by calling ::operator new(rounded,
std::align_val_t{alignment}) when alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__,
and ensure allocate_fast is changed to accept and propagate the alignment
parameter (or have do_allocate handle alignment and call a new
allocate_slow(aligned) path); alternatively, if you decide not to support
over-alignment, add clear documentation on
recycling_memory_resource::do_allocate / allocate_fast that only default
alignment is guaranteed.

@vinniefalco vinniefalco merged commit cd84fb0 into cppalliance:develop Feb 12, 2026
14 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.

2 participants