New Feature: ss_prefault_box.h - Box for controlling SuperSlab page prefaulting policy - ENV: HAKMEM_SS_PREFAULT (0=OFF, 1=POPULATE, 2=TOUCH) - Default: OFF (safe mode until further optimization) Bug Fix: 4MB MAP_POPULATE regression - Problem: Fallback path allocated 4MB (2x size for alignment) with MAP_POPULATE causing 52x slower mmap (0.585ms → 30.6ms) and 35% throughput regression - Solution: Remove MAP_POPULATE from 4MB allocation, apply madvise(MADV_WILLNEED) only to the aligned 2MB region after trimming prefix/suffix Changes: - core/box/ss_prefault_box.h: New prefault policy box (header-only) - core/box/ss_allocation_box.c: Integrate prefault box, call ss_prefault_region() - core/superslab_cache.c: Fix fallback path - no MAP_POPULATE on 4MB, always munmap prefix/suffix, use MADV_WILLNEED for 2MB only - docs/specs/ENV_VARS*.md: Document HAKMEM_SS_PREFAULT Performance: - bench_random_mixed: 4.32M ops/s (regression fixed, slight improvement) - bench_tiny_hot: 157M ops/s with prefault=1 (no crash) Box Theory: - OS layer (ss_os_acquire): "how to mmap" - Prefault Box: "when to page-in" - Allocation Box: "when to call prefault" 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
81 lines
2.9 KiB
C
81 lines
2.9 KiB
C
// ss_prefault_box.h - Box: SuperSlab Page Prefaulting Policy
|
||
// Purpose: Control SuperSlab prefault strategy (MAP_POPULATE / manual touch)
|
||
// Box Theory:
|
||
// - This box owns「いつ / どの程度 pf を前倒しするか」というポリシーだけを担当。
|
||
// - 実際の mmap は ss_os_acquire_box(下層)、Superslab 初期化は ss_allocation_box(上層)が担当。
|
||
//
|
||
// Responsibilities:
|
||
// - HAKMEM_SS_PREFAULT の値から prefault ポリシーを決定
|
||
// - 必要に応じて SuperSlab 全域を 1 回だけ touch して page-in する
|
||
//
|
||
// ENV:
|
||
// HAKMEM_SS_PREFAULT=0 Off (既存動作に近い。g_ss_populate_once のワンショットのみ)
|
||
// HAKMEM_SS_PREFAULT=1 POPULATE (MAP_POPULATE のみ使用)
|
||
// HAKMEM_SS_PREFAULT=2 TOUCH (MAP_POPULATE + 手動4KB touch)
|
||
// HAKMEM_SS_PREFAULT=3 ASYNC (予約値。現状は TOUCH と同等に扱う)
|
||
//
|
||
// Default:
|
||
// - 未指定時は 1 (POPULATE) とし、SuperSlab 取得時の page fault を大きく削減する。
|
||
|
||
#ifndef HAKMEM_SS_PREFAULT_BOX_H
|
||
#define HAKMEM_SS_PREFAULT_BOX_H
|
||
|
||
#include <stddef.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
|
||
typedef enum {
|
||
SS_PREFAULT_OFF = 0, // No prefault (legacy: g_ss_populate_once のみ)
|
||
SS_PREFAULT_POPULATE = 1, // MAP_POPULATE only
|
||
SS_PREFAULT_TOUCH = 2, // MAP_POPULATE + manual page touch
|
||
SS_PREFAULT_ASYNC = 3 // Reserved for future background prefault
|
||
} SSPrefaultPolicy;
|
||
|
||
// Return current prefault policy (parsed once from HAKMEM_SS_PREFAULT).
|
||
static inline SSPrefaultPolicy ss_prefault_policy(void)
|
||
{
|
||
static int cached = -1;
|
||
if (cached != -1) {
|
||
return (SSPrefaultPolicy)cached;
|
||
}
|
||
|
||
// 一時的な安全デフォルト: OFF
|
||
// (4MB MAP_POPULATE 問題の影響を避けつつ、必要なときだけ明示的に ON にする)
|
||
int policy = SS_PREFAULT_OFF;
|
||
const char* env = getenv("HAKMEM_SS_PREFAULT");
|
||
if (env && *env) {
|
||
int v = atoi(env);
|
||
if (v < 0) v = 0;
|
||
if (v > 3) v = 3;
|
||
policy = v;
|
||
}
|
||
cached = policy;
|
||
return (SSPrefaultPolicy)policy;
|
||
}
|
||
|
||
// Prefault a contiguous SuperSlab region by touching one byte per page.
|
||
// Caller is expected to call this「mmap直後」に 1 回だけ実行する。
|
||
static inline void ss_prefault_region(void* addr, size_t size)
|
||
{
|
||
SSPrefaultPolicy policy = ss_prefault_policy();
|
||
if (policy < SS_PREFAULT_TOUCH) {
|
||
// POPULATE または OFF の場合は手動 touch は行わない。
|
||
return;
|
||
}
|
||
if (!addr || size == 0) {
|
||
return;
|
||
}
|
||
|
||
long ps = sysconf(_SC_PAGESIZE);
|
||
size_t page = (ps > 0) ? (size_t)ps : (size_t)4096;
|
||
|
||
volatile char* p = (volatile char*)addr;
|
||
for (size_t off = 0; off < size; off += page) {
|
||
(void)p[off];
|
||
}
|
||
// Ensure last byte is also touched (covers exact multiples).
|
||
(void)p[size - 1];
|
||
}
|
||
|
||
#endif // HAKMEM_SS_PREFAULT_BOX_H
|