테스트¶
mlxcel은 계층화된 테스트 전략을 사용합니다: 순수 로직을 위한 인라인 단위 테스트, 모듈 수준 동작을 위한 인라인 _tests.rs 모듈, 크로스 모듈 정확성을 위한 tests/의 통합 테스트, 그리고 전체 TP, PP, 분리형 추론 스택을 위한 분산 엔드-투-엔드 테스트.
목차¶
단위 테스트¶
인라인 #[cfg(test)] 모듈¶
소규모의 집중적인 테스트는 #[cfg(test)]로 보호되어 테스트하는 모듈에 직접 위치합니다. 이것이 코드베이스에서 가장 일반적인 종류의 테스트입니다.
// src/distributed/tensor_parallel/shard_strategy.rs
#[cfg(test)]
#[path = "shard_strategy_tests.rs"]
mod tests;
#[path = ...] 패턴은 테스트 파일을 소스 파일의 형제로 유지하며 명시적인 _tests.rs 접미사를 사용하여 테스트 검색을 쉽게 합니다:
src/distributed/tensor_parallel/
shard_strategy.rs # implementation
shard_strategy_tests.rs # tests for shard_strategy.rs
collective.rs
collective_tests.rs
...
이 패턴은 코드베이스 전반에 걸쳐 일관되게 사용됩니다. 단순하지 않은 모듈에는 모두 짝이 되는 _tests.rs 파일이 있습니다.
단위 테스트 예시¶
// shard_strategy_tests.rs
use super::*;
#[test]
fn test_column_parallel_shard_plan() {
let plan = LayerShardPlan {
weight_pattern: "model.layers.{}.self_attn.q_proj.weight".to_string(),
strategy: ShardStrategy::ColumnParallel,
shard_axis: 0,
comm_pattern: CommPattern::AllGather,
};
assert_eq!(plan.strategy, ShardStrategy::ColumnParallel);
assert_eq!(plan.comm_pattern, CommPattern::AllGather);
}
테스트를 추가할 위치¶
| 테스트 대상 코드 | 테스트 파일 위치 |
|---|---|
| 모델 감지 헬퍼 | src/models/detection_tests.rs |
| 모델 정제 | src/models/sanitize_tests.rs |
| 로더 정규화 | src/loading/tests.rs 또는 src/loading/<family>_tests.rs |
| VLM 로더 | src/loading/vlm_<family>_tests.rs |
| 비전 병합 계약 | src/vision/merge_tests.rs |
| 프롬프트/토큰 확장 | src/multimodal/*_tests.rs |
| 서버 타입 | src/server/chat_request_tests.rs, src/server/config_tests.rs 등 |
| 분산 프리미티브 | src/distributed/<module>_tests.rs |
| 클러스터 핸드셰이크, 프레임 보호, 인증 | src/distributed/cluster_handshake_tests.rs, frame_protection_tests.rs, cluster_auth_tests.rs |
디코드 엔진(Engine, DirectEngine, verify round) | src/lib/mlxcel-core/src/engine/*_tests.rs |
| 투기적 드래프터 | src/lib/mlxcel-core/src/speculative/*_tests.rs |
| 가중치 materialization 정책 | src/lib/mlxcel-core/src/weight_materialize_tests.rs |
통합 테스트¶
통합 테스트는 tests/ 디렉터리에 있습니다. 크로스 모듈 동작을 테스트하며 분산 테스트의 경우 실제 모델 가중치가 필요하지 않습니다.
테스트 파일¶
| 파일 | 설명 |
|---|---|
tests/model_loading.rs | 모델 감지 및 로딩 (로컬 모델 가중치 필요; 대부분의 테스트는 #[ignore]) |
tests/distributed_integration.rs | 크로스 모듈 분산 인프라 테스트 (전송, 레지스트리, 지표) |
tests/tp_e2e.rs | 텐서 병렬화 엔드-투-엔드 테스트 (완전 시뮬레이션, 하드웨어 불필요) |
tests/pipeline_e2e.rs | 파이프라인 병렬화 엔드-투-엔드 테스트 (완전 시뮬레이션) |
tests/disaggregated_e2e.rs | 분리형 추론 엔드-투-엔드 테스트 (완전 시뮬레이션) |
tests/turbo_kv_e2e.rs | TurboQuant KV 캐시 품질 게이트: wikitext-2 PPL + NIAH 검색 (모델 가중치 필요; 모든 테스트는 #[ignore]) |
tests/disaggregated_serving_e2e.rs | 실제 mlxcel-server 프로세스 2개(--node-role prefill과 decode)와 KV 핸드오프 |
tests/disaggregated_router_e2e.rs | disaggregated 라우터 뒤의 실제 mlxcel-server 프로세스 3개 |
tests/zero_config_cluster_init.rs | 제로 설정 멀티 머신 파이프라인 기동 |
tests/speculative_dispatch.rs, tests/speculative_parity.rs | 서버 투기적 디스패치 매트릭스와 드래프터 조합의 greedy 일치 |
tests/paged_*_parity.rs | 페이지드 KV 캐시 일치(스케줄러, 핸드오프, 직렬화, 접두사 공유, 실제 모델) |
tests/prompt_cache_e2e.rs | 요청 간 프롬프트 접두사 KV 캐시 |
tests/cli_help_consistency.rs | 공유 CLI 플래그 그룹의 도움말 일관성 |
tests/architecture_registry_invariants.rs | 공개 API를 통한 레지스트리 키 불변식 |
tests/pipeline_*_real_models.rs, tests/tensor_parallel_real_models.rs, tests/pp_tp_2d_real_models.rs | 실제 체크포인트에서의 파이프라인, 텐서, 2D 병렬 일치(모델 가중치 필요) |
tests/*_parity.rs(모델 패밀리) | 참조 출력과의 패밀리별 일치. 예: apertus_parity.rs, internvl_parity.rs, molmo_parity.rs, plamo2_parity.rs, qwen35_mtp_verify_parity.rs |
tests/test_*.py | 저장소 스크립트용 Python 테스트(품질 게이트, 레시피, 벤치마크 헬퍼) |
분산 테스트가 커버하는 내용¶
분산 E2E 테스트는 프로세스 내 시뮬레이션을 사용하여 실제 하드웨어 없이 전체 스택을 테스트합니다:
TP E2E (tp_e2e.rs): - GQA 인식 샤딩을 위한 어텐션 헤드 할당 - 랭크 전체에 걸친 FFN 중간 차원 커버리지 - MoE 전문가 할당 및 커버리지 검증 - 표준 트랜스포머, MoE, SSM 아키텍처에 대한 샤드 플랜 생성 - 집합 연산 정확성 (all-reduce, all-gather, reduce-scatter) - 프로세스 내 메시지 전달을 사용한 스케줄러/실행기 잠금 단계 시뮬레이션 - 확장성 분석 및 크로스오버 벤치마크
PP E2E (pipeline_e2e.rs): - 레이어 파티션 정확성 (자동 및 수동) - GPipe 스케줄 완료까지 구동 - 마이크로 배치 분할 및 크기 권장 사항 - 파이프라인 코디네이터 요청 생명주기 - 청크 프리필 파이프라인 - 스테이지 헬스 추적 - 스테이지별 캐시 관리 - 확장성 벤치마크
Disaggregated E2E (disaggregated_e2e.rs): - 프리필 스케줄러 수용 제어 및 우선순위 정렬 - 디코드 스케줄러 수집 큐 및 배치 관리 - 프리필 → 디코드 핸드오프 전체에 걸친 요청 라우터 생명주기 - 부하 분산 전략 정확성 - 스트림 브리지 단계 전환 및 토큰 순서 - KV 캐시 직렬화 / 역직렬화 왕복 - 전송 전략 선택 - 엔드-투-엔드 지연 벤치마크 및 크로스오버 분석
분산 엔드-투-엔드 테스트¶
분산 E2E 테스트는 실제 GPU나 네트워크 연결이 필요 없는 프로세스 내 시뮬레이션을 사용합니다. 즉, CI의 어느 머신에서도 실행됩니다.
프로세스 내 집합 연산 시뮬레이션¶
집합 연산은 std::sync::mpsc 채널로 연결된 시뮬레이션된 랭크 그룹을 생성하여 테스트합니다:
// Create loopback collective groups for 4 ranks
fn create_loopback_groups(world_size: usize) -> Vec<CollectiveGroup> {
// Shared mailboxes: mailbox[dest_rank] holds data sent to that rank
let mailboxes: Vec<Mutex<Option<Vec<u8>>>> =
(0..world_size).map(|_| Mutex::new(None)).collect();
let mailboxes = Arc::new(mailboxes);
(0..world_size).map(|rank| {
let mb = Arc::clone(&mailboxes);
let config = CollectiveConfig { rank, world_size, chunk_size: 1024 };
CollectiveGroup::new(config, Arc::new(move |dest, data| {
*mb[dest].lock().unwrap() = Some(data);
// Poll for incoming data...
})).unwrap()
}).collect()
}
E2E 테스트 실행¶
# All tests including distributed E2E
cargo test -- --test-threads=1
# Specific test file
cargo test --test tp_e2e -- --test-threads=1
cargo test --test pipeline_e2e -- --test-threads=1
cargo test --test disaggregated_e2e -- --test-threads=1
# With verbose output
cargo test --test tp_e2e -- --nocapture --test-threads=1
참고: 많은 테스트가 전역 상태(예: 모델 레지스트리 싱글톤, 파일 시스템 픽스처)를 공유하므로 --test-threads=1이 필요합니다.
벤치마킹¶
mlxcel은 Makefile 또는 cargo test로 실행 가능한 분산 컴포넌트에 대한 내부 벤치마크 스위트를 포함합니다.
Makefile 벤치마크 타깃¶
# Run all model benchmarks (text + VLM): requires local model weights
make bench
# Text models only
make bench-text
# VLM models only
make bench-vlm
# Single model
make bench-model MODEL=./models/qwen3-8b
# Show last benchmark results summary
make bench-report
벤치마크 하네스는 표준 프롬프트로 모델을 실행하고 초당 토큰 수를 보고합니다. 결과는 benchmark_results.log에 기록됩니다.
엔진 parity와 B=1 디코드 벤치마크¶
mlxcel generate, mlxcel run, 서버는 모두 같은 엔진(mlxcel_core::engine)으로 디코딩하므로, 같은 요청에 대한 토큰 스트림이 일치해야 합니다. 이를 확인하는 도구가 mlxcel-engine-parity입니다. 프롬프트 하나를 CLI raw-completion client(a:cli), B=1로 in-process 실행한 서버 BatchScheduler의 dense(b:dense)와 paged(c:paged) 디코드 storage, 직접 실행한 Engine(d:engine), mlxcel run -p(e:run), mlxcel-server 기본 구성(f:server, greedy 케이스만)으로 각각 돌리고, 모든 쌍에 대해 identical 또는 처음 갈라지는 토큰을 출력합니다. dense arm에서는 프롬프트 캐시 miss와 hit도 비교하고 양쪽 prefill이 어떻게 나뉘었는지 보고합니다.
cargo build --release --bin mlxcel-engine-parity
MLXCEL_SDPA_DETERMINISTIC=1 ./target/release/mlxcel-engine-parity \
--model models/mlx/qwen3-1.7b-4bit --expect-identical
--expect-identical 없이 실행하면 baseline을 기록하고 0으로 종료하며, 이 옵션을 주면 갈라지는 쌍이 하나라도 있을 때 1로 종료합니다. CUDA의 기본 decode SDPA는 reduction 순서가 재현되지 않으므로 --expect-identical은 MLXCEL_SDPA_DETERMINISTIC=1이 설정되지 않으면 실행을 거부합니다. --cases greedy,seeded, --no-chat-template, --server-prefill-chunk N, --no-prompt-cache-case, --json PATH로 실행 범위를 좁히거나 결과를 기록할 수 있습니다.
mlxcel-bench-engine은 같은 합성 프롬프트로 두 front end의 단일 스트림 디코드를 측정합니다. cli는 mlxcel generate가 실행하는 DirectEngine loop로 mlxcel-bench-decode와 같은 측정 호출이고, server는 미리 토큰화한 id로 model worker와 BatchScheduler를 in-process로 구동하므로 HTTP와 토큰화가 측정 구간 밖에 있습니다. 기본 context는 256토큰과 8192토큰입니다.
cargo build --release --bin mlxcel-bench-engine
./target/release/mlxcel-bench-engine --model models/mlx/qwen3-1.7b-4bit
--prefill-chunk는 두 경로의 청크 크기를 정하고 --decode-storage는 서버 storage backend를 고릅니다. 한 번 실행은 기능 확인용이고, 수치가 필요하면 여러 프로세스를 번갈아 실행하십시오. mlxcel-bench-decode는 Decode path: engine을 출력하며, --temperature, --top-p, --top-k를 받으므로 샘플링 비용까지 profile에 포함할 수 있습니다.
분산 컴포넌트 벤치마크¶
분산 E2E 테스트 파일에는 각 컴포넌트의 처리량과 지연을 측정하는 #[ignore]로 태그된 벤치마크 테스트가 포함되어 있습니다:
# TP collective benchmarks
cargo test --test tp_e2e -- --ignored --nocapture --test-threads=1
# Pipeline benchmarks
cargo test --test pipeline_e2e -- --ignored --nocapture --test-threads=1
# Disaggregated inference benchmarks
cargo test --test disaggregated_e2e -- --ignored --nocapture --test-threads=1
이 벤치마크들은 다음을 측정합니다:
TP 벤치마크: - 텐서 크기 및 월드 크기 대비 all-reduce 처리량 - 잠금 단계 스케줄러/실행기 처리량 (초당 시퀀스) - 크로스오버 분석: TP 오버헤드가 단일 GPU 비용을 초과하는 시점
PP 벤치마크: - 스테이지 수 및 마이크로 배치 수 대비 파이프라인 처리량 - 스케줄 효율성 (버블 비율) - 활성화 전송 대역폭
DI 벤치마크: - 엔드-투-엔드 지연: 요청 제출부터 첫 번째 토큰까지의 시간 - 프리필 스케줄러 처리량 - 디코드 스케줄러 처리량 - 캐시 크기 및 전략에 따른 KV 캐시 전송 시간 - 크로스오버 분석: 분리 오버헤드가 이득을 초과하는 시점
테스트 실행¶
전체 테스트 스위트¶
# Using Make (recommended for development)
make test
# Equivalent cargo command
cargo test -- --test-threads=1
라이브러리 테스트만¶
test-utils 피처 사용¶
일부 테스트는 추가 테스트 헬퍼(모의 전송, 프로세스 내 클러스터 시뮬레이션)를 활성화하기 위해 test-utils 피처 플래그가 필요합니다:
모델 로딩 테스트 (로컬 가중치 필요)¶
TurboQuant KV 캐시 품질 게이트 (로컬 가중치 필요)¶
tests/turbo_kv_e2e.rs는 TurboQuant KV 캐시 압축 경로에서 KVCacheMode::Turbo4Asym의 품질 회귀 예산을 검증합니다.
모델별로 세 개의 독립적 게이트가 실행됩니다:
- Wikitext-2 perplexity 게이트.
tests/fixtures/wikitext2_excerpt.txt의 비중첩 4K-토큰 청크들에 대해 음의 로그 우도(NLL)를 누적한 뒤(ppl_turbo - ppl_baseline) / ppl_baseline ≤ 0.01을 확인합니다. - NIAH 단일 needle 검색 게이트. 4K 컨텍스트에서 9개 (depth, length) 셀 (depth 0%/50%/100% × length 1K/2K/4K)과 8K 컨텍스트의 추가 3개 셀에 매직 워드를 심고 temperature 0에서 32토큰 이내로 생성한 뒤 turbo retrieval 카운트가 baseline 카운트 이상인지 확인합니다.
- 회전 kurtosis 정합성 테스트. 비양자화 K-projection 가중치 텐서를 로드하고
mlxcel_core::cache::turbo::turbo4_v_rotate를 통해 WHT + 부호 반전 회전을 적용한 뒤, 회전 후 비초과 kurtosis가 5 미만인지 확인합니다.
PPL 실행 중 디코드 tok/s, 프리필 tok/s, wall-clock 지연이 기록되지만(게이트는 아님) 문서화와 추세 분석에 사용할 수 있도록 benchmarks/turbo_kv/<YYYY-MM-DD>_<machine>.csv에 추가됩니다.
네 테스트 모두 #[ignore]로 표시되어 있어서 모델 체크아웃이 없는 머신에서는 부드럽게 건너뛰고 wall time을 예측 가능하게 유지하기 위해 게이트별로 개별 실행합니다:
# PPL + NIAH 게이트: Qwen2.5-1.5B (base 변형)
cargo test --test turbo_kv_e2e --release -- --ignored test_qwen25_15b_quality_gate --nocapture
# PPL + NIAH 게이트: Meta-Llama-3.1-8B-Instruct-4bit
cargo test --test turbo_kv_e2e --release -- --ignored test_llama31_8b_quality_gate --nocapture
# PPL + NIAH 게이트: gemma-3-4b-it-4bit
cargo test --test turbo_kv_e2e --release -- --ignored test_gemma3_4b_quality_gate --nocapture
# 회전 kurtosis 정합성 (사용 가능한 모든 모델 사용)
cargo test --test turbo_kv_e2e --release -- --ignored test_rotation_kurtosis_sanity --nocapture
필요한 모델 체크아웃 (필요한 항목만 받으면 해당 테스트가 활성화됩니다):
./target/release/mlxcel download mlx-community/Qwen2.5-1.5B-4bit
./target/release/mlxcel download mlx-community/Meta-Llama-3.1-8B-Instruct-4bit
./target/release/mlxcel download mlx-community/gemma-3-4b-it-4bit
Kurtosis 정합성 테스트는 비양자화 모델(예: qwen2.5-0.5b-bf16)이 필요한데, 4-bit 양자화된 K-projection 가중치는 패킹된 정수로 저장되어 있어서 dequantize 없이는 부동소수로 재해석할 수 없기 때문입니다.
현재 품질 게이트에는 VLM 품질 검사가 포함되지 않습니다.
TurboQuant 모드에 대한 사용자 가이드(CLI 플래그, 권장 구성, 알려진 제한 사항 포함)는 TurboQuant KV 캐시를 참조하십시오.
품질 게이트¶
전체 품질 게이트 스크립트는 형식 확인, clippy, 테스트를 실행합니다:
다음과 동일합니다:
CPU 전용 링크 검사¶
GPU feature 없이 빌드한 Linux 바이너리는 GPU backend 없이 MLX를 컴파일하므로 MLX의 GPU 쪽 helper가 존재하지 않습니다. src/lib/mlxcelverse/kernels/와 src/lib/mlxcel-core/cpp/의 브리지 C++ 코드가 그런 helper를 호출한다면 MLXCEL_BRIDGE_GPU_BACKEND preprocessor define 뒤에 두어야 하며, mlxcel-core/build.rs는 MLX가 Metal, CUDA, ROCm을 빌드할 때만 이 define을 설정합니다. cargo check는 링크하지 않으므로 guard가 빠지면 이 구성의 링크 오류로만 드러납니다. GPU 빌드 cache를 밀어내지 않도록 별도 target 디렉터리로 확인하십시오.
문서 테스트¶
테스트 구성 관례¶
-
테스트를 코드와 함께 배치한다:
#[path = "<module>_tests.rs"]를 사용하여 테스트 파일을 소스 파일 옆에 유지합니다. -
모듈당 하나의 테스트 파일: 단일 파일에 서로 다른 모듈의 테스트를 혼합하지 마십시오.
-
tests/에 통합 테스트: 모듈 경계를 넘거나 전체 스택을 테스트하는 것은tests/에 속합니다. -
하드웨어 의존 테스트는
#[ignore]로 표시: 실제 GPU 하드웨어, 네트워크, 또는 로컬 모델 가중치가 필요한 테스트는 반드시#[ignore]로 표시해야 합니다. 이러한 테스트는 문서화되고 수동으로 실행 가능해야 합니다. -
설명적인 테스트 이름 사용: 테스트 함수 이름은 무엇을 테스트하는지와 예상 결과가 무엇인지를 설명해야 합니다. 예:
test_column_parallel_shard_plan_produces_correct_axis(). -
단위 테스트를 빠르게 유지: 단위 테스트는 밀리초 단위로 완료되어야 합니다. 테스트가 몇 초 이상 걸린다면,
#[ignore]로 표시하고 통합 테스트 스위트로 이동하는 것을 고려하십시오.