오픈소스 Edge NPU Cluster
6 TOPS × 3. 정말 18 TOPS가 되는가?
저비용 NPU 여러 대를 표준 이더넷으로 묶어 분산 AI 추론을 확장하는 오픈소스 Edge NPU Cluster 런타임. Rust로 썼고 평범한 gRPC만 쓴다 — 전용 전송 계층도, RDMA도, 커널 우회도 없다.
Edge NPU Cluster는 모델을 통째로 하나씩 지닌 저렴한 NPU 보드 여럿을 스케줄러 하나가 앞에서 요청당 한 대씩 배정하는 구조다. 초당 처리량을 올린다. 여러 보드를 더 큰 가속기 한 대로 합치는 것도, 요청 하나를 더 빨리 만드는 것도 아니다.
확장 성능이 실제로 어디로 가는지 알아보려고 유효 하드웨어 측정 421건을 돌렸고, 유효 run의 추론 오류는 0이었다. 결론부터 말하면 확장은 된다. 흥미로운 부분은 그것을 거의 막을 뻔한 나머지 전부다.
| 측정 하드웨어 | NanoPi R76S 3대 — Rockchip RK3576, 개당 6 TOPS NPU |
|---|---|
| 네트워크 | 노드당 2.5 GbE, 스케줄러 호스트 10 GbE |
| 전송 | gRPC over HTTP/2 (tonic), 평문 |
| 런타임 | Rust |
| 워크로드 | RKNN을 통한 YOLOv8n INT8, 640×640×3 RGB |
| 측정 | 유효 벤치마크 421건, 추론 오류 0 |
| 1 → 3노드 | 112.9 → 229.0 → 338.4 inf/s — 3.00배 |
| 최고 처리량 | 전송 튜닝 시 387.2 inf/s — 다만 확장 효율은 98.9% → 95.3%로 내려간다 |
| 측정한 규모 | 1대 · 2대 · 3대 |
| 4대 이상 | 미측정 |
문자 그대로는 아니다. NPUDure는 NPU 세 대를 18 TOPS 가속기 하나로 합치지 않는다. 독립적인 추론 요청을 여러 Edge NPU 노드에 분배할 뿐이다. 측정한 구성에서 3노드는 1노드 대비 3.00배의 총 추론 처리량을 냈다.
| 준선형 확장 | 3.00배 — 30회 반복에서 112.9 / 229.0 / 338.4 inf/s · S2 |
|---|---|
| 전송 튜닝 | 387.2 inf/s (+13.3%) — 그러나 효율은 98.9% → 95.3% · S3.8 |
| 손실이 간 곳 | tail이었다 — p50은 그대로, p99는 +36% · S3.9a |
| 지속 부하 | 31분 동안 능동 냉각 −1.9% vs 무팬 −11.3% · S0 |
| 스케줄링 | 우리 기본 설정에 있던 herding 버그. 고치니 p99가 −37% · S0-C |
| 열 이질성 | 최고 SoC 온도는 거의 같았는데(~86 °C) 노드별 지연 편차는 1.10배–2.40배로 벌어졌다. 온도만으로는 설명되지 않았다. |
| io_uring | 구현하지 않음 — 측정이 반대했다 · S3.9b |
절대 처리량과 확장 효율은 반대 방향으로 움직인다. 전송을 튜닝해 처리량을 13.3% 얻고 확장 효율 3.6포인트를 잃었다. 둘 중 하나만 인용하면 이 교환관계가 가려지므로, 여기서는 언제나 측정 조건과 함께 둘 다 적는다.
계측기가 우리를 오도한 여섯 번을 포함한 긴 이야기: Edge NPU Cluster: 6 TOPS NPU 세 대는 정말 18 TOPS가 되는가?
이 한계들은 묻어두지 않고 공개한다. 경계를 그을 수 없는 결과는 인용할 수 없는 결과다.
문자 그대로는 아니다. NPUDure는 NPU 세 대를 18 TOPS 가속기 하나로 합치지 않는다. 독립적인 추론 요청을 여러 Edge NPU 노드에 분배할 뿐이다. 측정한 구성에서 3노드는 1노드 대비 3.00배의 총 추론 처리량을 냈다 — 30회 반복에서 112.9에서 338.4 inf/s.
모델을 통째로 하나씩 지닌 저렴한 NPU 보드 여럿을, 스케줄러 하나가 앞에서 요청당 한 대씩 배정하는 구조다. 초당 처리 요청 수를 올린다. 보드들을 더 큰 가속기 한 대로 합치지는 않는다.
아니다. 요청 하나는 여전히 보드 한 대가 그 보드의 속도로 처리한다. NPUDure가 올리는 것은 총 처리량이지, 개별 추론의 지연이 아니다.
측정이 그것을 정당화하지 않았기 때문이다. 전송 비용은 요청당 16.35 CPU-ms였고 그중 syscall 진입은 약 1.0%였다. 부하 중에도 보드 CPU는 48.9%가 유휴였다. 여기서 CPU는 제약이 아니라 비용이다.
중앙값이 아니라 tail이다. 전송을 튜닝하니 절대 처리량은 13.3% 올랐지만(387.2 inf/s) 확장 효율은 98.9%에서 95.3%로 내려갔다. p50은 그대로였고 p99가 36% 올랐다. 절대 처리량과 확장 효율은 반대 방향으로 움직이므로, 이 프로젝트는 언제나 둘을 함께 보고한다.
낡은 상태를 보고 몰려갔기 때문이다. 부하 인지 정책은 라운드로빈 대비 처리량을 55~58% 떨어뜨렸는데, 모든 스케줄러 인스턴스가 이미 지난 하트비트 상태를 보고 같은 순간에 같은 "한가한" 노드를 골랐다. 로컬에서 추적하는 in-flight 카운터로 바꾸자 p99가 37% 줄고 노드별 지연 편차가 1.33배에서 1.00배로 고르게 됐다. 우리 기본 설정의 버그였고, 정책 A/B로 찾았다.
계획은 CPU를 프로파일하고 syscall과 복사 비용을 잰 뒤 구현하는 것이었다. 앞의 두 단계를 하고 나니 도달 가능한 몫이 전송 비용의 약 8%였고, 포화되지 않은 자원에서 그것을 회수해봐야 얻는 게 없었다. 측정이 구현에 반대했으므로 커밋 로그가 아니라 기각된 결정으로 기록했다.
모른다. 1대·2대·3대만 측정했다. 4대 이상은 측정한 적이 없고, 준선형 결과를 3대 너머로 외삽해서는 안 된다.
저장소의 FAQ 원문은 영문이다 — 전체 10개 질문 · docs/FAQ.md
이 페이지는 왜 · 무엇을 · 결과를 설명한다. 저장소에는 어떻게 · 근거 · 코드가 있다.