# リリースビルド デバッグ処理 洗い出しレポート ## 🔥 **CRITICAL: 5-8倍の性能差の根本原因** **現状**: HAKMEM 9M ops/s vs System malloc 43M ops/s(**4.8倍遅い**) **診断結果**: リリースビルド(`-DHAKMEM_BUILD_RELEASE=1 -DNDEBUG`)でも**大量のデバッグ処理が実行されている** --- ## 💀 **重大な問題(ホットパス)** ### 1. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:24-29` - **デバッグログ(毎回実行)** ```c __attribute__((always_inline)) inline void* hak_alloc_at(size_t size, hak_callsite_t site) { static _Atomic uint64_t hak_alloc_call_count = 0; uint64_t call_num = atomic_fetch_add(&hak_alloc_call_count, 1); if (call_num > 14250 && call_num < 14280 && size <= 1024) { fprintf(stderr, "[HAK_ALLOC_AT] call=%lu size=%zu\n", call_num, size); fflush(stderr); } ``` - **問題**: リリースビルドでも**毎回**カウンタをインクリメント + 条件分岐実行 - **影響度**: ★★★★★(ホットパス - 全allocで実行) - **修正案**: ```c #if !HAKMEM_BUILD_RELEASE static _Atomic uint64_t hak_alloc_call_count = 0; uint64_t call_num = atomic_fetch_add(&hak_alloc_call_count, 1); if (call_num > 14250 && call_num < 14280 && size <= 1024) { fprintf(stderr, "[HAK_ALLOC_AT] call=%lu size=%zu\n", call_num, size); fflush(stderr); } #endif ``` - **コスト**: atomic_fetch_add(5-10サイクル) + 条件分岐(1-2サイクル) = **7-12サイクル/alloc** --- ### 2. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:39-56` - **Tiny Path デバッグログ(3箇所)** ```c if (__builtin_expect(size <= TINY_MAX_SIZE, 1)) { if (call_num > 14250 && call_num < 14280 && size <= 1024) { fprintf(stderr, "[HAK_ALLOC_AT] call=%lu entering tiny path\n", call_num); fflush(stderr); } #ifdef HAKMEM_TINY_PHASE6_BOX_REFACTOR if (call_num > 14250 && call_num < 14280 && size <= 1024) { fprintf(stderr, "[HAK_ALLOC_AT] call=%lu calling hak_tiny_alloc_fast_wrapper\n", call_num); fflush(stderr); } tiny_ptr = hak_tiny_alloc_fast_wrapper(size); if (call_num > 14250 && call_num < 14280 && size <= 1024) { fprintf(stderr, "[HAK_ALLOC_AT] call=%lu hak_tiny_alloc_fast_wrapper returned %p\n", call_num, tiny_ptr); fflush(stderr); } #endif ``` - **問題**: `call_num`変数がスコープ内に存在するため、**リリースビルドでも3つの条件分岐を評価** - **影響度**: ★★★★★(Tiny Path = 全allocの95%+) - **修正案**: 行24-29と同様に`#if !HAKMEM_BUILD_RELEASE`でガード - **コスト**: 3つの条件分岐 × (1-2サイクル) = **3-6サイクル/alloc** --- ### 3. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:76-79,83` - **Tiny Fallback ログ** ```c if (!tiny_ptr && size <= TINY_MAX_SIZE) { static int log_count = 0; if (log_count < 3) { fprintf(stderr, "[DEBUG] Phase 7: tiny_alloc(%zu) failed, trying Mid/ACE layers (no malloc fallback)\n", size); log_count++; } ``` - **問題**: `log_count`チェックがリリースビルドでも実行 - **影響度**: ★★★(Tiny失敗時のみ、頻度は低い) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード - **コスト**: 条件分岐(1-2サイクル) --- ### 4. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:147-165` - **33KB デバッグログ(3箇所)** ```c if (size >= 33000 && size <= 34000) { fprintf(stderr, "[ALLOC] 33KB: TINY_MAX_SIZE=%d, threshold=%zu, condition=%d\n", TINY_MAX_SIZE, threshold, (size > TINY_MAX_SIZE && size < threshold)); } if (size > TINY_MAX_SIZE && size < threshold) { if (size >= 33000 && size <= 34000) { fprintf(stderr, "[ALLOC] 33KB: Calling hkm_ace_alloc\n"); } // ... if (size >= 33000 && size <= 34000) { fprintf(stderr, "[ALLOC] 33KB: hkm_ace_alloc returned %p\n", l1); } ``` - **問題**: 33KB allocで毎回3つの条件分岐 + fprintf実行 - **影響度**: ★★★★(Mid-Large Path) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード - **コスト**: 3つの条件分岐 + fprintf(数千サイクル) --- ### 5. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:191-194,201-203` - **Gap/OOM ログ** ```c static _Atomic int gap_alloc_count = 0; int count = atomic_fetch_add(&gap_alloc_count, 1); #if HAKMEM_DEBUG_VERBOSE if (count < 3) fprintf(stderr, "[HAKMEM] INFO: mid-gap fallback size=%zu\n", size); #endif ``` ```c static _Atomic int oom_count = 0; int count = atomic_fetch_add(&oom_count, 1); if (count < 10) { fprintf(stderr, "[HAKMEM] OOM: Unexpected allocation path for size=%zu, returning NULL\n", size); fprintf(stderr, "[HAKMEM] (OOM count: %d) This should not happen!\n", count + 1); } ``` - **問題**: `atomic_fetch_add`と条件分岐がリリースビルドでも実行 - **影響度**: ★★★(Gap/OOM時のみ) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード全体を囲む - **コスト**: atomic_fetch_add(5-10サイクル) + 条件分岐(1-2サイクル) --- ### 6. `/mnt/workdisk/public_share/hakmem/core/box/hak_alloc_api.inc.h:216` - **Invalid Magic エラー** ```c if (hdr->magic != HAKMEM_MAGIC) { fprintf(stderr, "[hakmem] ERROR: Invalid magic in allocated header!\n"); return ptr; } ``` - **問題**: マジックチェック失敗時にfprintf実行(ホットパスではないが、本番で起きると致命的) - **影響度**: ★★(エラー時のみ) - **修正案**: ```c if (hdr->magic != HAKMEM_MAGIC) { #if !HAKMEM_BUILD_RELEASE fprintf(stderr, "[hakmem] ERROR: Invalid magic in allocated header!\n"); #endif return ptr; } ``` --- ### 7. `/mnt/workdisk/public_share/hakmem/core/box/hak_free_api.inc.h:77-87` - **Free Wrapper トレース** ```c static int free_trace_en = -1; static _Atomic int free_trace_count = 0; if (__builtin_expect(free_trace_en == -1, 0)) { const char* e = getenv("HAKMEM_FREE_WRAP_TRACE"); free_trace_en = (e && *e && *e != '0') ? 1 : 0; } if (free_trace_en) { int n = atomic_fetch_add(&free_trace_count, 1); if (n < 8) { fprintf(stderr, "[FREE_WRAP_ENTER] ptr=%p\n", ptr); } } ``` - **問題**: **毎回getenv()チェック + 条件分岐** (初回のみgetenv、以降はキャッシュだが分岐は毎回) - **影響度**: ★★★★★(ホットパス - 全freeで実行) - **修正案**: ```c #if !HAKMEM_BUILD_RELEASE static int free_trace_en = -1; static _Atomic int free_trace_count = 0; if (__builtin_expect(free_trace_en == -1, 0)) { const char* e = getenv("HAKMEM_FREE_WRAP_TRACE"); free_trace_en = (e && *e && *e != '0') ? 1 : 0; } if (free_trace_en) { int n = atomic_fetch_add(&free_trace_count, 1); if (n < 8) { fprintf(stderr, "[FREE_WRAP_ENTER] ptr=%p\n", ptr); } } #endif ``` - **コスト**: 条件分岐(1-2サイクル) × 2 = **2-4サイクル/free** --- ### 8. `/mnt/workdisk/public_share/hakmem/core/box/hak_free_api.inc.h:15-33` - **Free Route トレース** ```c static inline int hak_free_route_trace_on(void) { static int g_trace = -1; if (__builtin_expect(g_trace == -1, 0)) { const char* e = getenv("HAKMEM_FREE_ROUTE_TRACE"); g_trace = (e && *e && *e != '0') ? 1 : 0; } return g_trace; } // ... (hak_free_route_log calls this every free) ``` - **問題**: `hak_free_route_log()`が複数箇所で呼ばれ、**毎回条件分岐実行** - **影響度**: ★★★★★(ホットパス - 全freeで複数回実行) - **修正案**: ```c #if !HAKMEM_BUILD_RELEASE static inline int hak_free_route_trace_on(void) { /* ... */ } static inline void hak_free_route_log(const char* tag, void* p) { /* ... */ } #else #define hak_free_route_trace_on() 0 #define hak_free_route_log(tag, p) do { } while(0) #endif ``` - **コスト**: 条件分岐(1-2サイクル) × 5-10回/free = **5-20サイクル/free** --- ### 9. `/mnt/workdisk/public_share/hakmem/core/box/hak_free_api.inc.h:195,213-217` - **Invalid Magic ログ** ```c if (g_invalid_free_log) fprintf(stderr, "[hakmem] ERROR: Invalid magic 0x%X (expected 0x%X)\n", hdr->magic, HAKMEM_MAGIC); // ... if (g_invalid_free_mode) { static int leak_warn = 0; if (!leak_warn) { fprintf(stderr, "[hakmem] WARNING: Skipping free of invalid pointer %p (may leak memory)\n", ptr); leak_warn = 1; } ``` - **問題**: `g_invalid_free_log`チェック + fprintf実行 - **影響度**: ★★(エラー時のみ) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード --- ### 10. `/mnt/workdisk/public_share/hakmem/core/box/hak_free_api.inc.h:231` - **BigCache L25 getenv** ```c static int g_bc_l25_en_free = -1; if (g_bc_l25_en_free == -1) { const char* e = getenv("HAKMEM_BIGCACHE_L25"); g_bc_l25_en_free = (e && atoi(e) != 0) ? 1 : 0; } ``` - **問題**: **初回のみgetenv実行**(キャッシュされるが、条件分岐は毎回) - **影響度**: ★★★(Large Free Path) - **修正案**: 初期化時に一度だけ実行し、TLS変数にキャッシュ --- ### 11. `/mnt/workdisk/public_share/hakmem/core/box/hak_wrappers.inc.h:118,123` - **Malloc Wrapper ログ** ```c #ifdef HAKMEM_TINY_PHASE6_BOX_REFACTOR fprintf(stderr, "[MALLOC_WRAPPER] count=%lu calling hak_alloc_at\n", count); #endif void* ptr = hak_alloc_at(size, (hak_callsite_t)site); #ifdef HAKMEM_TINY_PHASE6_BOX_REFACTOR fprintf(stderr, "[MALLOC_WRAPPER] count=%lu hak_alloc_at returned %p\n", count, ptr); #endif ``` - **問題**: `HAKMEM_TINY_PHASE6_BOX_REFACTOR`はビルドフラグだが、**リリースビルドでも定義されている可能性** - **影響度**: ★★★★★(ホットパス - 全mallocで2回実行) - **修正案**: ```c #if !HAKMEM_BUILD_RELEASE && defined(HAKMEM_TINY_PHASE6_BOX_REFACTOR) fprintf(stderr, "[MALLOC_WRAPPER] count=%lu calling hak_alloc_at\n", count); #endif ``` --- ## 🔧 **中程度の問題(ウォームパス)** ### 12. `/mnt/workdisk/public_share/hakmem/core/tiny_alloc_fast.inc.h:106,130-136` - **getenv チェック(初回のみ)** ```c static inline int tiny_profile_enabled(void) { if (__builtin_expect(g_tiny_profile_enabled == -1, 0)) { const char* env = getenv("HAKMEM_TINY_PROFILE"); g_tiny_profile_enabled = (env && *env && *env != '0') ? 1 : 0; } return g_tiny_profile_enabled; } ``` - **問題**: 初回のみgetenv実行、以降はキャッシュ(**条件分岐は毎回**) - **影響度**: ★★★(Refill時のみ) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード全体を囲む --- ### 13. `/mnt/workdisk/public_share/hakmem/core/tiny_alloc_fast.inc.h:139-156` - **Profiling Print(destructor)** ```c static void tiny_fast_print_profile(void) __attribute__((destructor)); static void tiny_fast_print_profile(void) { if (!tiny_profile_enabled()) return; if (g_tiny_alloc_hits == 0 && g_tiny_refill_calls == 0) return; fprintf(stderr, "\n========== Box Theory Fast Path Profile ==========\n"); // ... } ``` - **問題**: リリースビルドでも**プログラム終了時にfprintf実行** - **影響度**: ★★(終了時のみ) - **修正案**: `#if !HAKMEM_BUILD_RELEASE`でガード --- ### 14. `/mnt/workdisk/public_share/hakmem/core/tiny_alloc_fast.inc.h:192-204` - **Debug Counters(Integrity Check)** ```c #if !HAKMEM_BUILD_RELEASE atomic_fetch_add(&g_integrity_check_class_bounds, 1); static _Atomic uint64_t g_fast_pop_count = 0; uint64_t pop_call = atomic_fetch_add(&g_fast_pop_count, 1); if (0 && class_idx == 2 && pop_call > 5840 && pop_call < 5900) { fprintf(stderr, "[FAST_POP_C2] call=%lu cls=%d head=%p count=%u\n", pop_call, class_idx, g_tls_sll_head[class_idx], g_tls_sll_count[class_idx]); fflush(stderr); } #endif ``` - **問題**: **すでにガード済み** ✅ - **影響度**: なし(リリースビルドではスキップ) --- ### 15. `/mnt/workdisk/public_share/hakmem/core/tiny_alloc_fast.inc.h:311-320` - **getenv(Cascade Percentage)** ```c static inline int sfc_cascade_pct(void) { static int pct = -1; if (__builtin_expect(pct == -1, 0)) { const char* e = getenv("HAKMEM_SFC_CASCADE_PCT"); int v = e && *e ? atoi(e) : 50; if (v < 0) v = 0; if (v > 100) v = 100; pct = v; } return pct; } ``` - **問題**: 初回のみgetenv実行、以降はキャッシュ(**条件分岐は毎回**) - **影響度**: ★★(SFC Refill時のみ) - **修正案**: 初期化時に一度だけ実行 --- ### 16. `/mnt/workdisk/public_share/hakmem/core/tiny_free_fast.inc.h:106-112` - **SFC Debug ログ** ```c static __thread int free_ss_debug_count = 0; if (getenv("HAKMEM_SFC_DEBUG") && free_ss_debug_count < 20) { free_ss_debug_count++; // ... fprintf(stderr, "[FREE_SS] base=%p, cls=%d, same_thread=%d, sfc_enabled=%d\n", base, ss->size_class, is_same, g_sfc_enabled); } ``` - **問題**: **毎回getenv()実行** (キャッシュなし!) - **影響度**: ★★★★(SuperSlab Free Path) - **修正案**: ```c #if !HAKMEM_BUILD_RELEASE static __thread int free_ss_debug_count = 0; static int sfc_debug_en = -1; if (sfc_debug_en == -1) { sfc_debug_en = getenv("HAKMEM_SFC_DEBUG") ? 1 : 0; } if (sfc_debug_en && free_ss_debug_count < 20) { // ... } #endif ``` - **コスト**: **getenv(数百サイクル)毎回実行** ← **CRITICAL!** --- ### 17. `/mnt/workdisk/public_share/hakmem/core/tiny_free_fast.inc.h:206-212` - **getenv(Free Fast)** ```c static int s_free_fast_en = -1; if (__builtin_expect(s_free_fast_en == -1, 0)) { const char* e = getenv("HAKMEM_TINY_FREE_FAST"); // ... } ``` - **問題**: 初回のみgetenv実行、以降はキャッシュ(**条件分岐は毎回**) - **影響度**: ★★★(Free Fast Path) - **修正案**: 初期化時に一度だけ実行 --- ## 📊 **軽微な問題(コールドパス)** ### 18. `/mnt/workdisk/public_share/hakmem/core/hakmem_tiny.c:83-87` - **getenv(SuperSlab Trace)** ```c static inline int superslab_trace_enabled(void) { static int g_ss_trace_flag = -1; if (__builtin_expect(g_ss_trace_flag == -1, 0)) { const char* tr = getenv("HAKMEM_TINY_SUPERSLAB_TRACE"); g_ss_trace_flag = (tr && atoi(tr) != 0) ? 1 : 0; } return g_ss_trace_flag; } ``` - **問題**: 初回のみgetenv実行、以降はキャッシュ - **影響度**: ★(コールドパス) --- ### 19. 大量のログ出力関数(fprintf/printf) **全ファイル共通**: 200以上のfprintf/printf呼び出しがリリースビルドでも実行される可能性 **主な問題箇所**: - `core/hakmem_tiny_sfc.c`: SFC統計ログ(約40箇所) - `core/hakmem_elo.c`: ELOログ(約20箇所) - `core/hakmem_learner.c`: Learnerログ(約30箇所) - `core/hakmem_whale.c`: Whale統計ログ(約10箇所) - `core/tiny_region_id.h`: ヘッダー検証ログ(約10箇所) - `core/tiny_superslab_free.inc.h`: Free詳細ログ(約20箇所) **修正方針**: 全てを`#if !HAKMEM_BUILD_RELEASE`でガード --- ## 🎯 **修正優先度** ### **最優先(即座に修正すべき)** 1. **`hak_alloc_api.inc.h`**: 行24-29, 39-56, 147-165のfprintf/atomic_fetch_add 2. **`hak_free_api.inc.h`**: 行77-87のgetenv + atomic_fetch_add 3. **`hak_free_api.inc.h`**: 行15-33のRoute Trace(5-10回/free) 4. **`hak_wrappers.inc.h`**: 行118, 123のMalloc Wrapperログ 5. **`tiny_free_fast.inc.h`**: 行106-112の**毎回getenv実行** ← **CRITICAL!** **期待効果**: これら5つだけで **20-50サイクル/操作** の削減 → **30-50% 性能向上** --- ### **高優先度(次に修正すべき)** 6. `hak_alloc_api.inc.h`: 行191-194, 201-203のGap/OOMログ 7. `hak_alloc_api.inc.h`: 行216の Invalid Magicログ 8. `hak_free_api.inc.h`: 行195, 213-217の Invalid Magicログ 9. `hak_free_api.inc.h`: 行231の BigCache L25 getenv 10. `tiny_alloc_fast.inc.h`: 行106, 130-136のProfilingチェック 11. `tiny_alloc_fast.inc.h`: 行139-156のProfileログ出力 **期待効果**: **5-15サイクル/操作** の削減 → **5-15% 性能向上** --- ### **中優先度(時間があれば修正)** 12. `tiny_alloc_fast.inc.h`: 行311-320のgetenv(Cascade) 13. `tiny_free_fast.inc.h`: 行206-212のgetenv(Free Fast) 14. 全ファイルの200+箇所のfprintf/printfをガード **期待効果**: **1-5サイクル/操作** の削減 → **1-5% 性能向上** --- ## 🚀 **総合的な期待効果** ### **最優先修正のみ(5項目)** - **削減サイクル**: 20-50サイクル/操作 - **現在のオーバーヘッド**: ~50-80サイクル/操作(推定) - **改善率**: **30-50%** 性能向上 - **期待性能**: 9M → **12-14M ops/s** ### **最優先 + 高優先度修正(11項目)** - **削減サイクル**: 25-65サイクル/操作 - **改善率**: **40-60%** 性能向上 - **期待性能**: 9M → **13-18M ops/s** ### **全修正(すべてのfprintfガード)** - **削減サイクル**: 30-80サイクル/操作 - **改善率**: **50-70%** 性能向上 - **期待性能**: 9M → **15-25M ops/s** - **System malloc比**: 25M / 43M = **58%** (現状の4.8倍遅い → **1.7倍遅い**に改善) --- ## 💡 **推奨修正パターン** ### **パターン1: 条件付きコンパイル** ```c #if !HAKMEM_BUILD_RELEASE static _Atomic uint64_t debug_counter = 0; uint64_t count = atomic_fetch_add(&debug_counter, 1); if (count < 10) { fprintf(stderr, "[DEBUG] ...\n"); } #endif ``` ### **パターン2: マクロ化** ```c #if !HAKMEM_BUILD_RELEASE #define DEBUG_LOG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) #else #define DEBUG_LOG(fmt, ...) do { } while(0) #endif // Usage: DEBUG_LOG("[HAK_ALLOC_AT] call=%lu size=%zu\n", call_num, size); ``` ### **パターン3: getenv初期化時キャッシュ** ```c // Before: 毎回チェック if (g_flag == -1) { g_flag = getenv("VAR") ? 1 : 0; } // After: 初期化関数で一度だけ void hak_init(void) { g_flag = getenv("VAR") ? 1 : 0; } ``` --- ## 🔬 **検証方法** ### **Before/After 比較** ```bash # Before ./out/release/bench_fixed_size_hakmem 100000 256 128 # Expected: ~9M ops/s # After (最優先修正のみ) ./out/release/bench_fixed_size_hakmem 100000 256 128 # Expected: ~12-14M ops/s (+33-55%) # After (全修正) ./out/release/bench_fixed_size_hakmem 100000 256 128 # Expected: ~15-25M ops/s (+66-177%) ``` ### **Perf 分析** ```bash # IPC (Instructions Per Cycle) 確認 perf stat -e cycles,instructions,branches,branch-misses ./out/release/bench_* # Before: IPC ~1.2-1.5 (低い = 多くのストール) # After: IPC ~2.0-2.5 (高い = 効率的な実行) ``` --- ## 📝 **まとめ** ### **現状の問題** 1. リリースビルドでも**大量のデバッグ処理が実行**されている 2. ホットパスで**毎回atomic_fetch_add + 条件分岐 + fprintf**実行 3. 特に`tiny_free_fast.inc.h`の**毎回getenv実行**は致命的 ### **修正の影響** - **最優先5項目**: 30-50% 性能向上(9M → 12-14M ops/s) - **全項目**: 50-70% 性能向上(9M → 15-25M ops/s) - **System malloc比**: 4.8倍遅い → 1.7倍遅い(**60%差を埋める**) ### **次のステップ** 1. **最優先5項目を修正**(1-2時間) 2. **ベンチマーク実行**(Before/After比較) 3. **Perf分析**(IPC改善を確認) 4. **高優先度項目を修正**(追加1-2時間) 5. **最終ベンチマーク**(System mallocとの差を確認) --- ## 🎓 **学んだこと** 1. **リリースビルドでもデバッグ処理は消えない** - `#if !HAKMEM_BUILD_RELEASE`でガード必須 2. **fprintf 1個でも致命的** - ホットパスでは絶対に許容できない 3. **getenv毎回実行は論外** - 初期化時に一度だけキャッシュすべき 4. **atomic_fetch_add も高コスト** - 5-10サイクル消費するため、デバッグのみで使用 5. **条件分岐すら最小限に** - メモリアロケータのホットパスでは1サイクルが重要 --- **レポート作成日時**: 2025-11-13 **対象コミット**: 79c74e72d (Debug patches: C7 logging, Front Gate detection, TLS-SLL fixes) **分析者**: Claude (Sonnet 4.5)