같은 페이지 · 같은 GPU · 같은 가중치 · 숫자마다 어댑터
유사 솔루션 실측 비교
오늘 브라우저에서 WebGPU 로 신경망을 학습하는 라이브러리는 셋 — TF.js, jax-js, Burn — 이고, 추론만 하는 런타임이 하나, ONNX Runtime Web 이다. 이 페이지는 그 넷을 borch.ts 와 같은 페이지, 같은 GPU 에서, 같은 시드 데이터 또는 torch 에서 내보낸 같은 가중치로 돌리고, 숫자 아래마다 어댑터를 찍는다. borch 가 지는 행은 남기고, 모든 표를 재현하는 명령은 맨 아래에 있다. Chrome 과 WebGPU 가 필요하다. 숫자는 그날 그 카드의 것이고, run 이 움직일 때 움직인다.
학습 — ResNet-18 (CIFAR), 스텝당 ms
같은 구조, SGD 0.05/0.9, 교차엔트로피, 같은 시드 픽셀; 워밍업 2회 뒤 5회 측정, 매 스텝 손실 리드백을 넣어 GPU 가 끝나는 시점까지 시계에 들어간다. 라이브러리마다 자기 레이아웃(NHWC 또는 NCHW)과 자기 옵티마이저로 돈다 — 그 차이가 비교의 일부다.
| 어댑터 · 날짜 | 라이브러리 | batch 16 | 32 | 64 | borch 대비 |
|---|---|---|---|---|---|
apple / metal-3 · 2026-09-22, borch-ts 0.6.0 게시본 | borch.ts | 19.3 | 31.7 | 55.1 | 1× |
| TF.js 4.22.0 (WebGPU) | 86.5 | 172.8 | 349.1 | 4.5× · 5.5× · 6.3× 느림 | |
| jax-js 0.1.25 + optax 0.1.2 | 68.1 | 97.1 | 152.2 | 3.5× · 3.1× · 2.8× | |
| Burn 0.21 (Rust → wasm, wgpu) | 264.7 | 520.7 | 1025.6 | 13.7× · 16.4× · 18.5× | |
nvidia / blackwell (RTX 5080, Linux, Vulkan, Chrome 151) · 2026-09-22, borch-ts 0.6.0 게시본 (두 run) | borch.ts | 10.7–11.3 | 14.2–14.7 | 22.6–23.1 | 1× |
| TF.js 4.22.0 | 75.0 | 121.4 | 223.3 | 6.6× · 8.3× · 9.9× | |
| jax-js 0.1.25 + optax 0.1.2 | 72.2 | 86.3 | 114.1 | 6.7× · 6.1× · 4.9× | |
| Burn 0.21 | 이 기계엔 Rust 툴체인이 없어 빌드하지 않음 — Burn 의 NVIDIA 값은 아래 4090 행 | ||||
nvidia / lovelace (RTX 4090, Linux, Vulkan, Chrome 153) · 2026-09-22, borch-ts 0.6.0 게시본 (두 run) | borch.ts | 10.0–10.5 | 10.9–12.3 | 17.7–17.9 | 1× |
| TF.js 4.22.0 | 67.5 | 113.7 | 211.7 | 6.4× · 10.4× · 12.0× | |
| jax-js 0.1.25 + optax 0.1.2 | 67.3 | 74.4 | 103.6 | 6.7× · 6.0× · 5.8× | |
| Burn 0.21 (Rust → wasm, wgpu) | 115.7 | 248.5 | 516.9 | 11.6× · 20.2× · 28.9× | |
nvidia / blackwell, Direct3D 12 경유 (RTX 5050 Laptop, Windows 11) · 2026-09-21 | borch.ts | 32.3 | 53.7 | 98.1 | 1× |
| TF.js 4.22.0 | 174.8 | 334.9 | 659.2 | 5.4× · 6.2× · 6.7× | |
2026-09-03 에 같은 페이지는 TF.js 대비 2.2–2.9× 였다. TF.js 는 움직이지 않았고(같은 바이트, 86.4 → 86.5), borch 가 움직였다 — subgroup 행렬 위의 합성곱 backward, 융합된 옵티마이저 스텝, 카드마다 측정으로 고른 커널. jax-js 에는 BatchNorm 과 교차엔트로피 모듈이 없어 그 스텝은 있는 것으로 조립했고, Burn 의 wasm wgpu 백엔드는 단일 스레드로 리드백마다 동기화한다(그리고 batch 16 에서 4090 이 Metal 보다 2.3× 빠르다 — borch 와의 거리가 카드 따라 움직이는 유일한 라이브러리). 각각 그 라이브러리가 오늘 브라우저에서 할 수 있는 최선이지, 그 라이브러리의 최적 구현은 아니다.
추론 — ResNet-18 (CIFAR) forward, ms
torch 에서 한 번 내보낸 같은 신경망 — borch.ts 에는 safetensors, ORT 에는 ONNX — 이고, 두
런타임이 시드 입력에서 torch 의 logits 를 1e-3 안에서 재현한 뒤에만 표를 찍는다(실측 ~5e-8).
워밍업 3회 뒤 20회 평균, forward 마다 scope 하나, 리드백 포함. borch 행은 eval forward 를
기록해 되감은 것(compiled(model))이다.
| 어댑터 | 런타임 | batch 1 | batch 16 | borch / ORT (1× 미만이면 borch 가 앞섬) |
|---|---|---|---|---|
apple / metal-3 · 2026-09-22 | borch.ts f32, fused + captured | 1.09 | 4.12 | 0.25× · 0.78× |
| ONNX Runtime Web 1.29.0, f32 | 4.35 | 5.30 | ||
| ONNX Runtime Web, f16 파일 (입출력 f32) | 3.29 | 4.26 | 0.33× · 0.97× — 무승부 | |
nvidia / blackwell (RTX 5080, Vulkan, Chrome 151) · 2026-09-22, 0.6.0 게시본 | borch.ts f32, fused + captured | 0.56 | 1.63 | 0.15× · 0.44× |
| borch.ts int8 static + captured | 0.52 | 0.96 | 0.14× · 0.26× | |
| ONNX Runtime Web 1.29.0, f32 | 3.75 | 3.72 | ||
| ONNX Runtime Web, f16 파일 | 세션 거부 — 이 카드의 Chrome(Linux, Vulkan)은 ORT 의 디바이스에 f16 을 주지 않는다 | |||
| ONNX Runtime Web, int8 파일 (QDQ) · WebGPU / wasm 프로바이더 | 38.6 / 4.0 | 109.3 / 55.2 | 자기 f32 보다 10–29× 느림 | |
nvidia / lovelace (RTX 4090, Vulkan, Chrome 153) · 2026-09-22, 0.6.0 게시본 | borch.ts f32, fused + captured | 0.58 | 1.65 | 0.16× · 0.46× |
| borch.ts int8 static + captured | 0.56 | 0.83 | 0.15× · 0.23× | |
| ONNX Runtime Web 1.29.0, f32 | 3.70 | 3.61 | ||
| ONNX Runtime Web, f16 파일 | 세션 거부, 5080 과 같다 — Linux Chrome 은 ORT 의 디바이스에 f16 을 주지 않는다 | |||
| ONNX Runtime Web, int8 파일 (QDQ) · WebGPU / wasm 프로바이더 | 40.9 / 4.6 | 104.6 / 64.3 | 자기 f32 보다 11–29× 느림 | |
nvidia / blackwell, Direct3D 12 경유 (RTX 5050 Laptop) · 2026-09-21 | borch.ts f32, fused + captured | 1.69–1.81 | 6.50–7.29 | 0.25–0.30× · 0.41–0.46× |
| ONNX Runtime Web 1.29.0, f32 | 5.67–7.34 | 15.7–16.0 | ||
같은 1,000장 held-out CIFAR-10 의 top-1, 이 시험을 위해 학습한 신경망, 5080: borch f32 92.40 %, borch int8 static 92.50 %, ORT f32 92.40 %, ORT int8 92.50 %. 양자화된 신경망은 같은 신경망이고, 다른 것은 누가 GPU 에서 돌리느냐다. ORT 의 int8 파일은 모든 어댑터에서 자기 f32 보다 WebGPU 에서 느리고(Metal 6×, 5080 11–28×) batch 1 에서는 wasm 프로바이더보다도 느리다 — 양자화된 합성곱이 WebGPU 프로바이더의 것이 아니다.
추론 — ViT-Tiny/16 (224², 1000 클래스) forward, ms
timm 의 vit_tiny_patch16_224 seed 0, borch 쪽은 bimm-ts 모델, 둘 다 torch 의
logits 로 게이트(실측 ~1e-6). 트랜스포머용으로 이 라이브러리에 쓴 것은 없다: 선형층은
합성곱의 GEMM 타일로, 어텐션은 배치 행렬곱과 subgroup softmax 로 돈다.
| 어댑터 | borch captured, b1 | ORT f32, b1 | borch, b16 | ORT, b16 | borch / ORT (1× 미만이면 borch 가 앞섬) |
|---|---|---|---|---|---|
apple / metal-3 · 2026-09-22 (토큰 열 197 → 200 패딩, subgroup 행렬) | 2.22 | 5.46 | 10.64 | 19.77 | 0.41× · 0.54× |
nvidia / blackwell (RTX 5080, Vulkan, Chrome 151) · 2026-09-22, 0.6.0 게시본 — 이 카드엔 f32 subgroup 행렬이 없어 스칼라 GEMM | 1.78 | 6.55 | 5.00 | 14.11 | 0.27× · 0.35× |
nvidia / lovelace (RTX 4090, Vulkan, Chrome 153) · 2026-09-22, 0.6.0 게시본 — 5080 과 같은 커널 집합, 토큰 197 패딩 없음 | 1.67 | 8.12 | 4.88 | 11.41 | 0.21× · 0.43× |
nvidia / blackwell, Direct3D 12 경유 (RTX 5050 Laptop) · 2026-09-21 | 3.31–3.37 | 10.0–11.8 | 24.0–27.8 | 47.5–61.0 | 0.29–0.33× · 0.46–0.50× |
이 페이지가 말하지 않는 것
- 모델 하나씩. ResNet-18 은 32 × 32 CIFAR 이고 ViT 는 tiny 다. 더 큰 입력과 더 큰 모델(57 MB 창으로 파인튜닝한 346 MB ViT-Base 는
docs/SCALE.md에 따로 있다)은 이 표 밖이다. - Chrome 만, 그것도 최근 것. Safari 와 Firefox 는 재지 않았다. 어댑터마다 기계 하나. Linux Chrome 은 5080 과 4090 에서 ORT 에 f16 을, borch 에 f32 subgroup 행렬을 주지 않는다(int8 만). 4090 의 Chrome 은 그 행을 재던 아침에 143 이었고, 그 Chrome 의 WGSL 전단은 int8 합성곱의
subgroupMatrixStore를 거부했다(151·153 은subgroup_id에 더는 적용하지 않는 uniformity 규칙). 라이브러리는 fault 를 세어 숫자를 찍지 않았고, 행은 Chrome 153 의 것이다. - 노트북의 오후 행이 범위인 것은 기계가 사용 중이었기 때문이다. 모든 라이브러리가 함께 2–3× 느렸고 비율은 유지됐다. 기계 없는 시간은 인용은 되어도 반박은 안 되기에 어댑터가 모든 행에 있다.
- borch 에 없는 것: f16 연산 커널(Metal 에서 ORT 의 f16 이 비기는 이유), LLM 디코드, 일반 그래프 IR. int8 은 합성곱만, 그리고 카드가 int8 subgroup 설정을 내주는 곳에서만.
- eager 는 이 표가 아니다.
compiled없이 모델을 부르는 페이지는 eager 행을 본다: Metal 에서 되감기의 1.1–1.35×, 5080 batch 1 에서는 ORT 와 대등, 4090 에서는 뒤진다(5.8 대 3.7) — 카드가 짧은 burst 사이에 클럭을 내리고 eager 루프가 그 틈을 준다. batch 16 은 둘 다 eager 로도 앞선다. - 첫 호출은 컴파일한다. Metal 과 5080 에서 0.05 초, 노트북 D3D12 에서 0.16 초(브라우저가 컴파일된 파이프라인을 다음 방문까지 보관해 두 번째는 0.015 초), 입력 모양당 한 번.
재현
이 페이지의 모든 표는 소프트웨어 어댑터를 거부하고 숫자 아래에 기계를 적는 러너가 찍는다.
git clone https://github.com/playidea-lab/borch && cd borch && npm ci && npm run build:ts
npm run compare:ts # 학습 vs TF.js · 추론 vs ORT Web f32/f16/int8 · ViT-Tiny
npm run compare-peers:ts # jax-js 와 Burn (Burn crate 는 tests/browser/burn_resnet18 에서 빌드)
npm run capture:ts # 기록, 튜너, 첫 호출의 비용
숫자마다 뒤에 있는 원장 — run 전에 적은 예측과 그중 틀린 것 — 은
docs/BOOK.md("How fast it is"), docs/INFER.md,
docs/INT8.md, docs/GEMM.md, docs/COMPILER.md,
docs/FIRST.md 다. 바이트 고정: TF.js 4.22.0, jax-js 0.1.25, Burn 0.21,
ONNX Runtime Web 1.29.0 (tests/browser/assets.lock).