Skip to content

Commit 0366e91

Browse files
committed
bench: overhaul benchmark suite, auto-fetch vendors, and refresh charts
- Add strict data dependency chain (checksum sink) to prevent GCC 16 LTO dead-code elimination - Replace std::stack + malloc with foonathan::memory (memory_stack) - Add automatic vendor fetching (bench_deps) and building in Makefile - Re-render throughput charts with solid dark card backgrounds for light/dark theme safety - Update README benchmark tables and metrics with fresh 4-matrix test results
1 parent 02c6c87 commit 0366e91

14 files changed

Lines changed: 339 additions & 2080 deletions
37.6 KB
Loading
61.1 KB
Loading
68.5 KB
Loading

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,5 @@ benchmark
7171
benchmark_*
7272
wasi*
7373
perf.data
74-
bench/perf*
74+
bench/perf*
75+
bench/vendor

Makefile

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,45 @@ FUZZ_DEBUG_BINS = $(FUZZ_SRCS:%.c=%_debug)
7171
FUZZ_FLAGS = -fsanitize=fuzzer,address,undefined -O3 -g3 -fno-omit-frame-pointer
7272
FUZZ_DEBUG_FLAGS = -fsanitize=fuzzer,address,undefined -O0 -g3 -fno-omit-frame-pointer -DESTACK_FUZZ_DEBUG -DDEBUG
7373

