어떤 GPU에서든 LLM 배포를 위한 emmy 스택
요약
emmy 스택은 최적화된 컴파일러, 벤치마킹 및 배포 기능을 제공하여 모든 LLM을 어떤 GPU 환경에서도 구동할 수 있게 합니다. 커널 퓨전, 자동 튜닝 등을 통해 추론 성능을 극대화하며, PyTorch 모델을 Graph IR로 변환하고 CUDA 코드를 생성하는 컴파일러 스택을 제시합니다.
핵심 포인트
- emmy는 LLM 배포를 위한 통합 컴파일/벤치마킹/배포 스택입니다.
- 커널 퓨전 및 자동 튜닝으로 추론 성능 최적화가 가능합니다.
- PyTorch 모델을 Graph IR로 변환하여 CUDA 코드를 생성할 수 있습니다.
- 특정 GPU 아키텍처(예: V100)를 지원하기 위해 PyTorch wheel 버전을 명시적으로 관리해야 합니다.
**컴파일 → 벤치마크 → 배포(Deploy)**를 통해 모든 LLM을 모든 GPU에서 구동할 수 있습니다. 최적화된 컴파일러, LLM 벤치마킹 및 배포 스택입니다. 커널 퓨전(kernel fusion), 자동 튜닝(autotuning), 고급 스케줄링을 통해 추론(inference)을 최적화하세요. 블로그 게시물을 확인하세요: Gemma4-12B에서 vLLM (cuBLAS 및 FlashAttention) 능가.
pip install emmy-ml # 추천 레시피들이 번들된 CLI 도구입니다.
emmy --version
컴파일러는 자체적인 추가 라이브러리(extra)가 필요합니다 (pip install "emmy-ml[compile]" — torch, transformers, cppyy). 이 wheel은 커널을 실행하는 런타임 확장 프로그램(emmy.emmy_runtime, crates/emmy-runtime-py에서 빌드됨)을 포함하며, Linux x86_64용입니다. 다른 플랫폼에서는 Rust toolchain이 존재할 때 sdist가 이를 빌드하고, Toolchain 없이도 순수하게 설치하여 GPU가 필요 없는 모든 명령어가 작동하도록 유지합니다. emmy 자체를 수정하려면 대신 클론(clone)하세요:
git clone https://github.com/cloudrift-ai/emmy.git
cd emmy && make setup
커널은 CUDA toolkit의 nvcc로 컴파일되고 Rust 런타임을 통해 실행됩니다 (make setup을 실행하면 venv에 빌드되므로, Rust toolchain이 필수 전제 조건입니다). 사전 Turing GPU(V100 sm_70, P100 sm_60)의 경우, toolkit은 CUDA 12 릴리스여야 합니다. CUDA 13은 sm_75 미만의 모든 아키텍처를 제거했습니다.
torch wheel은 별도의 사전 Turing 트랩(trap)입니다: 기본 +cu130 빌드는 sm_70 커널을 전혀 포함하지 않기 때문에, 모든 정확도 검사 및 모든 --bench-backends eager,tcompile 비교의 참조 측면이 no kernel image is available for execution on the device 오류로 실패합니다. 2.13.0+cu126 빌드는 Volta 커널을 포함합니다:
pip install --force-reinstall "torch==2.13.0+cu126" --index-url https://download.pytorch.org/whl/cu126
+cu126 로컬 버전을 명시적으로 요청하세요. 단순히 torch==2.13.0을 사용하면 이미 설치된 +cu130 wheel과 일치하며, pip은 아무것도 변경하지 않고 요구 사항이 충족되었다고 보고합니다.
원격 하드웨어만 구동하는 명령어들(deploy, bench, vm, teardown, …)은 로컬 toolchain을 건드리지 않으며 해당 호스트에서 계속 작동합니다.
해킹 가능한 PyTorch → Graph IR → CUDA 컴파일러. 모든 nn.Module을 추적하여
하나의 커널로 융합하고, 실행한 다음, 생성된 CUDA를 검사할 수 있습니다. 블로그 게시물을 참조하세요: A Principled ML Compiler Stack in 5,000 Lines of Python.
# 단일 연산 컴파일
emmy compile -c "nn.RMSNorm(2048)(torch.randn(1,32,2048))"
# 로컬 GPU에서 커널 벤치마크
...
Fast math는 기본적으로 활성화되어 있습니다. EMMY_FAST_MATH=0
은 NVCC fast math와 컴파일러의 정밀도 트레이딩 최적화를 비활성화하며, 개별 정밀도 핀이 이 포괄적인 설정을 재정의합니다. 별도의 반올림 정확도 진단을 위해서는 --nvcc-flags=--fmad=false를 추가하세요.
. 이 사용자 지정 플래그는 다른 정책을 변경하지 않으면서 곱셈-덧셈 축소(multiply-add contraction)를 비활성화합니다.
Layer-norm 스타일의 축소(두 번의 축소, 브로드캐스트 뺄셈, 요소별 체인)가 단일 커널로 융합된 예시:
emmy compile -c "
class LN(torch.nn.Module):
def forward(self, x):
...
여섯 개의 IR 단계로 구성된 원칙적인 컴파일 스택이며, 각 단계는 --ir <stage>를 통해 필요할 때 출력 가능합니다.
Torch IR— PyTorch의 연산 집합(rmsnorm, linear, softmax 등)을 1:1 미러링하는 FX 그래프를 포착합니다.Tensor IR— Torch 연산을 일반적인 요소별(elementwise), 축소(reduction), 인덱싱, 값 변환 기본 원시 요소로 분해합니다.Loop IR— 각 기본 원시 요소를 LoopOp으로 끌어올리고 융합하며, Tile IR— 커널을 GPU에 스케줄링합니다.Kernel IR— 스케줄을 프레임워크에 구애받지 않는 하드웨어 기본 원시 요소로 구현합니다.CUDA— nvcc용으로 준비된 최적화된 CUDA 코드입니다.
가독성 높은 스케줄: emmy compile -c "nn.RMSNorm(2048)(torch.randn(1,32,2048))" --ir tile
kernel k_rms_norm_reduce inputs: rms_norm_mean_count, rms_norm_eps, x, p_weight outputs: rms_norm
in0 = load rms_norm_mean_count[0]
in1 = load rms_norm_eps[0]
...
최적화된 CUDA 커널: emmy compile -c "nn.RMSNorm(2048)(torch.randn(1,32,2048))" --ir cuda
extern "C" __global__
__launch_bounds__(256) void k_rms_norm_reduce(const float* x, const float* p_weight, float* rms_norm) {
float in0 = 2048.0f;
...
저장소에는 두 개의 사전(prior)이 포함되어 있으며, 둘 다 작아서 각각 하나의 JSON 파일로 저장된 CatBoost 랭커입니다. 이들은 GPU 없이도 저장소의 goldens에 적합하며, 측정된 것이 없는 경우 컴파일 시에만 사용됩니다: 스케줄 사전 (emmy/compiler/pipeline/search/prior/weights/schedule.json)은 커널의 스케줄 행을 랭크하고, 그 옆의 배치 사전 (weights/placement.json)은 커널 세트 포크의 아밍(arms) — 즉, 커널을 온전히 유지할지, 실밥 중 하나를 자를지, 또는 리덕션을 CTA에 걸쳐 분할할지 — 을 랭크합니다.
# 1. 모든 저장소 golden — 하드웨어 goldens와 각 유지보수 레시피들 — 를 자체 DB로 불러옵니다.
emmy db import --db _data/dataset.db --fresh --repository
# 2. 공간별로 하나의 데이터셋을 내보냅니다: 열거되고 피처화된 모든 golden 풀, 또는 모든 커널 세트 포크의 아밍들
...
emmy fit은 또한 _tune/fits/<timestamp>/ 아래에 메트릭 파일도 작성합니다.
; 두 개의 fit은 그들의 메트릭 파일을 비교하여 비교됩니다.
기존 스케줄 사전을 대체하지 않고 후보를 비교하려면:
emmy fit _data/schedule _data/schedule-candidate.json --folds 0
emmy eval prior _data/schedule \
--offline-file emmy/compiler/pipeline/search/prior/weights/schedule.json \
...
JSON 보고서에는 순위 요약과 비교 결정이 모두 포함됩니다.
Nightly refresh는 저장소 golden으로부터 스케줄 및 배치 사전을 주기적으로 재적합(refits)합니다. 하드웨어 golden을 변경하는 PR은 PR 내에서 재적합하고, 레시피 golden을 추가하는 것은 weights를 nightly에 남겨두고 아래의 복제 게이트가 녹색 상태로 유지됩니다. Nightly refresh는 각 사전을 독립적으로 재적합하고 동일한 데이터셋에서 적합된 가중치와 배포된 가중치를 비교하여, GPU, 티어 및 풀 크기 그룹의 중앙값 golden 랭크가 최소 5% 하락하고 어떤 그룹의 중앙값이 상승하거나 커버리지를 잃지 않을 때만 후보를 main에 직접 커밋합니다. #emmy-robots의 nightly 요약에는 각 결과와 weights가 main에 남게 된 방식이 담겨 있습니다.
pick: how many pools
이전 단계(prior)는 이전처럼 자신들의 황금 기준(golden)을 결정하고, 선택된 항목의 후회(regret)는 측정된 황금 행(golden row)으로 계산합니다 (eval prior --json)
.
황금 순위(Golden rank)는 검증된 행이 어디에 안착했는지 측정하며, 잘못된 선택의 지연 시간(latency)을 측정하는 것이 아닙니다. 아래의 재현 게이트(reproduction gate)는 후보가 확정되기 전에 여전히 실행됩니다. 동일한 실행은 torch.compile 뒤에 있는 모든 황금 및 실현 코퍼스 행과 코퍼스의 예상 실패 사례(emmy golden list)
.
을 나열합니다.
재현 게이트(The reproduction gate). tests/compiler/pipeline/search/prior/test_reproduction.py
은 모든 저장소의 황금 기준에 배포된 이전 단계들을 보관하며, 측정 범위 없이 각 코퍼스와 공간마다 하나의 허용 오차(tolerance)를 가집니다: 커널 세트 포크(kernel-set fork)가 기록한 팔(arm)이 이전 단계의 선택이며, 기록된 스케줄 행은 그 풀(pool)에서 이전 단계가 지정하는 대로 드로우되는 것 중 더 나은 절반에 위치합니다—이는 스케줄 이전 단계가 개선됨에 따라 타이트해지는 기준선입니다 (중앙값 황금 값은 4%에 위치합니다).
모든 저장소의 황금 값은 make test에서 실행되며, 그 풀들은 16 단위로 슬라이스되어 작업이 워커들 위로 분산됩니다: 하나의 노드는 한 공간에서의 하나의 황금 값을 담고 있으며, 해당 슬라이스에 대한 허용 오차를 가집니다; 스케줄 절반은 테스트 세트에서 그리디 컴파일(greedy compile)이 하는 만큼의 행을 풀당 드로우합니다 (EMMY_POOL_DRAW (여기서는 512)). 빨간색 노드는 이전 단계가 재현할 수 없는 행들을 나타내며, 이 게이트는 모든 PR(Pull Request)에서 녹색으로 유지됩니다. 하드웨어 황금 값은 엄격하게 관리됩니다: 하나의 리핏(refit)이 이전 단계를 변경하면 그에 맞춰 이전 단계들이 수정되어 이를 재현합니다. 배포된 이전 단계가 재현하지 못하는 레시피 황금 값은 리핏되거나, 해당 레시피는 prior-pending 태그가 지정됩니다: 이 경우 게이트는 해당 황금 값을 건너뛰며, 이는 리핏을 통해 재현되고 태그가 제거될 때까지 증거 및 훈련 데이터로 남아 있습니다. 절대 허용 오차를 낮추지 마십시오.
emmy bench experiments/gemma-4-12B/* # 모든 Gemma 실험
emmy bench experiments/gemma-4-12B/gsm8k_mtp_rtx5090 # 단일 실험
emmy bench experiments/gemma-4-12B/* --filter
각 실제 실행은 타임스탬프가 찍힌 `raw-results` 디렉터리를 생성하고 매트릭스 행당 하나의 시스템 전용 YAML 실험 기록을 작성합니다. 실험을 실행하거나 사용자 정의하려면 `$run-experiment`를 사용하십시오. 플랫폼의 LFS 기반 `results_<gpu-short>x<gpu-count>.tar.gz` 파일을 대체하고, 포함된 기록을 검증하며, 공유되는 `RESULTS.md` 해석을 업데이트한 다음, 영구적인 플랫폼 스냅샷을 커밋합니다. 기록들은 최상위 실험 파일이 아닌 아카이브 내부에 남아 있습니다. 다른 플랫폼 스냅샷은 변경되지 않습니다. 러너와 실험 코드는 측정을 절대 해석하지 않으며, 스킬(skill)이 원시 증거를 검토합니다. 타임스탬프가 찍힌 디렉터리는 아카이브가 추출되거나 원시 파일과 바이트 체크된 후에는 무시하고 삭제할 수 있습니다.
SSH를 통한 원격 서버 접속
emmy deploy ssh --recipe recipes/gemma-4-12B-it --ssh user@host
여러 모델이 GPU에 있는 원격 서버 (recipe 대신 계획 파일 사용)
...
`--recipe`는 단순한 레시피 이름(`--recipe gemma-4-12B-it`)도 받습니다. 편집 가능한 설치(editable install)는 라이브 체크아웃에서 이를 해결하고, 휠 설치(wheel install)는 패키징된 카탈로그에서 이를 해결합니다. Emmy는 `deploy`가 그 옆에 컴포즈 파일(compose file)을 작성하고 `bench`가 타임스탬프가 찍힌 실행 디렉터리를 작성하기 때문에 먼저 레시피를 현재 디렉터리로 복사합니다. 항상 존재하는 경로가 우선하므로, 편집된 작업 사본은 절대 덮어쓰여지지 않습니다.
계획(plan)은 임대할 VM(`gpu`, `gpu_count`)과 `recipe`를 가진 `models`의 순서 지정 목록, 각 모델별 `gpu_memory_utilization` 및 고정되는 `gpu_device_ids`를 명시합니다. 모델 *i*는 호스트 포트 `8000 + i`에서 자체 컨테이너로 실행되며; GPU를 공유하는 모델들은 순차적으로 시작됩니다. `--result-json`은 성공 시 모델당 하나의 엔드포인트를 작성하고, `--lease`는 VM이 임대되는 순간 인스턴스 ID를 영구 저장합니다. `deploy` 명령어 참조에 자세한 내용이 있습니다.
서빙 레시피는 표준 불변 이미지 참조를 고정합니다. 게시 승인을 요청하기 전에 로컬 이미지, 그 출처 레이블(provenance labels), 그리고 레지스트리 충돌을 검증하세요. 그런 다음 로그인하여 푸시를 수행합니다:
emmy publish recipes/DeepSeek-V4-Flash-0731 --dry-run
emmy publish recipes/DeepSeek-V4-Flash-0731 --source-image local-baked-image --yes
게시된 참조는 `cloudriftai/<runtime-family>-<model-slug>:<runtime-version>-<source-sha>`를 사용합니다.
; 릴리스 게이트 및 레이블은 prebuilt-serving-image 아키텍처를 참고하세요.
vLLM의 OpenAI 쉘 (/v1/embeddings, tokenizer, scheduler, pooler)을 emmy 컴파일된 커널 위에 구현
emmy serve Qwen/Qwen3-Embedding-0.6B
curl localhost:8000/v1/embeddings -H 'Content-Type: application/json'
...
생성형 vLLM 러너의 경우, `--runner generate --dtype bfloat16`을 전달하여 밀집 모델(dense model)의 컴파일된 트렁크를 BF16으로 유지하세요. 생성형 기본값은 FP16입니다.
FP16 또는 FP8의 밀집 Qwen3 또는 Qwen3.5 텍스트 모델(FP8 트렁크는 코딩만 되고 가중치만 사용됨)을 독립적인 아티팩트로 준비하여 Rust 캐시 생성 루프를 통해 실행할 수 있습니다. 이 단일 요청 경로는 그리디(greedy) 또는 시드된(seeded) 온도/top-p 샘플링과 선택적 CUDA 그래프를 지원하며, 옵션이 있습니다. 출력 로짓(logits), 잔여값(residuals), 어텐션/로터리 중간 값은 FP32를 사용합니다. 프리필(Prefill)은 고정 폭 청크를 사용하며, vLLM이 서빙 기본값으로 유지됩니다. 준비, 명령어, 제한 사항 및 자격 요건에 대해서는 네이티브 생성 계약(native generation contract)을 참고하세요.
make native-dist # PATH에 아카이브의 일치하는 바이너리 설치
emmy serve Qwen/Qwen3-0.6B --runner generate --native --revision REVISION
네이티브 서빙은 스트리밍, 중지 문자열(stop strings), 사용량, 그리고 하나의 활성 요청을 통해 텍스트 및 채팅 완료를 노출합니다.
편집 가능한 설치를 위해 라이브 소스 레시피를 자동으로 검사하거나,
설치된 wheel에 번들링된 실행 가능한 레시피를 검사합니다.
emmy recipe list --json
...
`recipe list --json`
이는 버전이 지정된 머신 인터페이스(스키마 버전 2)입니다. 이 인터페이스는 `schema_version`과 `recipes`를 가진 객체를 반환합니다. 각 레시피는 디렉터리 이름(`name`), 모델 ID, 작업, 라이프사이클 인식 `runnable` 상태, 발열 점수, 그리고 GPU, GPU 개수, GPU 메모리 분율 및 유효 컨텍스트 길이를 포함하는 행렬 확장 배포를 가집니다. 분율만 다른 두 항목 모두 나열됩니다. 즉, 레시피가 자신의 GPU를 공유할 수 있음을 선언하는 방식입니다. 소비자들은 알 수 없는 스키마 버전을 거부해야 합니다. 스키마 버전에는 필드가 추가될 수 있지만, 기존 필드는 제거되거나 재정의되지 않습니다. Emmy는 항상 설치를 감지합니다: 편집 가능한 체크아웃은 라이브 최상위 `recipes/` 디렉터리를 사용하고, 일반 휠(wheel)은 패키징된 runnable 레시피 번들을 사용합니다.
`recipe query --json`은 범용 술어 및 안정 정렬 키를 위한 별도의 버전이 지정된 `rows` 인터페이스를 반환합니다. 선택적 `--candidate MODEL GPU COUNT`
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub ML Hardware의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기