# HAKMEM テスタビリティ & メンテナンス性分析レポート **分析日**: 2025-11-06 **プロジェクト**: HAKMEM Memory Allocator **コード規模**: 139ファイル, 32,175 LOC --- ## 1. テスト現状 ### テストコードの規模 | テスト | ファイル | 行数 | |--------|---------|------| | test_super_registry.c | SuperSlab registry | 59 | | test_ready_ring.c | Ready ring unit | 47 | | test_mailbox_box.c | Mailbox Box | 30 | | mailbox_test_stubs.c | テストスタブ | 16 | | **合計** | **4ファイル** | **152行** | ### 課題 - **テストが極小**: 152行のテストコードに対して 32,175 LOC - **カバレッジ推定**: < 5% (主要メモリアロケータ機能の大部分がテストされていない) - **統合テスト不足**: ユニットテストは 3つのモジュール(registry, ring, mailbox)のみ - **ホットパステスト欠落**: Box 5/6(High-frequency fast path)、Tiny allocator のテストなし --- ## 2. テスタビリティ阻害要因 ### 2.1 TLS変数の過度な使用 **TLS変数定義数**: 88行分を占有 **主なTLS変数** (`tiny_tls.h`, `tiny_alloc_fast.inc.h`): ```c extern __thread void* g_tls_sll_head[TINY_NUM_CLASSES]; // 物理レジスタ化困難 extern __thread uint32_t g_tls_sll_count[TINY_NUM_CLASSES]; extern __thread uint64_t g_tls_alloc_hits; // etc... ``` **テスタビリティへの影響**: - TLS状態は他スレッドから見えない → マルチスレッドテスト困難 - モック化不可能 → スタブ関数が必須 - デバッグ/検証用アクセス手段がない **改善案**: ```c // TLS wrapper 関数の提供 uint32_t* tls_get_sll_head(int class_idx); // DI可能に int tls_get_sll_count(int class_idx); ``` --- ### 2.2 グローバル変数の密集 **グローバル変数数**: 295個の extern 宣言 **主なグローバル変数** (hakmem.c, hakmem_tiny_superslab.c): ```c // hakmem.c static struct hkm_ace_controller g_ace_controller; static int g_initialized = 0; static int g_strict_free = 0; static _Atomic int g_cached_strategy_id = 0; // ... 40+以上のグローバル変数 // hakmem_tiny_superslab.c uint64_t g_superslabs_allocated = 0; static pthread_mutex_t g_superslab_lock = PTHREAD_MUTEX_INITIALIZER; uint64_t g_ss_alloc_by_class[8] = {0}; // ... ``` **テスタビリティへの影響**: - グローバル状態が初期化タイミングに依存 → テスト実行順序に敏感 - 各テスト間でのstate cleanup が困難 - 並行テスト不可 (mutex/atomic の競合) **改善案**: ```c // Context 構造体の導入 typedef struct { struct hkm_ace_controller ace; uint64_t superslabs_allocated; // ... } HakMemContext; HakMemContext* hak_context_create(void); void hak_context_destroy(HakMemContext*); ``` --- ### 2.3 Static関数の過度な使用 **Static関数数**: 175+個 **分布** (ファイル別): - hakmem_tiny.c: 56個 - hakmem_pool.c: 23個 - hakmem_l25_pool.c: 21個 - ... **テスタビリティへの影響**: - 関数単体テストが不可能 (visibility < file-level) - リファクタリング時に関数シグネチャ変更が局所的だが、一度変更すると cascade effect - ホワイトボックステストの実施困難 **改善案**: ```c // Test 専用の internal header #ifdef HAKMEM_TEST_EXPORT #define TEST_STATIC // empty #else #define TEST_STATIC static #endif TEST_STATIC void slab_refill(int class_idx); // Test可能に ``` --- ### 2.4 複雑な依存関係構造 **ファイル間の依存関係** (最多変更ファイル): ``` hakmem_tiny.c (33 commits) ├─ hakmem_tiny_superslab.h ├─ tiny_alloc_fast.inc.h ├─ tiny_free_fast.inc.h ├─ tiny_refill.h └─ hakmem_tiny_stats.h ├─ hakmem_tiny_batch_refill.h └─ ... ``` **Include depth**: - 最大深さ: 6~8レベル (`hakmem.c` → 32個のヘッダ) - .inc ファイルの重複include リスク (pragma once の必須化) **テスタビリティへの影響**: - 1つのモジュール単体テストに全体の 20+ファイルが必要 - ビルド依存関係が複雑化 → incremental build slow --- ### 2.5 .inc/.inc.h ファイルの設計の曖昧さ **ファイルタイプ分布**: - .inc ファイル: 13個 (malloc/free/init など) - .inc.h ファイル: 15個 (header-only など) - 境界が不明確 (inline vs include) **例**: ``` tiny_alloc_fast.inc.h (451 LOC) → inline funcs + extern externs tiny_free_fast.inc.h (307 LOC) → inline funcs + macro hooks tiny_atomic.h (20 statics) → atomic abstractions ``` **テスタビリティへの影響**: - .inc ファイルはヘッダのように treated → include dependency が深い - 変更時の再ビルド cascade (古いビルドシステムでは依存関係検出漏れ可能) - CLAUDE.md の記事で実際に発生: "ビルド依存関係に .inc ファイルが含まれていなかった" --- ## 3. テスタビリティスコア | ファイル | 規模 | スコア | 主阻害要因 | 改善度 | |---------|------|--------|-----------|-------| | hakmem_tiny.c | 1765 LOC | 2/5 | TLS多用(88行), static 56個, グローバル 40+ | HIGH | | hakmem.c | 1745 LOC | 2/5 | グローバル 40+, ACE 複雑度, LD_PRELOAD logic | HIGH | | hakmem_pool.c | 2592 LOC | 2/5 | static 23, TLS, mutex competition | HIGH | | hakmem_tiny_superslab.c | 821 LOC | 2/5 | pthread_mutex, static cache 6個 | HIGH | | tiny_alloc_fast.inc.h | 451 LOC | 3/5 | extern externs 多, macro-heavy, inline | MED | | tiny_free_fast.inc.h | 307 LOC | 3/5 | ownership check logic, cross-thread complexity | MED | | hakmem_tiny_refill.inc.h | 420 LOC | 2/5 | superslab refill state, O(n) scan | HIGH | | tiny_fastcache.c | 302 LOC | 3/5 | TLS-based, simple interface | MED | | test_super_registry.c | 59 LOC | 4/5 | よく設計, posix_memalign利用 | LOW | | test_mailbox_box.c | 30 LOC | 4/5 | minimal stubs, clear | LOW | --- ## 4. メンテナンス性の問題 ### 4.1 高頻度変更ファイル **最近30日の変更数** (git log): ``` 33 commits: core/hakmem_tiny.c 19 commits: core/hakmem.c 11 commits: core/hakmem_tiny_superslab.h 8 commits: core/hakmem_tiny_superslab.c 7 commits: core/tiny_fastcache.c 7 commits: core/hakmem_tiny_magazine.c ``` **影響度**: - 高頻度 = 実験的段階 or バグフィックスが多い - hakmem_tiny.c の 33 commits は約 2週間で完了 (激しい開発) - リグレッション risk が高い ### 4.2 コメント密度(ポジティブな指標) ``` hakmem_tiny.c: 1765 LOC, comments: 437 (~24%) ✓ 良好 hakmem.c: 1745 LOC, comments: 372 (~21%) ✓ 良好 hakmem_pool.c: 2592 LOC, comments: 555 (~21%) ✓ 良好 ``` **評価**: コメント密度は十分。問題は comments の **構造化の欠落** (inline comments が多く、unit-level docs が少ない) ### 4.3 命名規則の一貫性 **命名ルール** (一貫して実装): - Private functions: `static` + `func_name` - TLS variables: `g_tls_*` - Global counters: `g_*` - Atomic: `_Atomic` - Box terminology: 統一的に "Box 1", "Box 5", "Box 6" 使用 **評価**: 命名規則は一貫している。問題は **関数の役割が macro 層で隠蔽** されること --- ## 5. リファクタリング時のリスク評価 ### HIGH リスク (テスト困難 + 複雑) ``` hakmem_tiny.c hakmem.c hakmem_pool.c hakmem_tiny_superslab.c hakmem_tiny_refill.inc.h tiny_alloc_fast.inc.h tiny_free_fast.inc.h ``` **理由**: - TLS/グローバル状態が深く結合 - マルチスレッド競合の可能性 - ホットパス (microsecond-sensitive) である ### MED リスク (テスト可能性は MED だが変更多い) ``` hakmem_tiny_magazine.c hakmem_tiny_stats.c tiny_fastcache.c hakmem_mid_mt.c ``` ### LOW リスク (テスト充実 or 機能安定) ``` hakmem_super_registry.c (test_super_registry.c あり) test_*.c (テストコード自体) hakmem_tiny_simple.c (stable) hakmem_config.c (mostly data) ``` --- ## 6. テスト戦略提案 ### 6.1 Phase 1: Testability Refactoring (1週間) **目標**: TLS/グローバル状態を DI 可能に **実装**: ```c // 1. Context 構造体の導入 typedef struct { // Tiny allocator state void* tls_sll_head[TINY_NUM_CLASSES]; uint32_t tls_sll_count[TINY_NUM_CLASSES]; SuperSlab* superslabs[256]; uint64_t superslabs_allocated; // ... } HakMemTestCtx; // 2. Test-friendly API HakMemTestCtx* hak_test_ctx_create(void); void hak_test_ctx_destroy(HakMemTestCtx*); // 3. 既存の global 関数を wrapper に void* hak_tiny_alloc_test(HakMemTestCtx* ctx, size_t size); void hak_tiny_free_test(HakMemTestCtx* ctx, void* ptr); ``` **Expected benefit**: - TLS/global state が testable に - 並行テスト可能 - State reset が明示的に ### 6.2 Phase 2: Unit Test Foundation (1週間) **4つの test suite 構築**: ``` tests/unit/ ├── test_tiny_alloc.c (fast path, slow path, refill) ├── test_tiny_free.c (ownership check, remote free) ├── test_superslab.c (allocation, lookup, eviction) ├── test_hot_path.c (Box 5/6: <1us measurements) ├── test_concurrent.c (pthread multi-alloc/free) └── fixtures/ └── test_context.h (ctx_create, ctx_destroy) ``` **各テストの対象**: - test_tiny_alloc.c: 200+ cases (object sizes, refill scenarios) - test_tiny_free.c: 150+ cases (same/cross-thread, remote) - test_superslab.c: 100+ cases (registry lookup, cache) - test_hot_path.c: 50+ perf regression cases - test_concurrent.c: 30+ race conditions ### 6.3 Phase 3: Integration Tests (1周) ```c tests/integration/ ├── test_alloc_free_cycle.c (malloc → free → reuse) ├── test_fragmentation.c (random pattern, external fragmentation) ├── test_mixed_workload.c (interleaved alloc/free, size pattern learning) └── test_ld_preload.c (LD_PRELOAD mode, libc interposition) ``` ### 6.4 Phase 4: Regression Detection (continuous) ```bash # Larson benchmark を CI に統合 ./larson_hakmem 2 8 128 1024 1 4 # Expected: 4.0M - 5.0M ops/s (baseline: 4.19M) # Regression threshold: -10% (3.77M ops/s) ``` --- ## 7. Mock/Stub 必要箇所 | 機能 | Mock需要度 | 実装手段 | |------|----------|--------| | SuperSlab allocation (mmap) | HIGH | calloc stub + virtual addresses | | pthread_mutex (refill sync) | HIGH | spinlock mock or lock-free variant | | TLS access | HIGH | context-based DI | | Slab lookup (registry) | MED | in-memory hash table mock | | RDTSC profiling | LOW | skip in tests or mock clock | | LD_PRELOAD detection | MED | getenv mock | ### Mock実装例 ```c // test_context.h typedef struct { // Mock allocator void* (*malloc_mock)(size_t); void (*free_mock)(void*); // Mock TLS HakMemTestTLS tls; // Mock locks spinlock_t refill_lock; // Stats uint64_t alloc_count, free_count; } HakMemMockCtx; HakMemMockCtx* hak_mock_ctx_create(void); ``` --- ## 8. リファクタリングロードマップ ### Priority: 高 (ボトルネック解消) 1. **TLS Abstraction Layer** (3日) - `tls_*()` wrapper 関数化 - テスト用 TLS accessor 追加 2. **Global State Consolidation** (3日) - `HakMemGlobalState` 構造体作成 - グローバル変数を1つの struct に統合 - Lazy initialization を explicit に 3. **Dependency Injection Layer** (5日) - `hak_alloc(ctx, size)` API 作成 - 既存グローバル関数は wrapper に ### Priority: 中 (改善) 4. **Static Function Export** (2日) - Test-critical な static を internal header で expose - `#ifdef HAKMEM_TEST` guard で risk最小化 5. **Mutex の Lock-Free 化検討** (1週間) - superslab_refill の mutex contention を削除 - atomic CAS-loop or seqlock で replace 6. **Include Depth の削減** (3日) - .inc ファイルの reorganize - circular dependency check を CI に追加 ### Priority: 低 (保守) 7. **Documentation** (1週間) - Architecture guide (Box Theory とおり) - Dataflow diagram (tiny alloc flow) - Test coverage map --- ## 9. 改善効果の予測 ### テスタビリティ改善 | スコア項目 | 現状 | 改善後 | 効果 | |----------|------|--------|------| | テストカバレッジ | 5% | 60% | HIGH | | ユニットテスト可能性 | 2/5 | 4/5 | HIGH | | 並行テスト可能 | NO | YES | HIGH | | デバッグ時間 | 2-3時間/bug | 30分/bug | 4-6x speedup | | リグレッション検出 | MANUAL | AUTOMATED | HIGH | ### コード品質改善 | 項目 | 効果 | |------|------| | リファクタリング risk | 8/10 → 3/10 | | 新機能追加の安全性 | LOW → HIGH | | マルチスレッドバグ検出 | HARD → AUTOMATED | | 性能 regression 検出 | MANUAL → AUTOMATED | --- ## 10. まとめ ### 現状の評価 **テスタビリティ**: 2/5 - TLS/グローバル状態が未テスト - ホットパス (Box 5/6) の単体テストなし - 統合テスト極小 (152 LOC のみ) **メンテナンス性**: 2.5/5 - 高頻度変更 (hakmem_tiny.c: 33 commits) - コメント密度は良好 (21-24%) - 命名規則は一貫 - 但し、関数の役割が macro で隠蔽される **リスク**: HIGH - リファクタリング時のリグレッション risk - マルチスレッドバグの検出困難 - グローバル状態に依存した初期化 ### 推奨アクション **短期 (1-2週間)**: 1. TLS abstraction layer 作成 (tls_*() wrapper) 2. Unit test foundation 構築 (context-based DI) 3. Tiny allocator ホットパステスト追加 **中期 (1ヶ月)**: 4. グローバル状態の struct 統合 5. Integration test suite 完成 6. CI/CD に regression 検出追加 **長期 (2-3ヶ月)**: 7. Static function export (for testing) 8. Mutex の Lock-Free 化検討 9. Architecture documentation 完成 ### 結論 現在のコードはパフォーマンス最適化 (Phase 6-1.7 Box Theory) に成功している一方、テスタビリティは後回しにされている。TLS/グローバル状態を DI 可能に refactor することで、テストカバレッジを 5% → 60% に向上させ、リグレッション risk を大幅に削減できる。 **優先度**: HIGH - 高頻度変更 (hakmem_tiny.c の 33 commits) による regression risk を考慮すると、テストの自動化は緊急。