Skip to content

Commit 662dafc

Browse files
drop validate fast/full fork; memoize structural body via MSB of __magic
VALIDATION (the headline change) ================================ Drop the heap_validate_self_fast / heap_validate_self split entirely. The meson option -Dheap_validate_full and the FEATURE_HEAP_VALIDATE_FULL Config macro go with it. Every dispatch now runs the full structural validator -- no opt-in for "the safe one". To keep that fast, the MSB of every magic-bearing object's __magic field becomes a "structural invariants need re-checking" memo bit shared across all 11 magic-bearing types (5 allocators: Heap, Slab, Arena, Page, Budget, Debug; 5 containers: Vec, List, BitVec, Map, Graph; plus the Graph mutation-epoch path). Pattern in <Misra/Types.h>: MAGIC_VALIDATED_BIT (1ULL << 63) MAGIC_MATCHES(actual, expected) -- bit-stripped comparison MAGIC_MARK_DIRTY(obj_ptr) -- mutator-side OR-in MAKE_NEW_MAGIC_VALUE now ANDs against ~MAGIC_VALIDATED_BIT so the identity payload can NEVER collide with the memo bit regardless of what string the caller supplies (structural guarantee, not just a "magic strings start with lowercase" convention). Per-type validators split: precheck (NULL + magic + memo gate) + structural body. Memo bit clear -> body skipped. Mutators that change validator-read fields (data/capacity/allocator/grow paths) MARK_DIRTY; ones that preserve every invariant by construction (push, pop, single- element insert/remove without growth, length-only writes that stay <= capacity) do not. Init macros pre-set the bit so the first call after init always runs the body. Tests that planted invalid values directly into struct fields to trigger deadend assertions are themselves mutators and now call MAGIC_MARK_DIRTY after the bypass (Str.Type.c x2, List.Deadend.c x5, BitVec.Foreach.Deadend.c x1). List.Type.c layout test routes its __magic comparison through MAGIC_MATCHES instead of bare ==. Benchmark plumbing trimmed to a single tier: README + template + run.py + meson all drop the validate-full / validate-fast columns and the two-tier build steps; Benchmark/meson.build collapses the four bench-misra-* executables into one foreach. CONVENTION REVIEW SWEEP ======================= An 8-subsystem independent review against CODING-CONVENTIONS.md drove the following cleanups. Findings the reviewer flagged that turned out to be false positives or stylistic equivalents were left alone with rationale documented in the commit prose. Bugs / hardening ---------------- - Http.c: header-count cap (HTTP_REQUEST_HEADERS_MAX = 100) so a malformed stream cannot grow req->headers without bound. Public HttpHeaderDeinit / HttpRequestDeinit / HttpResponseDeinit docs aligned with the LOG_FATAL-on-NULL code. The http_header_deinit callback's dead NULL guard removed (containers guarantee non-NULL). - DwarfUnwind.c: CfiVm.code_align retyped from i64 to u64 (CIE ULEB128 cannot be signed) and rejected when source value is non-positive; every advance_loc multiplication/addition now goes through MulOverflow64 + AddOverflow64 with UINT64_MAX saturation, so a crafted CIE that ships a huge ULEB128 can no longer wrap into an unbounded location advance. - JSON.h JW_STR: single-evaluation of `s` via UNPL(jw_s) + cache StrLen result into UNPL(jw_len) so the source expression is only expanded once and the length is only computed once. - Map.c map_insert_entry: `(Zstr)entry + offset` byte arithmetic over opaque entry bytes retyped to `(const u8 *)`. Architectural ------------- - KvConfig.c stops reaching into <Misra/Std/Container/Map/Private.h>; uses the public MapGetFirstPtr macro instead. - All 9 parsers (Elf, Pe, Pdb, MachO, Dwarf, Http, KvConfig, Dns, JSON) now declare their snake_case backends in Include/Misra/Parsers/<X>/ Private.h. Public headers expose only PascalCase macros + types. - Sys helpers path_exists / basename_of / append_dirname deduped from 4 separate copies into Source/Misra/Sys/_Helpers.h. - parse_ipv4 / parse_ipv6 / hex_nibble_value deduped from Socket.c + Dns.c into Source/Misra/Sys/_IpParse.h. - fp_walk + BACKTRACE_MAX_WALK lifted out of the Darwin / Linux arms of Backtrace.c into a single shared block gated on !PLATFORM_WINDOWS && (gcc||clang). - Sys/Dns.c random_query_id drops the file-local LCG global; the non-direct-syscall fallback now defers to the project's existing Prng16() (the documented process-lifetime random singleton). Naming / drift -------------- - SYS_DIR_ENTRY_TYPE_* / SYS_PROC_STATUS_* / SYS_ERROR_STR_MAX_LENGTH: dropped the SYS_ prefix (convention reserves SYS_ for syscall-number constants in _Syscall.h). - All freestanding / start / syscall internal helpers lose the misra_ prefix: misra_sysN -> direct_sysN, misra_envp -> envp_global, misra_start_c -> linux_start_c, misra_start -> windows_start (meson /ENTRY: updated to match), g_misra_start_cmdline -> g_start_cmdline, g_misra_start_argv -> g_start_argv, misra_freestanding_size_t -> freestanding_size_t, misra_security_failure -> security_failure, misra_stdio_common_vsprintf_stub -> stdio_common_vsprintf_stub, misra_getenv_stub -> getenv_stub, MISRA_DARWIN_SC -> DARWIN_SC. Bin/Beam.c updated to match the renames. - Pdb.c kMsfMagic7 (foreign k-prefix) -> MSF_MAGIC_7; also retyped char[32] -> u8[32] since it's an on-disk byte signature. - Sys/Proc.c sys_proc_read_internal -> proc_read_internal; STDIN_FILENO / STDOUT_FILENO / STDERR_FILENO get literal 0/1/2 instead of the libc FILENO(stdin)/(stdout)/(stderr) macros that were pulling libc FILE* into the freestanding plumbing. - Pdb.c + DwarfInfo.c qsort comparators: int return -> i32, (a,b) parameters -> (lhs,rhs) to match the GenericCompare contract. Build / config cleanup ---------------------- - meson_options.txt: dead `iter` option (force-pinned to true) removed along with the FEATURE_ITER macro emit and the install-subdir- excludes branches. - meson.build: dead FEATURE_PARSER_ELF / FEATURE_PARSER_PE set10 emits removed (those macros were never referenced from any C source); a comment line that referenced a specific commit hash was deleted. - _StartWin.c's file: banner normalised to the lowercase short shape used by the other 184 source files. - All header _Generic dispatch in Sys/Socket.h, Sys/Dns.h, Sys/MachoCache.h, Sys/PdbCache.h, Parsers/Dns.h converted from function-pointer-selection style to the canonical inlined-arm-body shape used by Dir.h. Memo-bit review followups ------------------------- - Tests/Std/List.Type.c:21: __magic comparison now routes through MAGIC_MATCHES. - Slab.c slab_finalize_runtime_consts: ends with SLAB_MARK_DIRTY so the routine is self-contained against future call paths. - Three unnecessary MAGIC_MARK_DIRTY calls dropped where the transition preserves every structural invariant by construction (Arena.c chunk- add + Reset, List.c insert_into_list, Vec.c reduce_space_vec length==0 branch). False-positive review findings explicitly NOT applied ----------------------------------------------------- - "DwarfInfo.c:444 AddOverflow64 polarity is inverted." Math.h:178-182 documents AddOverflow64 returning true on SUCCESS; the assignment in DwarfInfo.c:444 is correct. - "BitVec is missing MARK_DIRTY in Resize/Clear/Remove*/Shift*/etc." Those only change length, not data/capacity/byte_size; the structural validator memoization pattern (matching Vec) only flips dirty when validator-read fields change. - "DwarfUnwind.c:259 discards decode_eh_ptr failure." The trailing IterMustMove resets the cursor regardless. - "Graph PascalCase functions in Access.h/Ops.h are wrappers." The CODING-CONVENTIONS skeleton's "Public API: PascalCase" rule applies; these are public API, not alias wrappers around a backend. - "PeSection.name/MachoSection.name should be u8[N]." They are NUL- terminated string fields, used via ZstrCompare; char[N] is correct. 87 files changed, 1428 insertions(+), 1333 deletions(-)
1 parent e6c3217 commit 662dafc