74-
# --- Benchmark Configuration ---
74+
# --- Benchmark Configuration & Dynamic Vendor Discovery ---
7575
DEPTH ?= 15
7676
BENCH_DIR = bench
77-
BENCH_SRCS = $(BENCH_DIR)/benchmark.cpp $(BENCH_DIR)/Allocator.cpp $(BENCH_DIR)/StackAllocator.cpp
78-
BENCH_BIN = $(BENCH_DIR)/benchmark_$(DEPTH)
77+
BENCH_VENDOR_DIR = $(BENCH_DIR)/vendor
78+
79+
BENCH_VENDOR_INCLUDES :=
80+
BENCH_VENDOR_SRCS :=
81+
82+
ifneq ($(wildcard $(BENCH_VENDOR_DIR)/wb_alloc),)
83+
BENCH_VENDOR_INCLUDES += -I$(BENCH_VENDOR_DIR)/wb_alloc
84+
endif
85+
86+
ifneq ($(wildcard $(BENCH_VENDOR_DIR)/memory-allocators),)
87+
BENCH_VENDOR_INCLUDES += -I$(BENCH_VENDOR_DIR)/memory-allocators/includes \
88+
-I$(BENCH_VENDOR_DIR)/memory-allocators/src
89+
BENCH_VENDOR_SRCS += $(BENCH_VENDOR_DIR)/memory-allocators/src/Allocator.cpp \
90+
$(BENCH_VENDOR_DIR)/memory-allocators/src/StackAllocator.cpp
91+
endif
92+
93+
ifneq ($(wildcard $(BENCH_VENDOR_DIR)/foonathan_memory),)
94+
BENCH_VENDOR_INCLUDES += -I$(BENCH_VENDOR_DIR)/foonathan_memory/include \
95+
-I$(BENCH_VENDOR_DIR)/foonathan_memory/include/foonathan/memory \
96+
-I$(BENCH_VENDOR_DIR)/foonathan_memory/build/src \
97+
-I$(BENCH_VENDOR_DIR)/foonathan_memory/build/src/container_node_sizes \
98+
-I$(BENCH_VENDOR_DIR)/foonathan_memory/src
99+
BENCH_VENDOR_SRCS += $(wildcard $(BENCH_VENDOR_DIR)/foonathan_memory/src/*.cpp) \
100+
$(wildcard $(BENCH_VENDOR_DIR)/foonathan_memory/src/detail/*.cpp)
101+
endif
102+
103+
BENCH_SRCS = $(BENCH_DIR)/benchmark.cpp $(BENCH_VENDOR_SRCS)
104+
BENCH_BIN = $(BENCH_DIR)/benchmark_$(DEPTH)
105+
79106
CXX ?= g++
80-
CXXFLAGS = -O3 -std=c++17 -flto -DNDEBUG -Wno-stringop-overflow -DBENCH_DEPTH=$(DEPTH) -I. -I$(BENCH_DIR)
107+
CXXFLAGS = -O3 -std=c++17 -flto -DNDEBUG -Wno-stringop-overflow -DBENCH_DEPTH=$(DEPTH) -I. -I$(BENCH_DIR) $(BENCH_VENDOR_INCLUDES)
81108

82109
# Define the primary source file to check coverage for.
83110
COVERAGE_SRC = easy_stack.h
84111

85-
.PHONY: all clean run tests tests_full list coverage build_coverage bench
112+
.PHONY: all clean run tests tests_full list coverage build_coverage bench bench_deps clean_vendor
86113

87114
# Default goal: show available commands
88115
.DEFAULT_GOAL := list
@@ -280,12 +307,34 @@ clean_matrix:
280307
rm -rf $(MATRIX_DIR)
281308

282309

283-
# --- Benchmark Target ---
310+
# --- Benchmark Dependencies & Target ---
311+
.PHONY: bench_deps clean_vendor bench
312+
313+
bench_deps:
314+
@mkdir -p $(BENCH_VENDOR_DIR)
315+
@if [ ! -d "$(BENCH_VENDOR_DIR)/wb_alloc" ]; then \
316+
printf "Fetching wb_alloc...\n"; \
317+
git clone --depth 1 https://github.com/WilliamBundy/wb_alloc.git $(BENCH_VENDOR_DIR)/wb_alloc; \
318+
fi
319+
@if [ ! -d "$(BENCH_VENDOR_DIR)/memory-allocators" ]; then \
320+
printf "Fetching Trebi memory-allocators...\n"; \
321+
git clone --depth 1 https://github.com/mtrebi/memory-allocators.git $(BENCH_VENDOR_DIR)/memory-allocators; \
322+
fi
323+
@if [ ! -d "$(BENCH_VENDOR_DIR)/foonathan_memory" ]; then \
324+
printf "Fetching foonathan/memory...\n"; \
325+
git clone --depth 1 https://github.com/foonathan/memory.git $(BENCH_VENDOR_DIR)/foonathan_memory; \
326+
(cd $(BENCH_VENDOR_DIR)/foonathan_memory && cmake -B build -DCMAKE_BUILD_TYPE=Release -DFOONATHAN_MEMORY_BUILD_TESTS=OFF -DFOONATHAN_MEMORY_BUILD_TOOLS=OFF && cmake --build build); \
327+
fi
328+
329+
clean_vendor:
330+
rm -rf $(BENCH_VENDOR_DIR)
331+
284332
$(BENCH_BIN): $(BENCH_SRCS) easy_stack.h
285333
@printf "Compiling benchmark suite (depth $(DEPTH)): $@\n"
286334
@$(CXX) $(CXXFLAGS) $(BENCH_SRCS) -o $@
287335

288-
bench: $(BENCH_BIN)
336+
bench: bench_deps
337+
@$(MAKE) --no-print-directory $(BENCH_BIN)
289338
@printf "\n--- Running Stack Allocator Benchmarks (Depth: $(DEPTH)) ---\n"
290339
@./$(BENCH_BIN)
291340

@@ -361,6 +410,7 @@ list:
361410
@printf " make tests_full - run all tests with debug output\n"
362411
@printf " make test_matrix -j$(nproc) - run matrix of tests\n"
363412
@printf " make bench [DEPTH=15|30|100] - compile and run benchmarks with customizable stack depth (default: 15)\n"
413+
@printf " make clean_vendor - remove downloaded benchmark vendor libraries\n"
364414
@printf " make coverage - build & run tests to generate coverage data for CodeCov\n"
365415
@printf " make fuzz_[name] - run the 'core' fuzzer for 5 minutes (auto-detects fuzz_*.c)\n"
366416
@printf " make replay_[name] CRASH=... - replay a specific crash file with ASCII visualization\n"

README.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,10 @@ To evaluate execution speed, cache resilience, and scalability under different w
143143
### Tested Allocators:
144144
1. **EasyStack (Contract)**: Trusted mode with validations delegated to assertions (compiled out in release).
145145
2. **EasyStack (Defensive)**: Default safety mode with full runtime bounds and API sanitization active.
146-
3. **Trebi LIFO**: A highly-optimized C++ template-based LIFO stack allocator.
147-
4. **GNU Obstack**: The glibc standard stack allocator (highly optimized C system baseline).
148-
5. **wb_alloc (Bundy)**: A popular fixed-size C arena/stack allocator.
149-
6. **std::stack + malloc**: The default standard library heap-allocated baseline.
146+
3. **GNU Obstack**: The glibc standard stack allocator (highly optimized C system baseline).
147+
4. **foonathan::memory**: A popular, highly-optimized C++ memory stack allocator (`foonathan::memory::memory_stack`).
148+
5. **Trebi LIFO**: A lightweight C++ template-based LIFO stack allocator.
149+
6. **wb_alloc (Bundy)**: A popular fixed-size C arena/stack allocator.
150150

151151
---
152152

@@ -170,22 +170,22 @@ To prevent compile-time folding, dead-code elimination (DCE), or loop hoisting b
170170
#### RAW Results (Best of 25 runs, 2,000,000 iterations/run)
171171
| Allocator | Depth 15 (M ops/s) | Depth 30 (M ops/s) | Depth 100 (M ops/s) |
172172
| :--- | :---: | :---: | :---: |
173-
| **EasyStack (Contract)** | **576.40** | **516.74** | **493.63** |
174-
| **EasyStack (Defensive)** | **415.25** | **385.75** | **402.64** |
175-
| GNU Obstack | 396.85 | 379.33 | 311.31 |
176-
| Trebi LIFO (C++) | 365.55 | 358.51 | 360.20 |
177-
| std::stack + malloc | 209.89 | 214.63 | 188.13 |
178-
| wb_alloc (Bundy) | 208.18 | 204.88 | 229.38 |
173+
| **EasyStack (Contract)** | **553.95** | **520.37** | **497.31** |
174+
| **EasyStack (Defensive)** | 438.24 | 425.04 | 393.81 |
175+
| GNU Obstack | 492.32 | 472.35 | 354.83 |
176+
| foonathan::memory | 418.05 | 385.99 | 406.24 |
177+
| Trebi LIFO (C++) | 402.27 | 399.90 | 397.59 |
178+
| wb_alloc (Bundy) | 225.39 | 228.38 | 250.47 |
179179

180180
#### PURE Algorithmic Results (Harness Overhead Subtracted)
181181
| Allocator | Depth 15 (M ops/s) | Depth 30 (M ops/s) | Depth 100 (M ops/s) |
182182
| :--- | :---: | :---: | :---: |
183-
| **EasyStack (Contract)** | **2747.82** | **2408.14** | **4972.83** |
184-
| **EasyStack (Defensive)** | **1076.10** | **941.91** | **1566.77** |
185-
| GNU Obstack | 870.41 | 895.82 | 720.72 |
186-
| Trebi LIFO (C++) | 732.81 | 787.78 | 1050.94 |
187-
| std::stack + malloc | 294.69 | 318.55 | 286.47 |
188-
| wb_alloc (Bundy) | 291.33 | 297.53 | 394.49 |
183+
| **EasyStack (Contract)** | **3733.89** | **2159.87** | **4987.23** |
184+
| **EasyStack (Defensive)** | 1157.74 | 1231.18 | 1354.20 |
185+
| GNU Obstack | 1631.08 | 1734.35 | 982.89 |
186+
| foonathan::memory | 1026.76 | 952.16 | 1513.53 |
187+
| Trebi LIFO (C++) | 936.54 | 1041.50 | 1400.02 |
188+
| wb_alloc (Bundy) | 331.27 | 352.33 | 456.30 |
189189

190190
---
191191

@@ -199,22 +199,22 @@ To prevent compile-time folding, dead-code elimination (DCE), or loop hoisting b
199199
#### RAW Results (Best of 25 runs, 2,000,000 iterations/run)
200200
| Allocator | Depth 15 (M ops/s) | Depth 30 (M ops/s) | Depth 100 (M ops/s) |
201201
| :--- | :---: | :---: | :---: |
202-
| **EasyStack (Contract)** | **589.30** | **506.21** | **494.47** |
203-
| **EasyStack (Defensive)** | **431.45** | **393.71** | **369.90** |
204-
| Trebi LIFO (C++) | 364.19 | 360.50 | 354.11 |
205-
| wb_alloc (Bundy) | 70.66 | 69.06 | 67.70 |
206-
| GNU Obstack | 57.07 | 58.45 | 56.06 |
207-
| std::stack + malloc | 43.13 | 45.57 | 42.78 |
202+
| **EasyStack (Contract)** | **586.84** | **509.05** | **498.43** |
203+
| **EasyStack (Defensive)** | 441.73 | 395.46 | 427.61 |
204+
| foonathan::memory | 428.22 | 380.73 | 402.30 |
205+
| Trebi LIFO (C++) | 403.17 | 402.90 | 397.68 |
206+
| wb_alloc (Bundy) | 73.77 | 72.36 | 72.36 |
207+
| GNU Obstack | 61.38 | 58.90 | 58.93 |
208208

209209
#### PURE Algorithmic Results (Harness Overhead Subtracted)
210210
| Allocator | Depth 15 (M ops/s) | Depth 30 (M ops/s) | Depth 100 (M ops/s) |
211211
| :--- | :---: | :---: | :---: |
212-
| **EasyStack (Contract)** | **3834.49** | **2164.57** | **4460.48** |
213-
| **EasyStack (Defensive)** | **1215.93** | **1014.13** | **1141.30** |
214-
| Trebi LIFO (C++) | 763.55 | 793.38 | 974.88 |
215-
| wb_alloc (Bundy) | 78.64 | 77.12 | 77.09 |
216-
| GNU Obstack | 62.17 | 64.12 | 62.35 |
217-
| std::stack + malloc | 45.97 | 48.94 | 46.34 |
212+
| **EasyStack (Contract)** | **4679.83** | **2600.74** | **8062.39** |
213+
| **EasyStack (Defensive)** | 1354.20 | 1072.00 | 1872.09 |
214+
| foonathan::memory | 1183.62 | 1073.10 | 1467.77 |
215+
| Trebi LIFO (C++) | 1010.15 | 1128.54 | 1408.14 |
216+
| wb_alloc (Bundy) | 82.88 | 81.81 | 83.23 |
217+
| GNU Obstack | 67.56 | 65.01 | 65.95 |
218218

219219
---
220220

bench/Allocator.cpp

Lines changed: 0 additions & 2 deletions
This file was deleted.

bench/Allocator.h

Lines changed: 0 additions & 27 deletions
This file was deleted.

bench/StackAllocator.cpp

Lines changed: 0 additions & 89 deletions
This file was deleted.

bench/StackAllocator.h

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)