VRIG's custom allocator test suite and benchmarking framework.
Clone the template:
git clone https://github.com/CLUB-NAME/allocator-test-suite.git cd allocator-test-suiteSetup Dependencies (Local Build): This script will clone and build
mimallocandjemalloclocally. Nosudorequired../setup_allocators.sh
Run Tests:
# Baseline (Glibc) make run-tests # Mimalloc make run-tests ALLOCATOR=allocators/mimalloc/mimalloc_wrapper.c \ EXTRA_CFLAGS="-Iallocators/mimalloc/mimalloc_src/include" \ LDFLAGS+="$(pwd)/build_secure/libmimalloc-secure.a -lpthread -lrt"# Jemalloc make run-tests ALLOCATOR=allocators/jemalloc/jemalloc_wrapper.c \ EXTRA_CFLAGS="-Iallocators/jemalloc/jemalloc_src/include/jemalloc" \ LDFLAGS+="$(pwd)/allocators/jemalloc/jemalloc_src/lib/libjemalloc.a -lpthread -ldl"
allocator-test-suite/
├── include/
│ ├── allocator.h # Allocator interface (from spec)
│ ├── test_harness.h # Test framework utilities
│ └── benchmark.h # Benchmark infrastructure
├── src/
│ ├── tests/
│ │ ├── test_correctness.c # TC-BASIC, TC-SIZE, TC-ALIGN, TC-REALLOC, TC-CALLOC
│ │ ├── test_stress.c # TC-STRESS-001 through TC-STRESS-006
│ │ ├── test_edge.c # TC-EDGE-001 through TC-EDGE-010
│ │ └── test_fragmentation.c # TC-FRAG-001 through TC-FRAG-004
│ ├── benchmarks/
│ │ └── bench_synthetic.c # WL-SYN-001 through WL-SYN-010
│ └── harness/
│ ├── main_tests.c # Test runner entry point
│ └── main_bench.c # Benchmark runner entry point
├── allocators/
│ └── glibc/
│ └── glibc_allocator.c # Default glibc wrapper
├── Makefile
└── README.md
Your allocator must export an allocator_t structure. Here's the minimal template:
// myalloc.c#include"allocator.h"// Implement required functionsstaticvoid*myalloc_malloc(size_tsize) { /* ... */ }
staticvoidmyalloc_free(void*ptr) { /* ... */ }
staticvoid*myalloc_realloc(void*ptr, size_tsize) { /* ... */ }
staticvoid*myalloc_calloc(size_tnmemb, size_tsize) { /* ... */ }
staticintmyalloc_init(void) { return0; }
staticvoidmyalloc_teardown(void) { }
// Export allocator structureallocator_tmyalloc_allocator= {
.malloc=myalloc_malloc,
.free=myalloc_free,
.realloc=myalloc_realloc,
.calloc=myalloc_calloc,
// Optional (set to NULL if not implemented)
.memalign=NULL,
.aligned_alloc=NULL,
.usable_size=NULL,
.print_stats=NULL,
.validate_heap=NULL,
.get_stats=NULL,
// Lifecycle
.init=myalloc_init,
.teardown=myalloc_teardown,
// Metadata
.name="myalloc",
.author="Your Name",
.version="1.0.0",
.description="My custom allocator",
.memory_backend="mmap",
.features= {
.thread_safe= false,
.min_alignment=16,
.max_alignment=4096,
},
};
// Override the default allocator getterallocator_t*get_test_allocator(void) {
return&myalloc_allocator;
}
allocator_t*get_bench_allocator(void) {
return&myalloc_allocator;
}# Point to your allocator source
make ALLOCATOR=path/to/myalloc.c run-tests
# Run with debug mode (AddressSanitizer)
make debug ALLOCATOR=path/to/myalloc.c run-tests
# Run benchmarks
make MODE=bench ALLOCATOR=path/to/myalloc.c run-bench- TC-BASIC-001 to 005: Basic malloc/free operations
- TC-SIZE-001 to 005: Size handling (1B to 256MB)
- TC-ALIGN-001 to 003: Alignment verification
- TC-REALLOC-001 to 007: Realloc semantics
- TC-CALLOC-001 to 004: Calloc semantics
- TC-USABLE-001 to 002: usable_size (if implemented)
- TC-STRESS-001: Random malloc/free (1M ops)
- TC-STRESS-002: LIFO pattern (1M ops)
- TC-STRESS-003: FIFO pattern (1M ops)
- TC-STRESS-004: Realloc chains (100K ops)
- TC-STRESS-005: Peak memory cycling (100 cycles)
- TC-STRESS-006: 100K simultaneous allocations
- TC-EDGE-001 to 002: SIZE_MAX handling
- TC-EDGE-003: 100K × 1-byte allocations
- TC-EDGE-004: Page boundary allocations
- TC-EDGE-005: Rapid init/teardown cycles
- TC-EDGE-006 to 010: Various edge cases
- TC-FRAG-001: Swiss cheese pattern
- TC-FRAG-002: Sawtooth pattern
- TC-FRAG-003: Size class thrashing
- TC-FRAG-004: Long-running simulation
| ID | Description | Iterations |
|---|---|---|
| WL-SYN-001 | Small fixed 64B, immediate free | 10M |
| WL-SYN-002 | Small random 16-256B | 10M |
| WL-SYN-003 | Medium fixed 4KB | 1M |
| WL-SYN-004 | Medium random 1-64KB | 1M |
| WL-SYN-005 | Large fixed 1MB | 100K |
| WL-SYN-006 | Large random 64KB-4MB | 100K |
| WL-SYN-007 | Mixed power-law, batch free | 10M |
| WL-SYN-008 | Realloc grow chain 16B→4KB | 1M |
| WL-SYN-009 | Realloc shrink chain 4KB→16B | 1M |
| WL-SYN-010 | Calloc random 16-4KB | 1M |
# Run all workloads
./bin/run_bench --all
# Run quick subset
./bin/run_bench --quick
# Run specific workload
./bin/run_bench --workload=WL-SYN-001
# Multiple runs for statistical significance
./bin/run_bench --runs=10
# CSV output for analysis
./bin/run_bench --csv > results.csv| Variable | Description |
|---|---|
ALLOCATOR=path | Path to custom allocator source |
MODE=debug | -O0 -g3 with ASan/UBSan |
MODE=release | -O2 -DNDEBUG |
MODE=bench | -O3 -march=native -DNDEBUG |
ARGS=... | Pass arguments to runner |
# Test glibc baseline
make run-bench --csv > results_glibc.csv
# Test your allocator
make ALLOCATOR=../myalloc/myalloc.c run-bench --csv > results_myalloc.csv
# Compare
diff results_glibc.csv results_myalloc.csvRun with sanitizers:
make debug run-tests
Run specific test suite:
make run-tests ARGS='--correctness' make run-tests ARGS='--stress'
Use Valgrind:
make release valgrind --leak-check=full ./bin/run_tests
Profile with perf:
make MODE=bench bench perf record ./bin/run_bench --workload=WL-SYN-001 perf report
Create test function in appropriate
test_*.cfile:statictest_result_ttest_new_001(allocator_t*alloc) { // Your testTEST_ASSERT(condition, "message"); returnTEST_PASS; }
Add to test registration array:
test_case_tmy_tests[] = { {"TC-NEW-001", "description", test_new_001}, };