본문으로 건너뛰기

[PR #3494] raw_block batched_remove 락 오버헤드 개선

[!tldr] 업무 관점 takeaway 우리 팀이 upstream에 올린 첫 raw_block 성능 PR (#3494, OPEN, 리뷰 대기). raw_block backend가 키 N개 삭제 시 락을 N번 잡던 걸 1번 batch로 줄임 — RustRawBlockBackend.batched_remove override 추가(+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이 그걸 안 따른 것이 문제.


코드 근거

backendbatched_removeN키 삭제 시 락
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 fallbackcpu_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-files clean
  • 다른 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-팀]] — 동료 작업 영역 (충돌 점검 맥락)