본문으로 건너뛰기

raw_block_line.md 의 "미충족 / 제약" 4 개 항목

변경 검증 가이드 (다음 fetch 후):

git log eaa2bfee..HEAD -- lmcache/v1/distributed/l2_adapters/raw_block_l2_adapter.py lmcache/v1/storage_backend/raw_block/core.py lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py
git log eaa2bfee..HEAD -- private/docs/notes/raw_block_line.md

위에 커밋이 잡히면 (1) 4 개 ValueError 가 살아있는지, (2) MP 가 per_tp_device_paths 를 지원하기 시작했는지, (3) use_odirect 시 alignment 검증이 다른 형태로 바뀌었는지 확인. (2) 가 일어나면 제약 1 섹션 + "FDP/HC-SSD 작업 시 영향" 의 per_tp_device_paths 항목 통째로 다시 써야 함.


원문: private/docs/notes/raw_block_line.md:73-78

이 섹션이 뭘 모은 건가

invariant 표는 adapter 가 보장하는 것, 이 섹션은 adapter/core 가 거부하는 것 = 호출자의 precondition 들. 어기면 startup 에서 즉시 ValueError.


1. per_tp_device_paths 는 MP 모드에서 거부

무슨 설정인가

extra_config:
rust_raw_block.per_tp_device_paths:
0: /dev/nvme0n1
1: /dev/nvme1n1
2: /dev/nvme2n1
3: /dev/nvme3n1

→ TP rank 별로 다른 디바이스 매핑. 각 vLLM 워커(GPU) 가 자기 전용 NVMe 에 KV 씀.

왜 non-MP 만 허용

rust_raw_block_backend.py:108-130:

if self.metadata.world_size > 1:
tp_rank = self.metadata.worker_id # 자기 rank 를 앎
device_path = per_tp_devices[tp_rank]

각 vLLM 워커 프로세스가 자기 rank 를 알기 때문에 rank 별 디바이스 선택 가능.

MP 가 거부하는 이유

raw_block_l2_adapter.py:147-150:

if "per_tp_device_paths" in d:
raise ValueError(
"per_tp_device_paths is not supported in MP raw_block mode"
)

MP 는 LMCache 서버 1 개가 여러 vLLM 워커 (심지어 다른 vLLM 인스턴스) 의 요청을 받아 처리. 하나의 큰 풀 + hash sharding 이 자연스러움. tp_and_workers.md 참고.

한 줄

non-MP: 워커 = 프로세스 → 각자 자기 디바이스. MP: 서버 1 개 → rank 별 분리는 모델에 안 맞음.


2. use_odirect=True 면 L1 alignment ≥ block_align

O_DIRECT 란

리눅스 파일 open 플래그. 켜면:

  • 커널 페이지 캐시 우회 — 디스크에 직접 read/write
  • 단점: 메모리 정렬 요구 — 버퍼 시작 주소, offset, 길이가 모두 디바이스 블록 (보통 512B 또는 4096B) 정렬

정렬 안 맞으면 EINVAL.

LMCache 의 L1 / block_align

용어
L1LMCache 인메모리 캐시 (CPU RAM 슬롯 풀) — 디스크 IO 시 사용되는 버퍼
l1_memory_desc.align_bytesL1 풀 슬롯의 정렬 (예: 64, 4096)
block_align디바이스/O_DIRECT 가 요구하는 정렬 (보통 4096)

왜 L1 정렬이 더 커야 하나

L1 슬롯이 디스크 IO 에 직접 쓰임 (zero-copy 경로):

L1 슬롯 시작: 0xABCD0040 (64-byte 정렬)
↓ pwrite (O_DIRECT)
디바이스 요구: 4096-byte 정렬
→ 64 < 4096 이라 EINVAL

→ L1 풀 정렬이 디바이스 정렬보다 작으면 zero-copy 항상 깨짐. startup 에서 미리 거부.

코드

raw_block_l2_adapter.py:306-313:

if (config.use_odirect
and l1_memory_desc is not None
and l1_memory_desc.align_bytes < config.block_align):
raise ValueError(
"raw_block requires l1_align_bytes >= block_align when use_odirect=true"
)