87 files changed

Lines changed: 1428 additions & 1333 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Benchmark/README.md

Lines changed: 41 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,129 +14,112 @@ Single alloc/free pair, 16 B:
1414
| glibc | 7.3 ns |
1515
| jemalloc | 3.5 ns |
1616
| mimalloc | 3.0 ns |
17-
| misra (Heap, validate-full) | 14.8 ns |
18-
| misra (Heap, validate-fast) | 14.3 ns |
19-
| misra-correct (Slab, validate-full) | 14.0 ns |
20-
| misra-correct (Slab, validate-fast) | 9.0 ns |
17+
| misra (Heap) | 14.8 ns |
18+
| misra-correct (Slab) | 14.0 ns |
2119

2220
## Timing
2321

2422
### Single alloc/free pair
2523

2624
One `alloc(size)` immediately followed by `free(ptr)`, repeated. Hot reuse — the same slot churns. Time per pair, lower is better.
2725

28-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
29-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
30-
| 16 B | 7.3 | 3.5 | 3.0 | 3.4 | 14.8 | 14.3 | 14.0 | 9.0 | 12.0 | n/a |
31-
| 64 B | 7.2 | 3.6 | 3.2 | 3.5 | 15.0 | 14.4 | 14.0 | 9.1 | 12.2 | n/a |
32-
| 256 B | 7.2 | 3.9 | 13.7 | 3.4 | 15.0 | 14.5 | 13.9 | 9.0 | 12.4 | n/a |
33-
| 1 KiB | 19.3 | 5.5 | 15.1 | 3.4 | 15.0 | 14.5 | 13.9 | 9.1 | 12.2 | n/a |
34-
| 4 KiB | 15.1 | 10.4 | 10.6 | 3.6 | 19.1 | 10.6 | 13.9 | 9.3 | 12.2 | 19.8 |
35-
| 16 KiB | 16.2 | 18.8 | 19.6 | 3.6 | 19.2 | 10.6 | 19.3 | 19.6 | 12.3 | 19.7 |
36-
| 64 KiB | 17.2 | 267.0 | 19.6 | 3.6 | 19.2 | 10.6 | 19.2 | 19.5 | 12.3 | 20.0 |
26+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
27+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
28+
| 16 B | 7.3 | 3.5 | 3.0 | 3.4 | 14.8 | 14.0 | 12.0 | n/a |
29+
| 64 B | 7.2 | 3.6 | 3.2 | 3.5 | 15.0 | 14.0 | 12.2 | n/a |
30+
| 256 B | 7.2 | 3.9 | 13.7 | 3.4 | 15.0 | 13.9 | 12.4 | n/a |
31+
| 1 KiB | 19.3 | 5.5 | 15.1 | 3.4 | 15.0 | 13.9 | 12.2 | n/a |
32+
| 4 KiB | 15.1 | 10.4 | 10.6 | 3.6 | 19.1 | 13.9 | 12.2 | 19.8 |
33+
| 16 KiB | 16.2 | 18.8 | 19.6 | 3.6 | 19.2 | 19.3 | 12.3 | 19.7 |
34+
| 64 KiB | 17.2 | 267.0 | 19.6 | 3.6 | 19.2 | 19.2 | 12.3 | 20.0 |
3735

