[PR #3494] raw_block batched_remove 락 오버헤드 개선
[!tldr] 업무 관점 takeaway 우리 팀이 upstream에 올린 첫 raw_block 성능 PR (#3494, OPEN, 리뷰 대기). raw_block backend가 키 N개 삭제 시 락을 N번 잡던 걸 1번 batch로 줄임 —
RustRawBlockBackend.batched_removeoverride 추가(+30줄). Samsung 대용량 SSD 시나리오에서 더 두드러지는 이슈(eviction batch N이 수백~수천). 동작 변경 없는 순수 최적화라 머지 난이도 낮고, raw_block 기여 발판이 된다.
- PR: #3494 — 2026-06-02 업로드, OPEN (리뷰 대기)
- 브랜치:
nayeonikim:perf/raw-block-batched-remove - 대상 파일:
lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py(A1-1 스코프) - 성격: Performance optimization (락 획득 횟수 감소, 동작·시그니처 불변)
한 줄 주장
raw_block / local_cpu backend는 단일-lock batch 능력을 이미 가졌는데 (_core.delete_many가 락 1회로 N키 처리), batched_remove override가 없어 abstract의 for-loop fallback을 타면서 키 N개당 락을 N번 잡는다. DAX backend는 같은 자리에 이미 override를 둬서 해결한 패턴 — raw_block이 그걸 안 따른 것이 문제.
코드 근거
| backend | batched_remove | N키 삭제 시 락 |
|---|---|---|
DAX (dax_backend.py:573) | ✅ override → delete_many 1회 | _lock 1회 |
raw_block (rust_raw_block_backend.py) | ❌ 없음 → for-loop fallback | _pin_lock N회 + _core._lock N회 |
local_cpu (local_cpu_backend.py) | ❌ 없음 → for-loop fallback | cpu_lock N회 |
# abstract_backend.py:235 — 기본 fallback (키마다 self.remove() → 락 N회)
def batched_remove(self, keys, force=True) -> int:
return sum(self.remove(key, force=force) for key in keys)
변경 내용 (A1-1)
RustRawBlockBackend에 override 추가 — _pin_lock 1회 + delete_many 내부 _core._lock 1회 → 락 acquire N → 2.
def batched_remove(self, keys, force=True) -> int:
if not keys:
return 0
specs = [encode_legacy_key(k).encoded for k in keys]
with self._pin_lock:
results = self._core.delete_many(specs, force=force)
for spec, removed in zip(specs, results, strict=True):
if removed:
self._pinned_keys.discard(spec)
return sum(results)
delete_many는 슬롯 메타 정리 + free slot 추가만 함 (디스크 I/O 없음 — 그건 checkpoint에서 일괄). 즉 이 락 경합은 순수 CPU/Python 레벨 오버헤드로, NVMe 성능과 무관. 다만 raw_block은 대용량 SSD 가정 → eviction N이 큼.
왜 Samsung 색깔인가
batched_remove는 계층 이동·캐시 정리·eviction에서 호출되며 N=수십~수만이 일상적(cache_engine.py:1247/1401/1461, local_cpu_backend.py:884 clear 등). MP 모드에서 backend는 공유 자원이라, eviction이 락을 N번 잡으면 매 unlock-relock 사이 context switch + 캐시 라인 ping-pong + GIL 경합이 누적된다. 대용량 SSD일수록 N이 커져 효과가 두드러짐.
검증 결과 (W23, 2026-06-01~07)
- 분량: 구현
+30/ 테스트+99, 인터페이스·시그니처 미변경 - 신규 단위 테스트 1건 / 5케이스: ① 빈 입력 ② batch 삭제 ③ pin 보존 ④ 미존재 키 ⑤ force 우회
- raw_block 관련 테스트 54/54 통과
pre-commit run --all-filesclean- 다른 backend·MP path(L2 adapter) 영향 없음 (override만 추가, default fallback 유지)
동료 작업과의 충돌
박대준 #3226(checkpoint 쓰기), 한대규 FDP·#3305(checksum, _write_one), Ankit #3274(io_uring, _write/_read_buffers), 서동주 #3119(L2 adapter), 권상윤 #3260(alignment) — 모두 다른 함수/모듈. 충돌 없음. backend 클래스에 새 메서드를 추가하는 데 국한.
후속 — A1-2 (별도 PR, 설계 단계)
local_cpu_backend도 같은 override가 필요하나, clear()가 수만 키를 한 락에 처리하면 점유 시간이 수십 ms가 되어 다른 워커가 통째로 블록됨. → batch size limit(chunking, 예: 1024키 단위) 도입이 선행돼야 함. 그래서 raw_block(A1-1)을 먼저 올리고, local_cpu(A1-2)는 chunking 설계를 포함해 분리.
관련 페이지
- [[raw_block-개선-Task]] — raw_block PR 후보 전체 (H1/H2/M1/M2/S2)
- [[raw_block-Cleanup-PR]] — 또 다른 raw_block PR 후보 (dead code 정리)
- [[raw_block-내부구조]] —
delete_many·_pin_lock·슬롯 구조 코드 근거 - [[Samsung-LMCache-팀]] — 동료 작업 영역 (충돌 점검 맥락)