나는 $130 채굴 카드에서 Qwen3.5-27B를 실행하기 위해 맞춤형 CUDA 추론 엔진을 작성했다
Source: Dev.to
Overview
나는 중고 시장에서 NVIDIA CMP 100‑210 카드를 한 장당 약 $130에 네 장 구입했다.
이들은 Volta GV100 다이 기반의 채굴 전용 카드이며, V100과 동일한 실리콘을 사용하고, 각각 16 GB HBM2를 탑재하고 있다. 명세상으로는 네 장을 합쳐 64 GB HBM2를 단일 중고 RTX 3090 한 대 가격에 얻을 수 있다.
실제로 NVIDIA는 하드웨어 수준에서 이들을 제한했다.
스로틀
- Tensor‑core 스로틀링: 64× 속도 저하.
- HMMA 지연이 8 cycles → 512 cycles 로 늘어남.
cuBLASWMMA는 ≈ 5 TFLOP per card 로 제한됨.
- PCIe: Gen1 ×1 로 고정됨.
- P2P 없음, NVLink 없음.
- CUPTI: 차단 → NVIDIA 프로파일러 사용 불가.
스루틀은 다이 상의 e‑fuse + PMU 부트‑ROM 이중 잠금으로 강제 적용됩니다 – 펌웨어 스위치가 아닌 하드웨어 수준의 잠금입니다. 소프트웨어 언락은 존재하지 않음 (시도해 봤음).
결과: cuBLAS 텐서 코어를 통하는 모든 작업이 1/64 속도로 실행되거나 즉시 실패합니다. 포함되는 항목:
- vLLM
llama.cpp의 기본cuBLAS경로- FlashAttention
- bitsandbytes
- PyTorch의 기본
matmul
따라서 표준 LLM 추론 스택은 이 하드웨어에서 사용 불가합니다.
우회 방법
Only the tensor cores are throttled. Two other execution paths on the same chip run at full speed:
| 경로 | 설명 | 성능 | 스로틀 |
|---|---|---|---|
| DP4A | 4‑방향 패킹된 int8 점곱 | ≈ 17 TFLOP | 없음 |
| HFMA2 | 2‑방향 패킹된 fp16 융합 곱셈‑덧셈 | ≈ 24 TFLOP | 없음 |
두 경우 모두 정상적인 V100의 텐서 코어와는 일치하지 않지만, 두 경우 모두 5 TFLOP cuBLAS WMMA 한계치를 훨씬 초과합니다. 모든 추론을 이 두 경로로 라우팅함으로써 제한되지 않은 V100 성능의 약 50 %를 회복할 수 있습니다 – 여전히 아무것도 하지 않는 것보다 훨씬 좋습니다.
qengine 소개
qengine은 Qwen 3.5 / Qwen 3.6 하이브리드 모델용 처음부터 만든 CUDA 추론 엔진입니다. (참고: Qwen 3.5/3.6은 dense GDN + Attention 아키텍처를 사용하며, 순수 트랜스포머가 아니기 때문에 커널이 다릅니다.)
특징
- 프리필을 위한 Hand‑written Q8_0 GEMM 타일 경로 – 전부 DP4A.
- Fused FlashAttention 커널 (score + softmax + value) – 단일 패스.
- 긴 컨텍스트를 위한 Split‑K FlashAttention.
- 3‑bit Walsh‑Hadamard + Lloyd‑Max KV 캐시 → 27 B 모델이 256 K 컨텍스트를 세 개의 16 GB 카드에 맞출 수 있음.
- OpenAI‑호환 HTTP API (스트리밍, 툴 호출, 비전, 연속 배칭, 슬롯별 프리픽스 캐시 지원).
모든 커널은 sm_70(CMP 제약)용으로 작성되었으며 기존 라이브러리의 포크가 아닙니다.
정직한 벤치마크
Comparison: qengine vs. llama.cpp (빌드 8462, -fa 1, 동일한 Q8_0 GGUFs, 동일한 하드웨어). 숫자가 클수록 좋습니다.
Prefill
| 모델 / 프리필 길이 | 토큰 / 초 (qengine) | 토큰 / 초 (llama.cpp) | 속도 향상 |
|---|---|---|---|
| Qwen 3.5‑9B – 297 tokens | 594 | 199 | 2.99× |
| Qwen 3.5‑9B – 1.16K tokens | 683 | 316 | 2.16× |
| Qwen 3.5‑9B – 4.62K tokens | 584 | 361 | 1.62× |
| Qwen 3.5‑9B – 18K tokens | 393 | 324 | 1.22× |
qengine은 처음 세 길이에서 앞서며 18 K에서 동등한 성능을 보입니다.
Generation Throughput
| 모델 | 토큰 / 초 (qengine) | 토큰 / 초 (llama.cpp) | 향상 |
|---|---|---|---|
| 9 B | ≈ 70 | 46.6 | +48 % |
| 27 B | 26.3 | 17.7 | +51 % |
약점: 9 B 듀얼‑GPU에서 18 K는 여전히 llama.cpp보다 뒤처집니다 (~0.48×). 그 이유는 llama.cpp가 활성화 전송을 연산과 겹쳐 수행하는 반면, qengine은 고정된 호스트 메모리를 통해 순차적으로 전송해야 하기 때문입니다 (P2P 없음). 싱글‑GPU 9 B는 이미 듀얼‑GPU 실행보다 빠르므로 차이는 대부분 이론적인 것입니다.
구현 과제
1. Multi‑GPU without P2P
- CMP 카드들은 peer‑to‑peer와 NVLink를 지원하지 않는다.
- 숨겨진 상태는 pinned host memory를 통해 GPU 간에 왕복해야 한다.
- 해결책: 교차‑GPU 엣지당 하나의 pinned‑host 버퍼와 GPU당 하나의 워커 스레드를 배치한다. 작동은 하지만 순차적으로 진행된다.
2. Numerical Drift Killing Korean Output
- Qwen 3.5‑9B의 한국어 회로는 약해서, 작은 fp16 재정렬 노이즈가 argmax 결정을 뒤바꿔서 한국어가 뒤죽박죽이 된다.
- 영어 테스트를 통과한 후에 수행한 chunked‑prefill 커널 최적화가 한국어를 깨뜨렸다.
- 해결책: attention‑reduction 순서를 건드리는 모든 커널이 Korean argmax‑stability check를 수행하도록 하여, 출력을 전송하기 전에 검증한다.
3. Split‑K FlashAttention without Breaking Determinism
- 기존 64‑block FA 그리드는 긴 컨텍스트에서 SM을 충분히 활용하지 못했다 (3 × 68 SM = 204 중 64 블록만 사용).
- 각
(kv_head, t_idx)를N개의 독립 블록에 매핑하고, 각 블록이 연속 타일 범위를 처리하도록 하는 split‑K 변형을 추가했다. - 부분 결과를 log‑sum‑exp 항등식을 이용해 병합한다:
m_global = max_s m_s
l_global = Σ_s exp(m_s − m_global) · l_s
o_global = Σ_s exp(m_s − m_global) · acc_o_s
- 첫 번째 버전은 부분
o누산기를 half로 저장했는데, 약 31개의 토큰을 생성한 뒤에 drift가 발생해 (비비트 정확) 결과가 달라졌다. - 부분 누산기를 fp32로 저장하면 drift가 fp32‑재정렬 노이즈 수준(~1e‑7 per add)으로 감소해, greedy argmax가 32개 이상의 토큰에서도 안정적으로 유지된다.
- 결과: 18 K prefill 속도가 270 → 393 t/s (9 B)와 104 → 139 t/s (27 B)로 향상되었다.
4. Speculative Decoding (still broken)
- 레포에는 미래에 미세 조정된 drafter를 위한 DFlash + DDTree 코드가 포함돼 있다.
- 사전 학습된 drafter (
lucebox‑hub/dflash)는 기본 Qwen 3.5에 대해 학습됐으며, 우리 distill 출력 분포와 불일치해 accept rate가 ≈ 0 %에 머물고 체인이 붕괴된다. - README에 broken on purpose라고 명시돼 있다.
- MTP K=1 단일 토큰 spec은 정상적으로 동작한다.
qengine을(를) 사용해야 하는 경우
| 상황 | 추천 |
|---|---|
| RTX 30/40‑series, A100, H100 | vLLM 또는 SGLang을 사용하세요 – 훨씬 더 최적화되어 있고 테스트 커버리지가 광범위합니다. |
| Ex‑mining cards (CMP 100‑210, ex‑mining V100, P104‑100, 등) | qengine이 유용할 수 있습니다. |
| Older Volta workstations (V100 16/32 GB, Titan V, Quadro GV100) | qengine이 작동합니다 (sm_70을 목표로 함). |
| T4 또는 RTX 20‑series에서 표준 스택이 실망스러울 때 | qengine이 도움이 될 수 있습니다. |
DP4A가 없는 GPU (예: sm_60) | 지원되지 않음. |
| AMD / Apple GPU | 지원되지 않음. |
qengine은 **sm_70**을 구체적으로 목표로 합니다. sm_75도 작동할 수 있지만 최적화되지 않았으며, sm_60은 DP4A가 없어 실행할 수 없습니다.
Silicon Definitely Won’t Work
Repo
https://github.com/Haru-neo/qengine — Apache 2.0
이 게시물의 벤치마크는 저장소에 있는 bench_curl.sh 스크립트를 사용하여 재현할 수 있습니다. 27 B 3‑GPU 수치는
- 내 컴퓨터에서 2026‑05‑03에 측정되었습니다.
- 하드웨어가 있다면 직접 시도해 보시고, 결과를 알려주시면 감사하겠습니다.
프로젝트 세부 사항
- 단독 프로젝트.
- CUDA에 대한 AI 지원이 많이 필요했습니다 — 저는 아키텍처 설계, 프로파일링, 디버깅을 여러 세션에 걸쳐 주도했으며, Claude가 대부분의 커널 구현을 담당했습니다.
- 저는 한국 고등학생입니다.
- PR(풀 리퀘스트) 처리 속도가 느립니다.