raw_block 성능 분석 — 종합 우선순위
[!tldr] 업무 관점 takeaway raw_block 스택(Rust io_uring 엔진 + Python
RawBlockCore+ L2 어댑터)을 종단 분석해 뽑은 개선 항목 우선순위 맵. 분류 기준 = correctness > 측정가능 성능 > 조건부 성능 > 설계 위험 > latent. 우리 업무 직결 항목: T1(delete TOCTOU 버그), L1/L2(put_many 락·배치), B2+P2(_free_slotsdeque+set — FDP wear leveling 선행), P0(io_uring batched API — io_uring/FDP 효과의 전제). 이 표가 raw_block 기여 PR들의 출처다 (이미 [[raw_block-batched-remove-PR|#3494]] 진행 중).
근거:
raw/docs/notes/raw_block_priority.md,raw/docs/notes/raw-block-perf-findings.md(based-on-commit 29bbd553 / eaa2bfee, 2026-05-28). 라벨 체계가 둘 있다 — 이 페이지(T1/L1/P0…)는 종합 분석 렌즈, [raw_block-개선-Task]는 독립 PR 후보 렌즈. 같은 코드를 다른 각도로 본 것이라 항목이 일부 겹친다(예: 여기 B2+P2 ↔ 거기 H1+H2).
Tier 1 — 즉시 (correctness 버그, 재현됨)
| # | 항목 | 경로 | 영향 | 증명 |
|---|---|---|---|---|
| T1 | delete() TOCTOU → _total_bytes_used 영구 과대 계상 | MP | eviction 조기 발동, 불필요 key 삭제 | ✅ mock sleep 재현 |
수정: delete_many가 was_indexed를 반환하도록 변경 (난이도 낮음). 리뷰 노트: raw/work/reviews/cr-9fc5a901-rawblock-delete-toctou.md.
Tier 2 — 단기 (측정 가능한 성능, 수정 쉬움)
| # | 항목 | 영향 | 증명 |
|---|---|---|---|
| L1 | put_many 키당 락 4→2회 | N=100 배치에서 락 400→200회 | ✅ perf_counter |
| L2 | legacy batched_submit_put_task N-key 배치를 1-key×N coroutine으로 분해 | 배치 이점 전무 + 이벤트루프 N배 오버헤드 | ✅ 배치 크기별 latency |
⚠️ L1 수정 시
inflight_io_count일괄 관리가 D2(idle 판단)에 영향 → 확인 필요. 참고: 같은 "락 N→소수" 패턴의 삭제 경로 버전이 [[raw_block-batched-remove-PR|batched_remove(#3494)]]로 이미 upstream 진행 중.
Tier 3 — 조건부 단기 (적용 조건 있음)
| # | 항목 | 조건 | 영향 | 증명 |
|---|---|---|---|---|
| B2+P2 | _free_slots를 deque+set으로 교체 (LIFO→FIFO, O(1) dedup) | FDP/HC-SSD 도입 시 | FDP에서 RU wear leveling | ✅ slot histogram → nvme fdp stats |
| P1 | _snapshot_state lock 내 shallow copy 분리 | entry > ~10,000 | lock 보유 시간 단축 | ✅ lock timing |
B2+P2는 한 번의 deque+set 교체로 동시 해결 — [[raw_block-개선-Task]] H1(O(1))·H2(FIFO)와 같은 변경. FDP placement hint 도입 전 선행 작업으로 우리 업무에 직접 걸린다.
Tier 4 — 코드 품질 (기능 영향 없음)
Optional→X | None(Q1), Any 명시화(Q2), zip(strict=True)(Q3), except: pass→logger.debug(Q4). LMCache 코딩 표준(§2 typing 등) 위반 정리 — [[LMCache-기여-가이드]] 규칙과 직결.
Tier 5 — 중기 설계
| # | 항목 | 영향 | 난이도 |
|---|---|---|---|
| D2 | max_dirty_threshold 추가 — 지속 I/O 중 checkpoint 미발생 방지 | 비정상 종료 시 index 손실 | 낮음 |
| D1 | JSON checkpoint 용량 초과 silent skip 대응 | 수만 entry 시 crash 후 index 전체 유실 | 중간 |
| L3 | io_uring SQ 활용률 (depth 256 대비 실제 2~4 SQE) | io_uring 이점 미사용 | 중간 (단기 대안: worker 수↑) |
D1 = [[S2-checkpoint-overflow|S2 버그]]와 동일 사안. 여기선 설계 tier로 분류했지만 HC SSD 스케일에선 correctness 급. L3 = io_uring SQ 미활용 → 아래 P0와 짝.
Tier 6~7 — latent / Rust 연동
- L4 legacy
close()10ms polling →threading.Condition(최대 10ms shutdown 지연) - T2
batched_async_containspin N×2 lock (기능 안전, eviction 시 낭비) - D3 단일 글로벌 lock 분리 (현재 I/O가 lock 밖 → 경합 제한적)
- P3+L3
pwrite_batch/pread_batchRust API → io_uring SQ 완전 활용 (선행: Rust 바인딩 확장 +put_many재설계)
Rust io_uring 엔진 분석 (perf-findings §1) — P0/P1
rust/raw_block/src/lib.rs 종단 분석에서 나온 별도 우선순위:
| 우선순위 | 항목 | 효과 |
|---|---|---|
| P0 | put_many/load_many_into → io_uring batched API 활용 (현재 키 단위 직렬 I/O) | 쓰기·읽기 처리량 대폭↑ |
| P0 | batched_write의 notify_one N→1회 감소 (notify-one 폭풍) | CPU 오버헤드↓ |
| P1 | wait_iouring timeout busy-wait(10μs) → condvar.wait() | idle CPU 낭비 제거 |
| P1 | _snapshot_state lock 구간 축소 | 대용량 인덱스 I/O 블로킹 방지 |
| P1 | register_fixed_buffers 실제 호출 (구현됐으나 Python 미호출) → zero-copy 활성화 | io_uring 경로 copy 1회 제거 |
P0(batched API)가 우리 io_uring/FDP 작업의 전제 — 지금은 SQ depth 256인데 실제 2~4 SQE만 채움(L3). 키 단위 직렬 제출을 배치 제출로 바꿔야 io_uring·FDP placement의 효과가 측정 가능해진다.
실행 순서
T1 (버그) → L1+L2 (측정하며 수정, 세트)
→ B2+P2 (FDP 준비에 포함) → P1 (대용량 NVMe 확정 시)
→ D2 → D1(=S2) (설계 안정화)
→ L3 단기대안(worker 수 튜닝) → P3+L3 근본해결(Rust batched API)
관련 페이지
- [[raw_block-개선-Task]] — 독립 PR 후보 렌즈 (H1/H2/M1/M2/S2)
- [[raw_block-batched-remove-PR]] — 진행 중 PR #3494 (락 N→2, 삭제 경로)
- [[raw_block-종단-분석]] — L1~L4 전계층, FDP 삽입 포인트
- [[raw_block-내부구조]] —
put_many/delete_many/_free_slots/_snapshot_state코드 근거 - [[raw_block-io_uring-cmd]] — io_uring/NVMe passthrough (P0·L3 배경)
- [[S2-checkpoint-overflow]] — D1 = checkpoint overflow 상세