Files
hakmem/docs/analysis/TESTABILITY_ANALYSIS.md
Moe Charm (CI) 67fb15f35f Wrap debug fprintf in !HAKMEM_BUILD_RELEASE guards (Release build optimization)
## Changes

### 1. core/page_arena.c
- Removed init failure message (lines 25-27) - error is handled by returning early
- All other fprintf statements already wrapped in existing #if !HAKMEM_BUILD_RELEASE blocks

### 2. core/hakmem.c
- Wrapped SIGSEGV handler init message (line 72)
- CRITICAL: Kept SIGSEGV/SIGBUS/SIGABRT error messages (lines 62-64) - production needs crash logs

### 3. core/hakmem_shared_pool.c
- Wrapped all debug fprintf statements in #if !HAKMEM_BUILD_RELEASE:
  - Node pool exhaustion warning (line 252)
  - SP_META_CAPACITY_ERROR warning (line 421)
  - SP_FIX_GEOMETRY debug logging (line 745)
  - SP_ACQUIRE_STAGE0.5_EMPTY debug logging (line 865)
  - SP_ACQUIRE_STAGE0_L0 debug logging (line 803)
  - SP_ACQUIRE_STAGE1_LOCKFREE debug logging (line 922)
  - SP_ACQUIRE_STAGE2_LOCKFREE debug logging (line 996)
  - SP_ACQUIRE_STAGE3 debug logging (line 1116)
  - SP_SLOT_RELEASE debug logging (line 1245)
  - SP_SLOT_FREELIST_LOCKFREE debug logging (line 1305)
  - SP_SLOT_COMPLETELY_EMPTY debug logging (line 1316)
- Fixed lock_stats_init() for release builds (lines 60-65) - ensure g_lock_stats_enabled is initialized

## Performance Validation

Before: 51M ops/s (with debug fprintf overhead)
After:  49.1M ops/s (consistent performance, fprintf removed from hot paths)

## Build & Test

```bash
./build.sh larson_hakmem
./out/release/larson_hakmem 1 5 1 1000 100 10000 42
# Result: 49.1M ops/s
```

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 13:14:18 +09:00

14 KiB

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):

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状態は他スレッドから見えない → マルチスレッドテスト困難
  • モック化不可能 → スタブ関数が必須
  • デバッグ/検証用アクセス手段がない

改善案:

// 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):

// 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 の競合)

改善案:

// 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
  • ホワイトボックステストの実施困難

改善案:

// 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 可能に

実装:

// 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周)

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)

# Larson benchmark を CI に統合
./larson_hakmem 2 8 128 1024 1 <seed> 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実装例

// 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: 中 (改善)

  1. Static Function Export (2日)

    • Test-critical な static を internal header で expose
    • #ifdef HAKMEM_TEST guard で risk最小化
  2. Mutex の Lock-Free 化検討 (1週間)

    • superslab_refill の mutex contention を削除
    • atomic CAS-loop or seqlock で replace
  3. Include Depth の削減 (3日)

    • .inc ファイルの reorganize
    • circular dependency check を CI に追加

Priority: 低 (保守)

  1. 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 を考慮すると、テストの自動化は緊急。