한 줄

O_DIRECT 정렬 까다로워서 L1 슬롯 정렬이 디바이스 정렬 이상이어야 zero-copy 가능.


3. slot_bytes, header_bytes, meta_total_bytes 모두 block_align 배수

디바이스 레이아웃

0 device_size
├── meta_total_bytes (256 MiB) ──┤
│ meta copy 0 │ meta copy 1 │ data slots ... │
└──────────────────────────────────────────────────┘

data_base_offset
= meta_total_bytes

각 데이터 슬롯:

│ header_bytes (4 KiB) │ payload (slot_bytes - header_bytes) │

왜 셋 다 block_align 배수

O_DIRECT/io_uring 이 모든 IO offset 도 정렬 요구. 한 항목이라도 어긋나면 다음 항목 시작 offset 어긋남:

변수위반 예결과
slot_bytes 가 4096 배수 아님 (예: 1MiB-1B)슬롯 1 시작 offset 어긋남슬롯 1 부터 모든 IO EINVAL
header_bytes 가 4096 배수 아님header 끝 = payload 시작 어긋남payload IO 깨짐
meta_total_bytes 가 4096 배수 아님data_base_offset 어긋남데이터 영역 전체 IO 깨짐

코드

core.py:208-217:

if self.header_bytes % self.block_align != 0:
raise ValueError("header_bytes must be a multiple of block_align")
...
if self.slot_bytes % self.block_align != 0:
raise ValueError("slot_bytes must be a multiple of block_align")
...
if self.meta_total_bytes % self.block_align != 0:
raise ValueError("meta_total_bytes must be a multiple of block_align")

한 줄

레이아웃의 모든 경계가 디바이스 정렬을 만족해야 어디서 read/write 해도 EINVAL 안 남.


4. slot_bytes >= header_bytes + 1

의미

슬롯 = header + payload. payload 영역 최소 1 바이트는 있어야 슬롯이 의미. header 만 있고 payload 0 이면 키 메타만 저장되고 데이터 못 저장 = 캐시 무용.

실제로는 더 큰 의미

block_align 배수 제약과 함께 보면 — block_align=4096, header_bytes=4096 이면:

  • slot_bytes=4097: ❌ 4096 배수 아님
  • slot_bytes=8192: ✅ payload 4096B (1 block)
  • slot_bytes=4096: ❌ payload 0
  • slot_bytes=1MiB: ✅ 일반적

→ 실제 최소값은 header_bytes + block_align.

코드

core.py:210-211:

if self.slot_bytes < self.header_bytes + 1:
raise ValueError("slot_bytes must be >= header_bytes + 1")

payload 용량

core.py:873:

payload_capacity = self.slot_bytes - self.header_bytes

이 값이 양수여야 KV 한 바이트라도 담을 수 있음.


4 개 묶어 보면

제약어디서 옴어기면
per_tp_device_paths MP 거부MP 아키텍처 (서버 1 개 모델)startup ValueError
L1 align ≥ block_alignO_DIRECT 정렬 요구zero-copy 경로 깨짐
셋 다 block_align 배수디바이스 정렬 요구런타임 IO EINVAL
slot_bytes >= header_bytes + 1의미적 sanity데이터 0 바이트 슬롯 방지

세 개(2,3,4) 는 다 O_DIRECT/raw block 디바이스 정렬 규칙의 다른 면, 첫 번째만 별도 — MP vs non-MP 아키텍처 차이.

FDP/HC-SSD 작업 시 영향

plugin L2 adapter 도 raw block 쓸 거면:

  • block_align: NVMe LBA 크기 (보통 4096) 따라가야 함
  • L1 정렬: plugin 이 받는 메모리 풀이 4096 정렬인지 startup 검증 추가
  • per_tp_device_paths: MP 멀티 디바이스 sharding 필요하면 다른 메커니즘 설계 (예: capacity-aware routing)
  • slot 크기: FDP RU(Reclaim Unit) 크기와 slot_bytes 정렬 맞추면 PLID-RU 매핑 깔끔 (raw_block_line.md H4 후보)