Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Fill the buffer before `std::iota(begin(), end(), 1)`, or call `resize(n)` first
## Hot path

- Not-full `emplace_back` / `emplace_front`: test `size_ < capacity_`, construct into the slot, then publish the cursor and `size_`.
- Full `emplace_*`: `ensure_writable()` (cold; throws `length_error` only when `capacity()==0`), then `overwrite_at` (in-place assign), then bump cursors. Full `emplace_front` writes `drop(first_)` before publishing `first_`.
- Full `emplace_*`: if `capacity()==0`, call cold `ensure_writable()` (throws `length_error`); else `overwrite_at` (in-place assign) with no call, then bump cursors. Full `emplace_front` writes `drop(first_)` before publishing `first_`.
- `bump` / `drop` wrap with `buff_` / `limit_`. `ptr_at` uses a linearized fast path when `last_ > first_` or `first_ == buff_`.
- Skip `allocator_traits::destroy` when `T` is trivially destructible.
- `EQZ_ALWAYS_INLINE` on bump/drop/`ptr_at`/emplace/`overwrite_at`/`[]`/`front`/`back`/`empty`/`full`/`pop_*`. gcc/clang only; other compilers get `inline`.
Expand All @@ -80,6 +80,8 @@ Measured slower or incorrect on the Boost suite, so these stay out:
- Boost-style `T*` linear iterators (logical index iterators stay).
- Skipping the empty-buffer cursor reset on the last `pop_*`.
- Inlining `throw` into `emplace_*` (puts EH in the hot function).
- Dropping `EQZ_COLD` from `ensure_writable` while the full path still calls it (GCC 13 `copy/full` ~11 µs).
- Always-inlining the cap==0 check with a separate cold throw helper (same GCC copy regression).
- `[[likely]]` / `[[unlikely]]` on `full()` or wrap (moves EH / branch layout).
- Duplicating `bump` as in-place `++last_` inside `emplace_back`.
- Computing the base as `limit_ - capacity_` instead of caching `buff_`.
11 changes: 8 additions & 3 deletions include/eqz/circular_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,9 @@ class circular_buffer {
++size_;
return *slot;
}
ensure_writable();
if (capacity_ == 0) {
ensure_writable();
}
T* slot = last_;
overwrite_at(slot, std::forward<Args>(args)...);
last_ = bump(slot);
Expand All @@ -650,7 +652,9 @@ class circular_buffer {
++size_;
return *slot;
}
ensure_writable();
if (capacity_ == 0) {
ensure_writable();
}
T* slot = drop(first_);
overwrite_at(slot, std::forward<Args>(args)...);
first_ = last_ = slot;
Expand Down Expand Up @@ -1052,7 +1056,8 @@ class circular_buffer {
limit_ = buff_ + n;
}

/// Cold path: `capacity()==0` throws. Full `emplace_*` call this instead of inlining `throw`.
/// Throws when `capacity()==0`. Full `emplace_*` call this only on that edge
/// so the overwrite path does not `call` a cold function every iteration.
EQZ_COLD constexpr void ensure_writable() const {
if (capacity_ == 0) {
throw std::length_error("eqz::circular_buffer: capacity is 0");
Expand Down
Loading