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.pygit 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
| 용어 | 뜻 |
|---|---|
| L1 | LMCache 인메모리 캐시 (CPU RAM 슬롯 풀) — 디스크 IO 시 사용되는 버퍼 |
l1_memory_desc.align_bytes | L1 풀 슬롯의 정렬 (예: 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 깨짐 |
코드
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 0slot_bytes=1MiB: ✅ 일반적
→ 실제 최소값은 header_bytes + block_align.
코드
if self.slot_bytes < self.header_bytes + 1:
raise ValueError("slot_bytes must be >= header_bytes + 1")
payload 용량
payload_capacity = self.slot_bytes - self.header_bytes
이 값이 양수여야 KV 한 바이트라도 담을 수 있음.
4 개 묶어 보면
| 제약 | 어디서 옴 | 어기면 |
|---|---|---|
per_tp_device_paths MP 거부 | MP 아키텍처 (서버 1 개 모델) | startup ValueError |
| L1 align ≥ block_align | O_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 후보)