잰 것만
이 탭의 GPU
borch 는 WebGPU 위에서 돈다. 즉 브라우저가 내주는 GPU 위에서 돈다. 브라우저가 안 내줄 때가 있다. 이 페이지는 그때 무엇을 하는지를 플랫폼별로 적고 — GPU 를 끝내 못 얻었을 때 borch 가 무엇을 하는지도 적는다.
먼저 배지를 읽는다
코드를 돌리는 모든 페이지가 어댑터 이름을 그대로 단다. 그 배지가 진단의 전부다:
apple / metal-3이나nvidia / blackwell같은 이름 — 진짜 GPU 다. 이 페이지는 당신과 상관없다.google / swiftshader, 또는llvmpipe·lavapipe가 들어간 이름 — GPU 의 인터페이스를 쓴 CPU 다. 돌기도 하고 골든과 값도 맞는데, 찍히는 속도는 전부 CPU 의 것이다.- 어댑터 없음 — 드라이버에 묻기도 전에 브라우저가 거절했다. 이 페이지의 나머지가 그 이야기다.
배지는 어느 장치인지를 말하고, 내 GPU 로 확인은 그 위에서 답이 맞는지를 말한다. 골든 전부를 — 진짜 PyTorch 로 굳힌 케이스 하나하나를 — 당신의 탭에서 돌리고, 개수 옆에 어댑터를 함께 찍는다. 이 페이지의 벤더 줄은 전부 여기 누군가가 가진 기계에서 나왔고, 그래서 그중 하나가 몇 달 동안 틀려 있었다.
어느 브라우저에 WebGPU 가 있나
MDN 의 browser-compat-data, 2026-09-10 에 읽음. 읽음 이라고 적힌 행은 읽은 것이지 본 것이 아니다 — 이 페이지의 다른 측정은 전부 여기 누군가가 가진 기계에서 나왔고, 그 둘은 아니다.
| 브라우저 | WebGPU 시작 | 여기서는 |
|---|---|---|
| Chrome, Edge | 113 (부분), 144 완전 | 측정 — 아래 모든 행, 그리고 그 아래 그래픽 API 셋 모두에서 골든이 통과한다: Metal, Vulkan, Direct3D 12 (윈도우 11 노트북, 2026-09-10) |
| 안드로이드 Chrome | 121 | 측정 — Galaxy Z Flip 6 |
| iOS Safari | Safari 를 따라간다 | 측정 — iPhone 13 mini |
| macOS Safari | 26 · 18~25 는 Feature Flag 뒤 | 읽음 — 위 아이폰과 같은 엔진이지만, 여기서 그 위에 열어 본 사람은 없다 |
| Firefox | 141 (부분) | 읽음 — 열어 보지 않았고, 부분 은 이 라이브러리가 쓰는 표면이 다 있다는 약속이 아니다 |
| 안드로이드 Firefox | 없음 | — |
Safari 나 Firefox 에서 열었는데 배지에 어댑터 이름이 뜬다면, 이 페이지는 인용보다 그 행을 원한다. 배지 글자가 보고서 전부다.
macOS
할 일이 없다. Apple M4 Max 에서 측정 — 어댑터가 apple / metal-3 으로 오고,
플래그도 설치도 설정도 없이 Run 이 돈다. 이 사이트에서 apple / metal-3 이라
적힌 수는 전부 그렇게 잰 것이다. 스위트 전체도 그렇다 —
agreeing 4753 / 4753 [apple / metal-3].
첫 실행이 얼마나 걸리나 — 어댑터별
tests/browser/first_run.py 로 2026-09-03 (위 세 행) 과 2026-09-09 (윈도우 노트북) 에 잰 값. 랜딩 페이지를 새로 열고, 장치를
probe 하고, Run 을 눌러 done 줄까지. "페이지 안" 은 페이지 자신의 타이머, "열기→done" 은
페이지를 연 시점부터의 벽시계. 배포 행은 GitHub Pages 전송이 더해진다.
| 어댑터 | 방문 | 페이지 안 | 열기→done | 기다림의 정체 |
|---|---|---|---|---|
apple / metal-3M4 Max, Chrome | 첫 방문 | 1.35 s | 1.6 s | 어댑터는 수십 ms 에 답한다. 첫 클릭은 Python 의 첫 import(0.5 s)와 실행값을 치른다 |
| 재방문 | 0.66 s | 1.3 s | ||
| 배포 사이트, 첫 방문 | 1.71 s | 1.9 s | ||
nvidia / lovelaceRTX 4090, 드라이버 550, Ubuntu (Xorg + GNOME), Chrome 143, 모니터 켠 상태 | 첫 방문 | 1.60 s | 1.8 s | Apple 과 같다: adapter 14 ms, device 15 ms, 셰이더 22 개 1 ms, 첫 readback 28 ms. 켜진 모니터 앞에서 잰 값 — 같은 기계가 화면이 꺼진 채로는 무엇을 읽었는지는 아래 주석에. |
| 재방문 | 1.51 s | 1.6 s | ||
nvidia / blackwellRTX 5080, 드라이버 580, Ubuntu (Xorg + GNOME), Chrome 151 | 화면을 켠 상태로 다시 잴 것 | 이전 행(첫 방문 2.1 s, "1초 단위 3초 핸드셰이크")은 ssh 로 꺼진 모니터 앞에서 잰 것이고, 그것이 잰 것은 모니터였다 — 철회. | ||
nvidia / blackwellRTX 5050 Laptop, 드라이버 581, Windows 11, Chrome (D3D12), 윈도우 그래픽 설정에서 Chrome 을 고성능으로 둔 상태 — 노트북 주석 참조 | 첫 방문 | 3.32 s | 4.2 s | 어댑터는 200–450 ms, 셰이더 17 개는 1–4 ms 에 답한다. 그 둘을 빼고도 2.5 s 이상이 설명되지 않은 채 매 방문마다 남는다 — 캐시가 따뜻해도. 크롬 D3D12 경로의 첫 실행 비용인데, 이 페이지는 아직 그 이름을 모른다. 노트북의 CQ 워커가 페이지를 로컬로 서빙하고 창을 띄워 잰 값. |
| 재방문 | 2.57 s | 3.3 s | ||
| 워밍업 뒤 클릭 | 3.57 s | 4.3 s | ||
첫 학습까지 — 학습자의 시계
첫 화면은 버튼을 누르는 순간 Python 으로 모델 하나를 학습한다. 이 시계는 배포된
사이트에서 페이지를 여는 순간 시작하고(다운로드가 안에 들어간다), 읽는 사람이
느끼는 순간마다 멈춘다: 버튼 밑에 Python 준비됨, 클릭 뒤 첫 loss 줄, 배운 것 줄.
tests/browser/learner_path.py 가 매일 밤 잰다.
| 어댑터 | 열고 나서 Python 준비까지 | 클릭 → 첫 loss 줄 | 클릭 → 배운 것 | 재방문, 바로 클릭 → 배운 것 | 레슨 0, 블록 셋 |
|---|---|---|---|---|---|
apple / metal-3M4 Max, Chrome | 4.4 s | 0.7 s | 0.8 s | 1.4 s | 2.4 s |
nvidia / lovelaceRTX 4090, 드라이버 550, Ubuntu, Chrome 143, 모니터 켠 상태 | 4.7 s | 0.6 s | 0.6 s | 1.6 s | 2.6 s |
꺼진 모니터는 GPU 의 모든 대기를 1초 대기로 만든다. 이 표를 처음
채웠을 때 4090 행은 Python 준비까지 8.6 s, 클릭에서 "배운 것"까지 6.5 s 였고, 그 위의
행은 어댑터 핸드셰이크가 1초 단위로 3초 걸린다고 했다. 둘 다 ssh 로, GNOME 이 5분
유휴 뒤 HDMI 모니터를 꺼 둔 데스크톱에서 잰 것이다(xset q: "Monitor is
Off"). 꺼진 화면 앞에서는 프레임 스왑이 약 1초에 타임아웃되고 GPU 의 모든 대기가
그 프레임 뒤에 줄을 선다: 어댑터 요청 3.0 s, 페이지에 한 줄 쓴 뒤의 readback 1.0 s,
loss 를 찍는 학습 루프 7초. 모니터를 깨우자 같은 프로브가 52 ms, 46 ms, 0.56 s 를
읽었다. tests/browser/readback_probe.py --fresh 가 줄마다 재현한다.
타이밍 스크립트는 이제 꺼진 화면 앞에서는 재기를 거부하고 그 사실을 말한다.
이 라이브러리 쪽에서 하는 것: 페이지가 어댑터를 한 번만 요청하고(probe 가 받은
어댑터를 init() 이 그대로 쓴다), 읽는 동안 device 와 Python 을 미리
준비해 클릭은 실행값만 치르게 한다. 화면이 켜져 있으면 NVIDIA 드라이버 위 Linux 는
여기 있는 모든 숫자에서 Apple 과 같다.
NVIDIA 카드를 단 리눅스
플래그 둘이면 충분하고, 러너도 이제 그 둘을 보냅니다. 그 둘이 없을 때 벌어지는 일은 거절이 아닙니다 — 그보다 조용합니다. 크롬은 자기 CPU 래스터라이저를 내주고 페이지를 돌게 두며, Vulkan 백엔드가 켜질 때까지 계속 그럽니다. 두 카드를 같은 방식으로 쟀습니다 — 진짜 X 세션과 시스템 크롬:
| 크롬을 이렇게 띄우면 | RTX 5080 드라이버 580 · 크롬 151 |
RTX 4090 드라이버 550 · 크롬 143 |
|---|---|---|
| 아무것도 없이 | 없음 | 없음 |
--enable-features=Vulkan 단독 |
nvidia / blackwell |
없음 |
--enable-unsafe-webgpu |
google / swiftshader |
google / swiftshader |
+ --ignore-gpu-blocklist |
google / swiftshader |
google / swiftshader |
+ --enable-features=Vulkan |
nvidia / blackwell |
nvidia / lovelace |
+ --disable-gpu-driver-bug-workarounds |
nvidia / blackwell |
nvidia / lovelace |
--enable-unsafe-webgpu --enable-features=Vulkan이 행이 러너의 목록이다 |
nvidia / blackwell |
nvidia / lovelace |
그러니 붙여넣을 줄은 이것입니다. 플래그 둘, 그리고 일을 하는 것은 둘째 — 리눅스에서 WebGPU 는 크롬의 Vulkan 백엔드 위에서 돌고, 그것이 켜지기 전까지 브라우저는 거절하는 대신 자기 CPU 래스터라이저를 대신 내줍니다. 카드가 막힌 적은 없었고, 백엔드가 꺼져 있었을 뿐입니다.
google-chrome --user-data-dir=/tmp/borch-check \
--enable-unsafe-webgpu --enable-features=Vulkan
chrome://flags 에서는 #enable-unsafe-webgpu 와
#enable-vulkan 입니다. 바꾼 뒤 다시 띄워야 합니다 —
이미 열려 있던 창은 예전 설정을 그대로 씁니다. 여기서 한 시간을 썼습니다.
그 둘로 안 되면 --ignore-gpu-blocklist 를 더하십시오.
크롬은 드라이버에 아무것도 묻기 전에 거절하는 GPU·드라이버 조합 목록을 들고 있고, 그
플래그가 그것에게 가서 보라고 말하는 방법입니다. 여기서 잰 어느 기계에서도 아무것도
바꾸지 않았습니다 — 다만 그 기계들은 하나도 그 목록에 없었습니다.
그러니 이 페이지는 그 플래그가 존재하는 이유가 되는 상황을 본 적이 없습니다.
당신 기계가 그것일 수 있습니다.
그 두 플래그로 스위트 전체를 돌렸습니다. 어댑터만 물어본 것이 아니라 —
5080 에서 agreeing 4460 / 4466 [nvidia / blackwell], 그리고 학습도 맞습니다.
작은 ResNet 30 스텝의 궤적, 층별 기울기, 배치정규화 버퍼, eval 까지 torch 와 대조해
64 values, 파이썬 바인딩으로 한 번 borch.ts 자체 API 로 한 번.
갈린 여섯은 GPU 가 아니라 그 기계에 대한 것입니다. 여섯 다
device='cuda' 일 때 무슨 일이 나는지를 묻는데, 그 답이
torch 에 CUDA 가 없는 기계에서 얼려졌습니다. 거기서는 양쪽 다 거절하니
케이스가 둘 다 멈춘다가 됩니다. 그 박스는 CUDA 빌드라 torch 가 안 멈추고,
cuda.device_count 는 골든이 0 을 기대하는 자리에서 1 이라고 답합니다.
셰이더에서 나온 값은 하나도 없습니다 — 같은 여섯이 Apple Metal 에서는
통과합니다. 얼려진 기계의 성질을 같이 얼려버린 케이스의 모양입니다. 이 종류가 앞서 둘
발견돼 고쳐졌고, 이것들이 그 가족의 나머지입니다.
4090 은 어댑터 측정만 있습니다. 세션 도중 카드가 PCIe 버스에서 빠진 뒤 돌아오지 않아 골든은 돌린 적이 없습니다.
이 표의 이전 판은 차단목록 플래그가 5080 을 연다고 말했습니다. 아닙니다 —
오늘 같은 기계에서 세 번 돌려 전부 google / swiftshader 였습니다. 그 값은
다른 브라우저(Playwright 가 들고 다니는 크로미움, 그리고 더 옛 크롬)에서
나온 것이고, 칸에 크롬 버전을 적은 이유도 그것입니다. 두 카드는 카드·드라이버·
브라우저가 한꺼번에 다릅니다. 그러니 칸 사이의 차이를 하드웨어에만 돌릴 수
없습니다. --enable-features=Vulkan 단독이 5080 에는 닿고 4090 에는 안 닿는데,
그 사이에 크롬 메이저 여덟 개가 놓여 있습니다.
WebGPU 워킹그룹의
구현 현황은
리눅스에 다른 조합을 줍니다 — --ozone-platform=x11 --use-angle=vulkan
--enable-features=Vulkan,VulkanFromANGLE — 그리고 그것도 카드에 닿습니다. 통하는
이유는 안에 --enable-features=Vulkan 이 있어서입니다.
--use-angle=vulkan 단독은 어댑터를 아예 못 주고,
--ozone-platform=x11 은 필요 없었습니다.
AMD·Intel 카드를 단 리눅스
안 쟀다. 위 차단 목록은 NVIDIA-리눅스 항목이라 같은 스위치 둘을 먼저 시도할 만하지만, 그 카드들에서는 아무것도 돌려보지 않았다. 짐작으로 적으면 이 저장소가 쓰는 쪽이 아니라 걷어내는 쪽의 문장이 된다.
윈도우
할 일이 없다. NVIDIA 카드를 단 윈도우 데스크톱에서 측정 —
플레이그라운드 배지가 nvidia / blackwell 로 왔고 플래그를 하나도
안 켜고 Run 이 돌았다. 윈도우의 크롬은 위 리눅스 절이 다루는 Vulkan 경로가
아니라 D3D12 를 쓰므로 그 사다리는 그쪽에 안 옮겨간다 — 그리고 옮겨갈 필요도 없었다.
이 칸은 누가 페이지를 열기 전까지 안 쟀다였다. 여기 있던 글이 자기 은퇴 조건을 스스로 적어뒀다 — "누군가 그 위에서 페이지를 열기 전까지 안 쟀다로 둔다. '된다고 읽었다'고 적는 페이지와 '도는 것을 봤다'고 적는 페이지는 다른 페이지". 구현 현황 페이지는 x86·x64 윈도우를 Chrome 113 부터 기본 켜짐으로 적고 있었고, 그것을 읽은 것은 본 것과 끝내 같지 않았다. 돌아온 답과 일치하며, 그건 알아둘 값어치가 있지 이 문단이 바뀐 이유는 아니다.
ARM64 윈도우는 아직 안 쟀다. 같은 페이지가 그쪽은 여전히
#enable-unsafe-webgpu 뒤로 적는다 — 그러니 "윈도우는 된다"는 아직도
어느 윈도우냐에 따라 답이 둘이고, 그중 하나만 여기서 봤다.
GPU 가 둘인 노트북
2026-09-09 RTX 5050 을 단 윈도우 노트북에서 측정 — 배지가 intel / gen-12lp
로 왔다. 내장 GPU 다. 페이지의 수치는 전부 그것의 것이었다. 페이지는
high-performance 어댑터를 요청하지만 윈도우의 크롬은 그 요청을 들어줄 수 없다 —
WebGPU 는 윈도우가 chrome.exe 에 배정한 GPU 에서 돌고, 브라우저의 기본값은 절전용이다.
두 번째 카드가 있는지는 페이지가 볼 수 없으므로, 배지는 판정이 아니라 안내를 띄운다.
- 설정 → 시스템 → 디스플레이 → 그래픽.
- 앱 추가 →
chrome.exe를 찾아 넣는다 (보통C:\Program Files\Google\Chrome\Application\chrome.exe). - Chrome → 옵션 → 고성능 → 저장.
- 크롬을 완전히 끝내고 — 트레이의 백그라운드 프로세스까지 — 페이지를 다시 연다. 배지에 외장 카드 이름이 와야 한다.
NVIDIA 카드라면 NVIDIA 제어판의 3D 설정 관리 → 프로그램 설정 도 같은 일을 한다. 둘 중 하나면 된다.
골든도 거기서 돈다. 같은 노트북에서, 그 위의 CQ 워커가 돌린 결과:
borch.ts passed 4057 / failed 0, 바인딩 agreeing 4753/4753, GPU 검증 오류 0 —
이 저장소가 Metal 이나 Vulkan 이 아니라 Direct3D 12 를 지나 값을 잰 첫 사례다.
폰과 태블릿
iOS 에서는 돈다. iPhone 13 mini 에서 측정 — 배지에 Apple 어댑터가 왔고 실제로 돌았다. 어댑터를 얻은 것에서 그치지 않고 화면에 결과가 나왔다는 뜻이고, 폰에서는 그 둘을 따로 확인할 값어치가 있다. iOS 버전은 적어두지 않아서 여기서 주장하지 않는다.
이 한 번의 측정이 데스크톱의 한 번보다 많은 것을 말한다 — iOS 의 모든 브라우저가 WebKit 을 쓴다. 시험할 두 번째 엔진이 없다. 사파리도, 다른 브라우저처럼 보이는 것들도 밑은 같은 엔진이다.
안드로이드에서도 돈다 — 갤럭시 Z 플립 6 의 크롬에서 측정. Adreno 어댑터가 왔고, 어댑터만이 아니라 화면에 결과가 나왔다. 소프트웨어가 아니라 진짜 GPU 이고, 이 페이지에서 중요한 구분은 그것이다.
다만 기기 하나이고, 안드로이드에서는 그 점이 가장 크다. 위의 iOS 줄이 iOS 전체를 덮는 것은 거기 엔진이 하나뿐이기 때문이다. 안드로이드는 브라우저도 엔진도 여럿이고, 그 밑의 GPU 계열도 셋이다 — Adreno·Mali·Xclipse. 스냅드래곤 폰의 크롬은 그 격자의 한 칸이다. 안드로이드 버전도 적어두지 않아서 주장하지 않는다.
배지에 어댑터 이름이 붙어 있는 이유
이 프로젝트의 예전 판은 WebGPU 를 못 잡으면 WebGL 로 떨어졌고, 한동안 그 저자들이 CPU
경로에서 잰 수를 GPU 의 수로 읽었다. 다른 모양으로 또 일어났다 — 845 건을 전부 통과한
골든 실행이 *"두 번째 벤더에서 확인"* 으로 기록됐는데, 어댑터는 내내
google / swiftshader 였다. 그 문장은 제대로 측정될 때까지 문서 셋에 살아
있었다.
그러니 규칙은 "CPU 에서는 절대 안 돈다" 가 아니라 "CPU 에서 잰 수가 GPU 의 수로 읽히면 안 된다" 이다. 배지에 어댑터 이름이 있는 이유, 소프트웨어 어댑터에서 배지가 꺼지는 이유가 그것이다.
이 페이지는 여기서 하루 동안 거짓을 말했고, 정정을 지우지 않고 남긴다.
init() 이 소프트웨어 어댑터를 조용히 받는 대신 멈춘다고 적었었다. 멈춘 적이
없다 — borch-ts/src 에 그런 검사가 없었고, SwiftShader 에서 통과한 모든 골든
실행이 그 증거다. 알려주는 것은 배지이고, 라이브러리는 브라우저가 주는 것을 받는다.
일부러 CPU 에 물어보기
어느 어댑터를 받을지는 브라우저가 정하므로, 소프트웨어 쪽을 일부러 달라고 하는 길이 있다. CPU 경로를 보고 싶을 때, 또는 내 코드가 특정 장치에 기대고 있지 않은지 확인할 때 쓴다:
await init({ forceFallbackAdapter: true });
const { software } = await probe(); // 그리고 무엇을 받았는지 안다
덜떨어진 실행이 아니라 진짜 실행이다. 오늘 측정 — 골든 전체가 양쪽에서
통과한다. google / swiftshader 에서 3079 / 0,
apple / metal-3 에서 3079 / 0. 같은 코드, 같은 값, 다른 장치.
소프트웨어 실행이 아닌 것은 속도의 출처 하나다.
GPU 를 끝내 못 얻으면
그 문장 밑에 서로 다른 두 상황이 숨어 있고 답이 다르다. 브라우저가 소프트웨어 어댑터라도 내주면 위 절이 당신의 경우다 — SwiftShader 도 WebGPU 이고, 코드는 돌고, GPU 의 것이 아닌 것은 속도뿐이다. 아무것도 안 내주면 — 원격 세션, 정책으로 꺼진 가속, 오래된 브라우저 — borch.ts 에는 한 종류의 일을 위한 두 번째 장치가 있고, 그것이 무엇이 아닌지도 분명히 말한다.
cpu 장치는 사전학습 체크포인트의 바이트를 WebAssembly SIMD
커널로 돌린다: 허브의 백본, 그 특징 위에 학습하는 선형 헤드, 검수 큐를 위한 코사인
이웃 — 작업대의 고정 백본 경로, 사진 수백 장을 가진 현장 학습자가 하는 일이다. 두 번째
Tensor 백엔드는 아니다: 당신의 모델, 작은 CNN, ONNX 내보내기는 여전히
어댑터가 필요하고, 각각이 이름을 대고 그렇다고 말한다. 페이지가 cross-origin isolated
이면 forward 를 워커 풀에 나눠 돈다; 이 사이트, site/serve.py, 그리고
localhost 의 오프라인 번들이 그렇고, file:// 페이지는 절대
아니다.
| 당신의 기계 | 도는 것 | 측정 |
|---|---|---|
| WebGPU 어댑터 | 전부 | EfficientNet-B0 1.5 ms/이미지, apple / metal-3 |
| 어댑터 없음, 격리된 페이지 | 워커 풀 위의 cpu 장치 | B0 2.6 ms/이미지, 8 워커, M4 Max |
| 어댑터 없음, 보통 페이지 | 한 스레드의 cpu 장치 | B0 14 ms/이미지, M4 Max |
| 소프트웨어 어댑터만 | CPU 속도의 WebGPU 전부, 그 옆의 cpu 장치 | B0 에서 SwiftShader 가 cpu 장치보다 25× 느리다 |
파이썬에서는 같은 장치가 import borch_cpu 다. 작업대의
첫 셀이 어댑터를 시도하고 없으면 스스로 여기로 내려오며, 무엇을 잡았는지와 스레드 몇 개인지를
말한다. 파이썬 쪽 뒤의 numpy 코어는 또 다른 세 번째 것이다 —
언어를 바꾸는 일이고, 어댑터가 필요 없고, 속도라 할 것도 없다. 위의 수치는 책의 것으로
각각 기계 이름이 붙어 있고, 그 수치를 낸 검사는 매일 밤 돈다.