nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
scratch.hpp
1// core/include/nk/value/scratch.hpp
2//
3// nk memory layer — two thin classes over std::pmr.
4//
5// `std::pmr::memory_resource*` is the single allocation primitive across
6// the codebase. Public APIs accept it directly. Embedders supply their
7// own memory_resource (subclass it, or use one of the std::pmr-provided
8// resources like get_default_resource(), pool resources, etc.).
9//
10// ── ScratchArena ──────────────────────────────────────────────────────
11//
12// Per-call bump arena. IS-A std::pmr::monotonic_buffer_resource —
13// pass `&arena` directly to anything pmr-aware (pmr containers, the
14// nested ScratchVec, etc.). 64 KiB inline storage absorbs the typical
15// scratch footprint of every public toolboxes/builtin and toolboxes/signal
16// function (incl. set-op hash maps at small N and tridiagonal scratches
17// in interp1Spline). Spillover paths still work for inputs that exceed
18// this. Stack burden +60 KiB per arena is fine on every supported
19// target (Windows 1 MB main, emcc 5 MB main / 1 MB worker) since
20// arenas are scope-confined to a single public function call (no
21// nesting / no recursion).
22//
23// Lifetime: the upstream memory_resource MUST outlive every ScratchArena
24// built on it, AND every Value/DataBuffer that flowed through that
25// memory_resource. In practice the upstream comes from Engine and
26// outlives all Values it produced — standard pmr lifetime contract.
27//
28// ── ScratchVec<T> ─────────────────────────────────────────────────────
29//
30// std::pmr::vector<T> with two structural footgun closures:
31//
32// #a Implicit copy ctor + copy-assignment DELETED.
33// `auto X = other_scratchvec;` is a compile error. Without the
34// deletion it would have silently allocated X off-arena via pmr's
35// select_on_container_copy_construction → default_resource. Use
36// the alloc-aware copy ctor (inherited from base) explicitly when
37// a duplicate is genuinely intended:
38// ScratchVec<T> dup(other_scratchvec, mr);
39//
40// #b bool rejected at compile time via static_assert.
41// MSVC's pmr::vector<bool> bit-packed proxy has an init-state
42// miscompilation that bit us on the primes() sieve. Use
43// ScratchVec<std::uint8_t> for boolean masks.
44//
45// Move stays defaulted (same resource → O(1) buffer transfer; cross-
46// resource degrades to element-wise — same as plain pmr::vector).
47//
48// All other base ctors are inherited via `using base_type::base_type`.
49// The standard pmr ctor `ScratchVec<T>(n, memory_resource*)` is the
50// canonical way to construct.
51//
52// Slicing-to-base (`pmr::vector<T> X(scratch_vec);`) bypasses the
53// deletions. Don't do it.
54
55#pragma once
56
57#include <array>
58#include <cstddef>
59#include <memory_resource>
60#include <type_traits>
61#include <vector>
62
63namespace nk {
64
65template <class T>
66class ScratchVec : public std::pmr::vector<T>
67{
68 static_assert(!std::is_same_v<T, bool>,
69 "ScratchVec<bool> miscompiles on MSVC (bit-packed proxy "
70 "init bug); use ScratchVec<std::uint8_t> for masks.");
71public:
72 using base_type = std::pmr::vector<T>;
73 using base_type::base_type;
74 using base_type::operator=;
75
76 ScratchVec(const ScratchVec &) = delete;
77 ScratchVec &operator=(const ScratchVec &) = delete;
78
79 ScratchVec(ScratchVec &&) noexcept = default;
80 ScratchVec &operator=(ScratchVec &&) noexcept = default;
81};
82
83namespace detail {
84// Holds the inline scratch buffer. It is listed as the FIRST base of
85// ScratchArena so it is constructed BEFORE the monotonic_buffer_resource
86// base that points into it (base-from-member idiom). If the buffer were a
87// plain member, the resource base — initialised with storage_.data()/size()
88// — would run first and read a member whose lifetime has not begun, which
89// clang flags as -Wuninitialized.
91 static constexpr std::size_t kInlineBytes = 65536;
92 alignas(std::max_align_t) std::array<std::byte, kInlineBytes> bytes;
93};
94} // namespace detail
95
96class ScratchArena : private detail::ScratchStorage,
97 public std::pmr::monotonic_buffer_resource
98{
99public:
100 explicit ScratchArena(std::pmr::memory_resource *upstream)
101 : std::pmr::monotonic_buffer_resource(
102 bytes.data(), bytes.size(),
103 upstream ? upstream : std::pmr::get_default_resource()) {}
104
105 ScratchArena(const ScratchArena &) = delete;
106 ScratchArena &operator=(const ScratchArena &) = delete;
107 ScratchArena(ScratchArena &&) = delete;
108 ScratchArena &operator=(ScratchArena &&) = delete;
109};
110
111} // namespace nk