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

481 lines
14 KiB
Markdown

# 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 <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実装例
```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 を考慮するとテストの自動化は緊急