3836
_Values in ns._
3937

4038
### Batch alloc-N-then-free-N
4139

4240
`N` allocs of fixed size, then `N` frees, then repeat. Holds `N` allocations live at peak. Time per batch, lower is better.
4341

44-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
45-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
46-
| 128 × 64 B | 1.4 | 0.5 | 0.4 | 0.5 | 1.6 | 1.5 | 1.9 | 1.2 | n/a | n/a |
47-
| 1024 × 64 B | 10.9 | 10.8 | 3.6 | 3.9 | 12.4 | 12.2 | 18.9 | 14.8 | n/a | n/a |
48-
| 8192 × 64 B | 87.3 | 88.7 | 33.2 | 38.9 | 99.6 | 98.8 | 399.5 | 373.9 | n/a | n/a |
42+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
43+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
44+
| 128 × 64 B | 1.4 | 0.5 | 0.4 | 0.5 | 1.6 | 1.9 | n/a | n/a |
45+
| 1024 × 64 B | 10.9 | 10.8 | 3.6 | 3.9 | 12.4 | 18.9 | n/a | n/a |
46+
| 8192 × 64 B | 87.3 | 88.7 | 33.2 | 38.9 | 99.6 | 399.5 | n/a | n/a |
4947

5048
_Values in us._
5149

