p99을 의미 있게 측정하려면 iters를 100 이상으로 올려야 합니다 (iters=10에서는 p99 = 9~10번째 샘플 = 사실상 max라 단일 jitter에 휘둘림). 스크립트는 이미 p99을 dict로 반환하지만 cmd_compare 출력 표에는 p50/p95만 찍고 있어서 그건 직접 보거나 표를 늘리시면 됩니다.
실행 가이드
- 사전 확인
cd /home/ny/workspace/LMCache-dev
(a) 디바이스 권한 확인 — user:ny:rw- 보여야 함
getfacl /dev/nvme10n1 | grep ny
(b) working tree 깨끗한지 — untracked만 있어야 안전 (.claude/settings.json, private/)
git status --short
(c) Rust extension 동작 확인 (use_uring_cmd kwarg 받아야 함)
PYTHONPATH=/home/ny/workspace/LMCache-dev .venv/bin/python -c " from lmcache_rust_raw_block_io import RawBlockDevice d = RawBlockDevice('/dev/nvme10n1', writable=True, use_odirect=True, alignment=4096, io_engine='io_uring', iouring_queue_depth=256, use_uring_cmd=False) print('OK') " use_uring_cmd 에러가 나면 재빌드:
.venv/bin/maturin develop --release --manifest-path rust/raw_block/Cargo.toml 2. baseline 측정 — bf7d2ff1 (PR #3274 머지 직후, P0 적용 전)
git checkout bf7d2ff1
PYTHONPATH=/home/ny/workspace/LMCache-dev .venv/bin/python
/tmp/bench_put_many_real_nvme.py /dev/nvme10n1
--label baseline --out /tmp/bench_baseline.json
--warmup 5 --iters 100
--n 100,1000 --payload 4096,65536
옵션 의미:
--warmup 5 --iters 100: 워밍업 5회 후 100회 측정 → p99이 95~96번째 샘플로 의미 있어짐 --n 100,1000 --payload 4096,65536: 핵심 4셀만 (시간 절약). 전체 그리드 보고 싶으면 옵션 빼세요 (기본 9셀) 3. p0_clean 측정 — 860d0598 (PR 후보 브랜치)
git checkout 860d0598
PYTHONPATH=/home/ny/workspace/LMCache-dev .venv/bin/python
/tmp/bench_put_many_real_nvme.py /dev/nvme10n1
--label p0_clean --out /tmp/bench_p0_clean.json
--warmup 5 --iters 100
--n 100,1000 --payload 4096,65536
4. 원래 브랜치 복귀 + 비교
git checkout perf/rawblock-put-many-batch-io
PYTHONPATH=/home/ny/workspace/LMCache-dev .venv/bin/python
/tmp/bench_put_many_real_nvme.py
--compare /tmp/bench_baseline.json /tmp/bench_p0_clean.json
기본 출력은 p50/p95 표만 찍습니다.
- p99까지 보는 방법 (3가지 중 택1) (a) JSON에서 직접 — 가장 간단:
.venv/bin/python -c " import json a = json.load(open('/tmp/bench_baseline.json')) b = json.load(open('/tmp/bench_p0_clean.json')) print(f'{"N":>6} {"payload":>8} {"base_p99":>10} {"p0_p99":>10} {"delta%":>8}') for ca, cb in zip(a['cells'], b['cells']): d = (cb['p99'] - ca['p99']) / ca['p99'] * 100 print(f'{ca["n"]:>6} {ca["payload_bytes"]:>8} {ca["p99"]:>10.3f} {cb["p99"]:>10.3f} {d:>+7.1f}%') " (b) compare 표에 p99 컬럼 추가 — /tmp/bench_put_many_real_nvme.py:184-217의 cmd_compare 함수 헤더와 행 출력에 p99 추가. 이 한 줄 수정만 하면 됩니다:
기존 (라인 ~205):
header = ( f"{'N':>6} {'payload':>10} " f"{label_a + '_p50':>14} {label_b + '_p50':>14} {'delta%':>8} " f"{label_a + '_p95':>14} {label_b + '_p95':>14} {'delta%':>8}" )
끝에 p99 컬럼 한 세트 추가하고, 아래 print에서 ca['p99']/cb['p99']를 같은 식으로 출력
(c) JSON에 들어 있는 dict 그대로 보기 — 셀별 raw stats 확인:
.venv/bin/python -c "import json; print(json.dumps(json.load(open('/tmp/bench_p0_clean.json'))['cells'], indent=2))" 6. 측정 시간 예상 --iters 100, 4셀 (N=100·1000 × 4KB·64KB):
N=100/64KB: ~12ms × 105 = ~1.3초/셀 N=1000/64KB: ~98ms × 105 = ~10초/셀 baseline 4셀 합: 약 30초 p0_clean 4셀 합: 약 15초 합계 약 1분 미만 전체 9셀 그리드면 baseline ~10분, p0_clean ~3분 정도 예상.
주의사항 워밍업 필수: 첫 1~2 run은 디바이스 캐시/io_uring SQE pool 워밍업으로 항상 느립니다. --warmup 5 권장. 다른 워크로드와 섞이지 마세요: 측정 중 다른 사람이 NVMe 쓰면 노이즈 확 늡니다. iostat -x nvme10n1 1로 동시 사용 모니터링 가능. N=1000은 분포 변동 큼: baseline의 p95/p50 ≈ 2배 → 같은 셀을 여러 번 돌려보면 결과가 ±10% 정도 흔들립니다. 의심스러우면 한 번 더. device 27.9TB 중 앞부분만 사용: 스크립트가 META_BYTES=4MB + slot_bytes × n_slots만큼만 capacity로 잡으니 안전합니다.