RTX 3080 언락 성능 비교 보고
통제된 stock vs unlocked A/B 호환성 보고서: Debian 13 / Proxmox VE에서 220 W로 전력 제한한 2x 수정된 RTX 3080 20 GB(GA102, 10de:2206)를 대상으로 dense GEMM, 수치 정합성, 실제 LLM 추론(장문맥 prefill, TTFT, decode)을 검증했습니다.
결과: 두 카드 모두 gpu_unlock: unlocked를 기록했고, NVIDIA 드라이버는 정상적으로 바인딩되었으며, unlocked 부팅과 이후의 stock 부팅 커널 로그에서 Xid/NVRM 오류를 발견하지 못했습니다(첫 번째 stock 부팅의 로그는 확인하지 않았습니다. Caveats 참조). 스크립트, 입력, 시드는 양쪽 모두 동일했습니다.
요약
| 워크로드(220 W 제한) | 기본값 | 잠금 해제 | 변경 |
|---|
| Dense GEMM FP16 (8192², FP32 acc.) | ~58 TFLOPS | ~88-90 | +52-54% |
|---|---|---|---|
| Dense GEMM BF16 | ~59 | ~92-94 | +56-59% |
| Dense GEMM TF32 | ~28.8 | ~43-44 | +49-53% |
| INT8 GEMM (tensor-core path) | ~158-160 TOPS | ~155-160 | 변동 없음 |
| LLM prefill, vLLM 0.30 (HyperQwen) | 8K, 32K 및 128K에서 +33-36% | ||
| LLM prefill, Strata (GGUF engine) | 8K에서 +0.5-0.8% (노이즈 범위 내), 32K 및 128K에서 +12-13% | ||
| LLM TTFT | -25-26% (vLLM); 32K/128K에서 -11-12%, 8K에서는 없음 (Strata) | ||
| LLM decode | 어느 엔진에서도 눈에 띄는 영향 없음 (-4.7%에서 +4.7%까지, 실행 간 편차 범위 내) |
수치 결과는 stock과 unlocked 사이에서 bit-identical입니다(아래 세부 내용 참조).
시스템
- GPU: 메모리 개조된 GeForce RTX 3080 20 GB 2개,
10de:2206, 서브시스템10de:146d,0000:03:00.0및0000:04:00.0. PCIe Gen3 x16. 두 장치 모두 기능 수준 리셋이 광고됨. - 호스트: Xeon E5-2696 v4, X99 보드(MACHINIST X99-MR9A PRO MAX), UEFI + GRUB, Secure Boot 비활성화.
- OS: Debian 13.6, Proxmox VE 9.2.18, 커널
7.0.14-16-pve. - NVIDIA 드라이버:
615.71.09(proprietary, DKMS), CUDA UMD 13.4. - 전력: 양쪽 카드 모두 양쪽 arm에서
nvidia-smi -pl 220적용(매 실행 시작 시 검증). - Cmdline:
... quiet intel_iommu=on iommu=pt ... gpu_unlock.targets=0000:03:00.0,0000:04:00.0 - gpu-unlock:
258f38a(당시main최신),make deb로 빌드하여gpu-unlock-dkms+gpu-unlock-firmware로 설치. DKMS는 설치된 세 개의 커널 모두에 대해 빌드됨. - GPU는 LXC 컨테이너에 공유되어 있으며, 모든 벤치마크는 LXC 내부의 Docker에서 실행되었습니다. unlock 자체는 호스트에서 수행되었습니다.
설치 메모
debhelper/dh-dkms가 설치되어 있지 않았습니다.apt-get build-dep에 해당하는 것은debhelper dh-dkms였습니다(제어 파일의dh-sequence-dkms는 Debian 13에서dh-dkms가 제공하는 가상 패키지입니다).- Ubuntu/initramfs-tools: nvidia.ko in the initramfs binds the card before gpu_unlock loads (unlock fails with -EBUSY) #11의 초기 init-top 훅을 추가했습니다(
/etc/initramfs-tools/scripts/init-top/gpu-unlock에서 udev보다 먼저modprobe gpu_unlock실행). 이 호스트에서는 아마 필요하지 않습니다. initramfs에nvidia.ko가 들어 있지 않기 때문입니다. 재빌드한 initramfs에gpu_unlock.ko와 다섯 개의 firmware 파일이 모두 포함되어 있으며, 스크립트가init-top/ORDER에서udev보다 앞서 정렬되는 것도 확인했습니다. - 두 카드가 같은 부팅에서 모두 unlocked 되었습니다. 로그에는 카드
03:00.0에서Falcon pre-reset timeout tolerated per HW bug 3419321도 포함되어 있는데, 이는 정보성 메시지로 보입니다. - stock arm을 얻기 위해
gpu_unlock.targets=인자만 제거하고 전원을 껐다 켰습니다. 모듈은 타깃 없이도 initramfs에서 계속 로드되지만 아무 일도 하지 않으며(로그에unlocked라인이 0개), 따라서 패키지를 제거할 필요는 없었습니다.
Dense GEMM, 8192x8192 (#12의 프로토콜을 따르며, 아래에 적은 차이점 제외)
PyTorch 2.13.0+cu130, 창당 5회 워밍업 + 20회 torch.mm, 5개 창, 중앙값 TFLOPS(벽시계 시간; CUDA 이벤트 타이밍도 0.1% 이내로 일치).
| Precision | GPU 0 stock -> unlocked | GPU 1 stock -> unlocked |
|---|
| FP16 | 58.0 -> 88.2 (+52%) | 58.4 -> 89.6 (+54%) |
|---|---|---|
| BF16 | 59.1 -> 93.8 (+59%) | 59.0 -> 92.2 (+56%) |
| TF32 | 28.8 -> 42.9 (+49%) | 28.7 -> 43.8 (+53%) |
stock 창들은 서로 1-2% 이내이므로, 성능 향상은 노이즈 범위를 확실히 벗어납니다. 지속적인 교대 FP16/BF16 스트레스(각 GPU당 11,566회 반복, #12와 동일)에서 unlock 적용 시 평균 56 -> 84-86 TFLOPS였고, 더 뜨거운 카드의 최고 온도는 66 C, 평균 전력은 약 211 W였으며, 비정상(non-finite) 출력은 없었습니다.
220 W 제한은 향상의 일부를 깎아 먹습니다. 그 지속 스트레스 동안 SM 클럭은 stock에서는 평균 약 1,720 MHz였지만 unlocked에서는 약 1,330-1,365 MHz에 그쳤습니다. MHz 기준으로 정규화하면 개선 폭은 대략 1.9배로, "이슈 처리율이 두 배"라는 설명과 일치합니다. 더 높은 전력 제한은 테스트하지 않았습니다.
수치 정합성
- FP64 CPU 기준값과 비교했을 때(전체 8192 출력 중 16x16 샘플과 전체 512x512 곱셈 포함): RMS 상대 오차는 FP16 2.1e-4, BF16 1.7e-3, TF32 3.4e-4입니다. stock과 unlocked 양쪽 모두 출력된 모든 자릿수가 완전히 동일합니다.
- 샘플 출력, 512x512 출력, 기준값은 모두 두 GPU에서 stock과 unlocked 사이에 완전히 동일한 SHA-256 해시를 가집니다.
- 모든 전체 출력에서 NaN 0개 / Inf 0개입니다.
A @ I == A: FP16과 BF16에서는 정확합니다. TF32에서는 TF32로 표현 가능한 입력에 대해서는 정확하며, 일반 float32 입력의 경우 최대 0.002까지 차이가 납니다. 이는 TF32 입력 반올림 때문이며 두 arm에서 동일합니다.- TF32 커널 이름(
cutlass_80_tensorop_s1688gemm_*)을 확인했으므로, 여기의 "TF32" 수치는 스칼라 FP32 폴백이 아니라 텐서 코어 TF32입니다.
INT8
torch._int_mm(int8 x int8 -> int32, private API): 두 arm 모두 약 158-160 TOPS이며 변화 없음, int64 CPU 기준값과 정확히 일치합니다. 이 커널이 SM80 텐서 코어 커널(cutlass_80_tensorop_i16832gemm_s8)에 도달하려면 B가 column-major여야 합니다. PyTorch의 기본 레이아웃은 두 arm 모두 약 36 TOPS의 레거시 비텐서코어 커널로 폴백합니다.
NVDEC 동시 실행
GPU당 3,093회의 GEMM 반복을 1080p60 H.264 하드웨어 디코딩 반복 루프(h264_cuvid)와 함께 실행했을 때: 두 arm 모두 드롭된 프레임 0개, 중복 프레임 0개, 약 60.5 fps였습니다. unlocked 실행은 동일한 GEMM 작업을 약 28% 더 빨리 끝냈습니다. 이는 헤드리스(null sink) 환경이므로 데스크톱의 부드러움에 대해서는 아무 말도 하지 않습니다.
LLM 추론
HyperQwen(vLLM 0.30, dbirks/Qwen3.8-27B-W4A16-AutoRound, W4A16 Marlin), 두 카드에 걸친 tensor-parallel, MTP speculative decoding, int8 KV cache, 262k 컨텍스트, 두 arm에서 동일한 설정.
매트릭스: 8K / 32K / 128K 입력 토큰 x 동시 스트림 1개와 2개, 셀당 5회 반복, 출력 토큰 384개, temperature 0, thinking off. 프롬프트는 두 arm에서 동일하며, 모든 요청은 고유한 cache_salt를 사용했고 vLLM의 히스토그램에서 캐시된 토큰 0개가 확인되었습니다. 두 arm 모두 30/30 그룹이 유효했습니다.
Prefill, pooled engine rate(프롬프트 토큰 / 엔진 prefill 초, tok/s):
| 문맥 | 스트림 | 재고 | 잠금 해제됨 | 변경 |
|---|
| 8K | 1 | 1,450 | 1,933 | +33% |
|---|---|---|---|---|
| 8K | 2 | 795 | 1,061 | +34% |
| 32K | 1 | 1,323 | 1,773 | +34% |
| 32K | 2 | 690 | 927 | +34% |
| 128K | 1 | 973 | 1,321 | +36% |
| 128K | 2 | 512 | 686 | +34% |
스트림당 TTFT(초): 5.7 -> 4.3, 10.6 -> 7.9, 24.9 -> 18.6, 47.7 -> 35.6, 135 -> 99.6, 257 -> 192(같은 셀 순서, 모든 셀에서 -25%~ -26%). 모든 셀에서 5회 반복의 최소-최대 범위가 두 arm 사이에 겹치지 않습니다.
Decode(스트림당 tok/s, streamed token IDs로 추정): 122.5 -> 123.5, 94.7 -> 98.5, 115.4 -> 119.1, 85.8 -> 89.8, 105.9 -> 106.4, 71.0 -> 74.0. stock과 unlocked의 범위는 모든 셀에서 겹치므로, 저는 이것을 개선이라고 보지 않습니다.
엔드투엔드 완료 속도(prefill 포함)는 8K에서 +18%, 128K에서 +34%로 개선되는데, 이는 각 요청에서 prefill이 차지하는 비중이 더 커지기 때문입니다.
두 번째 엔진: Strata
같은 카드, 같은 220 W 제한, 같은 매트릭스(8K / 32K / 128K x 스트림 1개와 2개, 5회 반복, 출력 토큰 384개, 두 arm 모두 30/30 그룹 유효). 모든 요청은 고유한 프롬프트 접두사를 사용했고, 서버가 캐시된 프롬프트 토큰을 보고하면 해당 요청은 거부했습니다. 이것은 Strata로, Qwen3.8 Flash-Next UD-Q4_K_XL을 실행하는 GGUF 엔진입니다(두 카드에 걸쳐 layer-split, resident experts, batch MTP, int8 KV). vLLM과 달리 요청별 엔진 prefill 및 decode 타이밍을 보고합니다.
Prefill, 스트림당 엔진 tok/s(5회 반복의 중앙값, 2개 스트림의 경우 각 반복에서 두 스트림의 중앙값):
| 문맥 | 스트림 | 재고 | 잠금 해제됨 | 변경 |
|---|
| 8K | 1 | 1,342 | 1,353 | +0.8% (범위가 겹침) |
|---|---|---|---|---|
| 8K | 2 | 1,346 | 1,353 | +0.5% (범위가 겹침) |
| 32K | 1 | 2,276 | 2,573 | +13.0% |
| 32K | 2 | 1,876 | 2,128 | +13.4% |
| 128K | 1 | 2,938 | 3,299 | +12.3% |
| 128K | 2 | 2,211 | 2,488 | +12.5% |
스트림당 TTFT(초): 8K 6.15 -> 6.11(1개 스트림) 및 12.39 -> 12.28(2개 스트림)으로 노이즈 범위 내입니다. 32K 14.51 -> 12.82 및 32.98 -> 29.05(-11.6% / -11.9%); 128K 44.82 -> 39.94 및 111.82 -> 99.20(-10.9% / -11.3%). 32K와 128K에서는 5회 반복 범위가 두 arm 사이에서 겹치지 않습니다.
Decode, 스트림당 엔진 tok/s(stock -> unlocked): 80.6 -> 76.8, 39.2 -> 39.8(8K); 76.9 -> 80.1, 38.5 -> 37.6(32K); 79.6 -> 78.2, 43.2 -> 45.1(128K). 변화폭은 -4.7%에서 +4.5%까지이며, 모든 셀에서 변화는 한 arm 내부의 분산보다 크지 않습니다(128K에서 2개 스트림일 때는 최소-최대 범위가 딱 갈라지지만, stock arm 자체의 분산보다 작습니다). 따라서 decode 효과는 보이지 않습니다.
위의 vLLM과 비교하면: Strata는 8K에서 prefill 변화가 전혀 없고 32K/128K에서 약 +12-13%인데 반해, vLLM/Marlin은 모든 구간에서 약 +33%입니다. 따라서 이 이득은 엔진과 그 커널에 크게 좌우되며, 짧은 프롬프트에서는 아무 효과도 없을 수 있습니다.
주의 사항
- 부트 1회당 워크로드 1회이며, 5번의 반복은 각 부트 내부의 복제 실행일 뿐 독립적인 부트가 아닙니다.
- 워크로드마다 암(arm) 순서가 달랐습니다. Dense GEMM, INT8, NVDEC, 그리고 vLLM 행렬은 먼저 잠금 해제 상태로 실행한 뒤 전원 순환 후 stock으로 실행했습니다. Strata는 반대 순서였습니다. 먼저 stock으로 실행했는데, 이때 호스트는 이미 한동안 켜져 있던 상태였고 막 전원 순환된 상태는 아니었습니다. 그다음 재시작 후 잠금 해제 상태로 실행했습니다. 향상은 두 순서 모두에서 나타나지만, 암 순서, 부트 경과 시간, 열 이력은 무작위화되거나 통제되지 않았고, 첫 stock 부트의 커널 로그에서 Xid 오류도 확인하지 않았습니다.
- vLLM prefill은 요청별 수치가 아니라 풀링된 값(prompt tokens / summed engine prefill seconds)이며, vLLM decode는 스트리밍된 토큰 ID로부터 추정한 값이므로 둘 다 근사치입니다. Strata의 수치는 요청별 엔진 타이밍입니다.
- 호환성 보고서: Arch Linux, RTX 3070 (ASUS Dual OC)에서 작동 - 70-73 TFLOPS + 수치적 정합성 및 S3 Suspend 분석 #12 차이점: 입력은 CPU에서 FP64로 생성한 뒤 대상 dtype으로 양자화합니다(따라서 참조값은 입력 반올림 오차와 matmul 오차를 분리할 수 있습니다). TF32는 명시적으로 활성화하고, reduced-precision reductions는 비활성화하며,
perf_counter와 CUDA events로 시간을 측정합니다. 제가 사용하는 수치적 경계는 공개된 허용 오차가 아니라 제 자체적인 건전성 검사 기준입니다. - 테스트하지 않음: 220 W를 초과하는 전력 제한, S3 suspend, 데스크톱 사용, 장기 안정성.
README나 공유 호환성 목록에 유용하다면 벤치마크 스크립트(dense GEMM + parity + stress, INT8, NVDEC overlap, 그리고 vLLM six-cell harness)와 원시 per-request JSON을 공유할 수 있습니다. 스크립트는 stdlib/PyTorch만 사용합니다.
출처
- 원문: https://github.com/Zagitta/gpu-unlock/issues/15 (2026-10-25 · github.com)