Design: Cache recently-used SuperSlab references in TLS to accelerate
ptr→SuperSlab resolution in Headerless mode free() path.
## Implementation
### New Box: core/box/tls_ss_hint_box.h
- Header-only Box (4-slot FIFO cache per thread)
- Functions: tls_ss_hint_init(), tls_ss_hint_update(), tls_ss_hint_lookup(), tls_ss_hint_clear()
- Memory overhead: 112 bytes per thread (negligible)
- Statistics API for debug builds (hit/miss counters)
### Integration Points
1. **Free path** (core/hakmem_tiny_free.inc):
- Lines 477-481: Fast path hint lookup before hak_super_lookup()
- Lines 550-555: Second lookup location (fallback path)
- Expected savings: 10-50 cycles → 2-5 cycles on cache hit
2. **Allocation path** (core/tiny_superslab_alloc.inc.h):
- Lines 115-122: Linear allocation return path
- Lines 179-186: Freelist allocation return path
- Cache update on successful allocation
3. **TLS variable** (core/hakmem_tiny_tls_state_box.inc):
- `__thread TlsSsHintCache g_tls_ss_hint = {0};`
### Build System
- **Build flag** (core/hakmem_build_flags.h):
- HAKMEM_TINY_SS_TLS_HINT (default: 0, disabled)
- Validation: requires HAKMEM_TINY_HEADERLESS=1
- **Makefile**:
- Removed old ss_tls_hint_box.o (conflicting implementation)
- Header-only design eliminates compiled object files
### Testing
- **Unit tests** (tests/test_tls_ss_hint.c):
- 6 test functions covering init, lookup, FIFO rotation, duplicates, clear, stats
- All tests PASSING
- **Build validation**:
- ✅ Compiles with hint disabled (default)
- ✅ Compiles with hint enabled (HAKMEM_TINY_SS_TLS_HINT=1)
### Documentation
- **Benchmark report** (docs/PHASE1_TLS_HINT_BENCHMARK.md):
- Implementation summary
- Build validation results
- Benchmark methodology (to be executed)
- Performance analysis framework
## Expected Performance
- **Hit rate**: 85-95% (single-threaded), 70-85% (multi-threaded)
- **Cycle savings**: 80-95% on cache hit (10-50 cycles → 2-5 cycles)
- **Target improvement**: 15-20% throughput increase vs Headerless baseline
- **Memory overhead**: 112 bytes per thread
## Box Theory
**Mission**: Cache hot SuperSlabs to avoid global registry lookup
**Boundary**: ptr → SuperSlab* or NULL (miss)
**Invariant**: hint.base ≤ ptr < hint.end → hit is valid
**Fallback**: Always safe to miss (triggers hak_super_lookup)
**Thread Safety**: TLS storage, no synchronization required
**Risk**: Low (read-only cache, fail-safe fallback, magic validation)
## Next Steps
1. Run full benchmark suite (sh8bench, cfrac, larson)
2. Measure actual hit rate with stats enabled
3. If performance target met (15-20% improvement), enable by default
4. Consider increasing cache slots if hit rate < 80%
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
251 lines
6.6 KiB
C
251 lines
6.6 KiB
C
// test_tls_ss_hint.c - Unit tests for TLS SuperSlab Hint Box
|
|
//
|
|
// Purpose: Validate TLS hint cache behavior (init, update, lookup, FIFO rotation)
|
|
// Build: gcc -o test_tls_ss_hint test_tls_ss_hint.c -I../core -DHAKMEM_TINY_SS_TLS_HINT=1
|
|
// Run: ./test_tls_ss_hint
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
// Define build flags for test compilation
|
|
#ifndef HAKMEM_BUILD_RELEASE
|
|
#define HAKMEM_BUILD_RELEASE 0
|
|
#endif
|
|
|
|
#ifndef HAKMEM_TINY_SS_TLS_HINT
|
|
#define HAKMEM_TINY_SS_TLS_HINT 1
|
|
#endif
|
|
|
|
// Include the hint box header
|
|
#include "box/tls_ss_hint_box.h"
|
|
|
|
// Mock SuperSlab for testing
|
|
#define SUPERSLAB_MAGIC 0x5353504C // 'SSPL'
|
|
|
|
typedef struct SuperSlab {
|
|
uint32_t magic;
|
|
uint8_t lg_size;
|
|
uint8_t _pad[3];
|
|
} SuperSlab;
|
|
|
|
// Define the TLS variable (normally in hakmem_tiny_tls_state_box.inc)
|
|
__thread TlsSsHintCache g_tls_ss_hint = {0};
|
|
|
|
// ============================================================================
|
|
// Test Functions
|
|
// ============================================================================
|
|
|
|
void test_hint_init(void) {
|
|
printf("test_hint_init...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Verify cache is empty
|
|
assert(g_tls_ss_hint.count == 0);
|
|
assert(g_tls_ss_hint.next_slot == 0);
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
assert(g_tls_ss_hint.hits == 0);
|
|
assert(g_tls_ss_hint.misses == 0);
|
|
#endif
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
|
|
void test_hint_basic(void) {
|
|
printf("test_hint_basic...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Mock SuperSlab
|
|
SuperSlab ss = {
|
|
.magic = SUPERSLAB_MAGIC,
|
|
.lg_size = 21, // 2MB
|
|
};
|
|
void* ss_base = (void*)0x1000000;
|
|
size_t ss_size = 2 * 1024 * 1024; // 2MB
|
|
|
|
// Update hint
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
|
|
// Verify cache entry
|
|
assert(g_tls_ss_hint.count == 1);
|
|
assert(g_tls_ss_hint.entries[0].base == ss_base);
|
|
assert(g_tls_ss_hint.entries[0].ss == &ss);
|
|
|
|
// Lookup should hit (within range)
|
|
SuperSlab* out = NULL;
|
|
assert(tls_ss_hint_lookup((void*)0x1000100, &out) == true);
|
|
assert(out == &ss);
|
|
|
|
// Lookup at base should hit
|
|
assert(tls_ss_hint_lookup((void*)0x1000000, &out) == true);
|
|
assert(out == &ss);
|
|
|
|
// Lookup at end-1 should hit
|
|
assert(tls_ss_hint_lookup((void*)0x11FFFFF, &out) == true);
|
|
assert(out == &ss);
|
|
|
|
// Lookup at end should miss (exclusive boundary)
|
|
assert(tls_ss_hint_lookup((void*)0x1200000, &out) == false);
|
|
|
|
// Lookup outside range should miss
|
|
assert(tls_ss_hint_lookup((void*)0x3000000, &out) == false);
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
|
|
void test_hint_fifo_rotation(void) {
|
|
printf("test_hint_fifo_rotation...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Create 6 mock SuperSlabs (cache has 4 slots)
|
|
SuperSlab ss[6];
|
|
for (int i = 0; i < 6; i++) {
|
|
ss[i].magic = SUPERSLAB_MAGIC;
|
|
ss[i].lg_size = 21; // 2MB
|
|
void* base = (void*)(uintptr_t)(0x1000000 + i * 0x200000); // 2MB apart
|
|
size_t size = 2 * 1024 * 1024;
|
|
|
|
tls_ss_hint_update(&ss[i], base, size);
|
|
}
|
|
|
|
// Cache should be full (4 slots)
|
|
assert(g_tls_ss_hint.count == TLS_SS_HINT_SLOTS);
|
|
|
|
// First 2 SuperSlabs should be evicted (FIFO)
|
|
SuperSlab* out = NULL;
|
|
assert(tls_ss_hint_lookup((void*)0x1000100, &out) == false); // ss[0] evicted
|
|
assert(tls_ss_hint_lookup((void*)0x1200100, &out) == false); // ss[1] evicted
|
|
|
|
// Last 4 SuperSlabs should be cached
|
|
assert(tls_ss_hint_lookup((void*)0x1400100, &out) == true); // ss[2]
|
|
assert(out == &ss[2]);
|
|
assert(tls_ss_hint_lookup((void*)0x1600100, &out) == true); // ss[3]
|
|
assert(out == &ss[3]);
|
|
assert(tls_ss_hint_lookup((void*)0x1800100, &out) == true); // ss[4]
|
|
assert(out == &ss[4]);
|
|
assert(tls_ss_hint_lookup((void*)0x1A00100, &out) == true); // ss[5]
|
|
assert(out == &ss[5]);
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
|
|
void test_hint_duplicate_detection(void) {
|
|
printf("test_hint_duplicate_detection...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Mock SuperSlab
|
|
SuperSlab ss = {
|
|
.magic = SUPERSLAB_MAGIC,
|
|
.lg_size = 21, // 2MB
|
|
};
|
|
void* ss_base = (void*)0x1000000;
|
|
size_t ss_size = 2 * 1024 * 1024;
|
|
|
|
// Update hint 3 times with same SuperSlab
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
|
|
// Cache should have only 1 entry (duplicates ignored)
|
|
assert(g_tls_ss_hint.count == 1);
|
|
assert(g_tls_ss_hint.entries[0].ss == &ss);
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
|
|
void test_hint_clear(void) {
|
|
printf("test_hint_clear...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Add some entries
|
|
SuperSlab ss = {
|
|
.magic = SUPERSLAB_MAGIC,
|
|
.lg_size = 21, // 2MB
|
|
};
|
|
void* ss_base = (void*)0x1000000;
|
|
size_t ss_size = 2 * 1024 * 1024;
|
|
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
|
|
assert(g_tls_ss_hint.count == 1);
|
|
|
|
// Clear cache
|
|
tls_ss_hint_clear();
|
|
|
|
// Cache should be empty
|
|
assert(g_tls_ss_hint.count == 0);
|
|
assert(g_tls_ss_hint.next_slot == 0);
|
|
|
|
// Lookup should miss
|
|
SuperSlab* out = NULL;
|
|
assert(tls_ss_hint_lookup((void*)0x1000100, &out) == false);
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
void test_hint_stats(void) {
|
|
printf("test_hint_stats...\n");
|
|
|
|
tls_ss_hint_init();
|
|
|
|
// Add entry
|
|
SuperSlab ss = {
|
|
.magic = SUPERSLAB_MAGIC,
|
|
.lg_size = 21, // 2MB
|
|
};
|
|
void* ss_base = (void*)0x1000000;
|
|
size_t ss_size = 2 * 1024 * 1024;
|
|
|
|
tls_ss_hint_update(&ss, ss_base, ss_size);
|
|
|
|
// Perform lookups
|
|
SuperSlab* out = NULL;
|
|
tls_ss_hint_lookup((void*)0x1000100, &out); // Hit
|
|
tls_ss_hint_lookup((void*)0x1000200, &out); // Hit
|
|
tls_ss_hint_lookup((void*)0x3000000, &out); // Miss
|
|
|
|
// Check stats
|
|
uint64_t hits = 0, misses = 0;
|
|
tls_ss_hint_stats(&hits, &misses);
|
|
|
|
assert(hits == 2);
|
|
assert(misses == 1);
|
|
|
|
printf(" PASS\n");
|
|
}
|
|
#endif
|
|
|
|
// ============================================================================
|
|
// Main Test Runner
|
|
// ============================================================================
|
|
|
|
int main(void) {
|
|
printf("===========================================\n");
|
|
printf("TLS SuperSlab Hint Box - Unit Tests\n");
|
|
printf("===========================================\n\n");
|
|
|
|
test_hint_init();
|
|
test_hint_basic();
|
|
test_hint_fifo_rotation();
|
|
test_hint_duplicate_detection();
|
|
test_hint_clear();
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
test_hint_stats();
|
|
#endif
|
|
|
|
printf("\n===========================================\n");
|
|
printf("All tests PASSED!\n");
|
|
printf("===========================================\n");
|
|
|
|
return 0;
|
|
}
|