5250
### Alloc + write every byte + free
5351

5452
Same shape as the pair test but writes every byte of the allocation before freeing. Catches page-fault cost that pure alloc/free hides. Time per item, lower is better.
5553

56-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
57-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
58-
| 64 B | 7.8 | 4.4 | 3.2 | 4.4 | 15.0 | 15.4 | 16.6 | 11.4 | 14.7 | n/a |
59-
| 4 KiB | 32.1 | 23.3 | 24.0 | 18.9 | 38.1 | 30.2 | 33.6 | 29.7 | 55.3 | 38.7 |
60-
| 64 KiB | 955.9 | 1061.8 | 863.4 | 840.3 | 852.3 | 846.6 | 850.4 | 855.3 | 916.3 | 851.1 |
54+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
55+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
56+
| 64 B | 7.8 | 4.4 | 3.2 | 4.4 | 15.0 | 16.6 | 14.7 | n/a |
57+
| 4 KiB | 32.1 | 23.3 | 24.0 | 18.9 | 38.1 | 33.6 | 55.3 | 38.7 |
58+
| 64 KiB | 955.9 | 1061.8 | 863.4 | 840.3 | 852.3 | 850.4 | 916.3 | 851.1 |
6159

6260
_Values in ns._
6361

6462
### Mixed-size Pareto
6563

6664
512-allocation batch with sizes drawn from a Pareto(α=1.16, xm=24) distribution capped at 256 KiB. Time per batch, lower is better.
6765

68-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
69-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
70-
| Pareto(1.16, 24) | 30.5 | 11.0 | 10.8 | 11.4 | 24.9 | 25.2 | 25.3 | 25.6 | n/a | n/a |
66+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
67+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
68+
| Pareto(1.16, 24) | 30.5 | 11.0 | 10.8 | 11.4 | 24.9 | 25.3 | n/a | n/a |
7169

7270
_Values in us._
7371

7472
### Realloc growth
7573

7674
Geometric realloc ladder from 8 B up to 1 MiB. Time per full ladder, lower is better.
7775

78-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
79-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
80-
| 8 B → 1 MiB | 2851.4 | 3883.1 | 18015.4 | 15131.8 | 16677.8 | 16511.0 | 15854.8 | 15104.7 | 135.0 | n/a |
76+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
77+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
78+
| 8 B → 1 MiB | 2851.4 | 3883.1 | 18015.4 | 15131.8 | 16677.8 | 15854.8 | 135.0 | n/a |
8179

8280
_Values in ns._
8381

8482
### Arena bump + bulk reset
8583

8684
Allocate N small (32 B) objects, then release them all. Arena does this as one O(1) reset; every other backend has to free each pointer individually. Time per batch, lower is better.
8785

88-
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra-full | misra-fast | misra-correct-full | misra-correct-fast | misra-arena | misra-page |
89-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
90-
| 128 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | 0.8 | n/a |
91-
| 1024 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | 6.5 | n/a |
92-
| 8192 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | 52.4 | n/a |
86+
| benchmark | glibc | jemalloc | mimalloc | tcmalloc | misra | misra-correct | misra-arena | misra-page |
87+
|---|---:|---:|---:|---:|---:|---:|---:|---:|
88+
| 128 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | 0.8 | n/a |
89+
| 1024 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | 6.5 | n/a |
90+
| 8192 × 32 B | n/a | n/a | n/a | n/a | n/a | n/a | 52.4 | n/a |
9391

9492
_Values in us._
9593

9694
## Fragmentation
9795

9896
Each allocator's own introspection API reports committed bytes after the workload runs. Lower committed-bytes for the same live-bytes is better.
9997

100-
| benchmark | live MB | glibc MB | jemalloc MB | mimalloc MB | tcmalloc MB | misra-full MB | misra-fast MB | misra-correct-full MB | misra-correct-fast MB | misra-arena MB | misra-page MB |
101-
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
102-
| Checkerboard (4 K small) | 0.6 | 2.2 | 9.1 | n/a | 7.0 | 0.8 | 0.9 | 0.8 | 0.8 | n/a | n/a |
103-
| Checkerboard (16 K small) | 2.5 | 4.1 | 11.7 | n/a | 7.0 | 3.2 | 3.2 | 3.1 | 3.1 | n/a | n/a |
104-
| Checkerboard (64 K small) | 10.0 | 15.3 | 22.0 | n/a | 16.0 | 12.5 | 12.5 | 12.5 | 12.5 | n/a | n/a |
105-
| Lifetime mix | 4.0 | 5.0 | 20.1 | n/a | 16.0 | 4.6 | 4.6 | 4.7 | 4.6 | n/a | n/a |
106-
| Page overhang | 18.2 | 20.5 | 32.8 | n/a | 29.0 | 38.4 | 38.4 | 38.4 | 38.4 | n/a | n/a |
98+
| benchmark | live MB | glibc MB | jemalloc MB | mimalloc MB | tcmalloc MB | misra MB | misra-correct MB | misra-arena MB | misra-page MB |
99+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
100+
| Checkerboard (4 K small) | 0.6 | 2.2 | 9.1 | n/a | 7.0 | 0.8 | 0.8 | n/a | n/a |
101+
| Checkerboard (16 K small) | 2.5 | 4.1 | 11.7 | n/a | 7.0 | 3.2 | 3.1 | n/a | n/a |
102+
| Checkerboard (64 K small) | 10.0 | 15.3 | 22.0 | n/a | 16.0 | 12.5 | 12.5 | n/a | n/a |
103+
| Lifetime mix | 4.0 | 5.0 | 20.1 | n/a | 16.0 | 4.6 | 4.7 | n/a | n/a |
104+
| Page overhang | 18.2 | 20.5 | 32.8 | n/a | 29.0 | 38.4 | 38.4 | n/a | n/a |
107105

108106
## How to read
109107

110108
MisraStdC's typed-allocator family shows up as several columns:
111109

112110
- **`misra` / `misra-correct`**`HeapAllocator` everywhere vs the right tool per workload (`SlabAllocator(slot_size)` for fixed-size benches, `HeapAllocator` for mixed). The latter is what a real MisraStdC caller would do.
113-
- **`*-full` vs `*-fast`** — same source, different `heap_validate_full` setting. `-full` does per-dispatch cross-class checks plus volatile descriptor probes (project default; catches torn pointers / freed metadata). `-fast` is the magic-only check (catches uninitialised / post-deinit / type-confusion only). The gap between them is MisraStdC's per-call safety tax — turn it off in shipping release builds for tighter dispatch.
114111
- **`misra-arena` / `misra-page`** — specialised backends (bump+bulk-reset, mmap-per-alloc). `n/a` rows mean the workload doesn't fit the backend's contract (e.g. arena can't handle many-live-allocs; page wastes whole pages on sub-page requests).
115112

116-
Where tcmalloc beats `misra-correct-fast` on small AllocFreePair, the gap is MisraStdC's structural safety (double-free, misalignment, magic-tag dispatch) that production allocators skip even in their fast paths.
113+
Where tcmalloc beats `misra-correct` on small AllocFreePair, the gap is MisraStdC's structural safety (double-free, misalignment, magic-tag dispatch, cross-class invariants) that production allocators skip even in their fast paths.
117114

118115
## Reproduce
119116

120-
Single-tier run (validate-full only):
121-
122117
```sh
123118
meson setup build -Dbenchmark=true -Dbuildtype=release -Doptimization=3
124119
ninja -C build
125120
python3 Benchmark/Scripts/run.py build
126121
```
127122

128-
Two-tier run that shows the safety overhead side-by-side (matches the numbers above):
129-
130-
```sh
131-
meson setup build-full -Dbenchmark=true -Dbuildtype=release -Doptimization=3 \
132-
-Dalloc_debug=false -Dheap_validate_full=true
133-
meson setup build-fast -Dbenchmark=true -Dbuildtype=release -Doptimization=3 \
134-
-Dalloc_debug=false -Dheap_validate_full=false
135-
ninja -C build-full
136-
ninja -C build-fast
137-
python3 Benchmark/Scripts/run.py build-full --validate-fast build-fast
138-
```
139-
140123
Regenerates this file with measurements from the host.
141124

142125
## Environment
@@ -148,5 +131,5 @@ Regenerates this file with measurements from the host.
148131
| host CPU | Intel(R) Core(TM) Ultra 7 165U |
149132
| kernel | Linux 6.18.28 |
150133
| compiler | gcc 14.3.0 |
151-
| build | validate-full: buildtype=release optimization=3 b_lto=False b_sanitize=[] alloc_debug=True heap_validate_full=True | validate-fast: buildtype=release optimization=3 b_lto=False b_sanitize=[] alloc_debug=True heap_validate_full=False |
134+
| build | buildtype=release optimization=3 b_lto=False b_sanitize=[] alloc_debug=True |
152135
| reps | 10 per row (median reported) |

Benchmark/README.template.md

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,33 +57,18 @@ Each allocator's own introspection API reports committed bytes after the workloa
5757
MisraStdC's typed-allocator family shows up as several columns:
5858

5959
- **`misra` / `misra-correct`**`HeapAllocator` everywhere vs the right tool per workload (`SlabAllocator(slot_size)` for fixed-size benches, `HeapAllocator` for mixed). The latter is what a real MisraStdC caller would do.
60-
- **`*-full` vs `*-fast`** — same source, different `heap_validate_full` setting. `-full` does per-dispatch cross-class checks plus volatile descriptor probes (project default; catches torn pointers / freed metadata). `-fast` is the magic-only check (catches uninitialised / post-deinit / type-confusion only). The gap between them is MisraStdC's per-call safety tax — turn it off in shipping release builds for tighter dispatch.
6160
- **`misra-arena` / `misra-page`** — specialised backends (bump+bulk-reset, mmap-per-alloc). `n/a` rows mean the workload doesn't fit the backend's contract (e.g. arena can't handle many-live-allocs; page wastes whole pages on sub-page requests).
6261

63-
Where tcmalloc beats `misra-correct-fast` on small AllocFreePair, the gap is MisraStdC's structural safety (double-free, misalignment, magic-tag dispatch) that production allocators skip even in their fast paths.
62+
Where tcmalloc beats `misra-correct` on small AllocFreePair, the gap is MisraStdC's structural safety (double-free, misalignment, magic-tag dispatch, cross-class invariants) that production allocators skip even in their fast paths.
6463

6564
## Reproduce
6665

67-
Single-tier run (validate-full only):
68-
6966
```sh
7067
meson setup build -Dbenchmark=true -Dbuildtype=release -Doptimization=3
7168
ninja -C build
7269
python3 Benchmark/Scripts/run.py build
7370
```
7471

75-
Two-tier run that shows the safety overhead side-by-side (matches the numbers above):
76-
77-
```sh
78-
meson setup build-full -Dbenchmark=true -Dbuildtype=release -Doptimization=3 \
79-
-Dalloc_debug=false -Dheap_validate_full=true
80-
meson setup build-fast -Dbenchmark=true -Dbuildtype=release -Doptimization=3 \
81-
-Dalloc_debug=false -Dheap_validate_full=false
82-
ninja -C build-full
83-
ninja -C build-fast
84-
python3 Benchmark/Scripts/run.py build-full --validate-fast build-fast
85-
```
86-
8772
Regenerates this file with measurements from the host.
8873

8974
## Environment

0 commit comments

Comments
 (0)