상태: 초안 (2026-10-16), 일부 실측. 단계별 시간은 집 랩의 8GB급 노트북용 GPU 한 장이 달린 서버에서 1회용 파드(CPU 4코어 한도)로 쟀습니다(예열 30회 뒤 300회의 중앙값). 폰에서의 잡기·인코딩 시간과 인터넷 구간은 미실측입니다. 큐 흉내는 노트북 CPU 예제(
code/01_latency_queue.py, 1초 안팎)입니다. 시리즈 「비전 모델 읽기 3: 실시간으로 돌리기」 1편. 이 시리즈는 비전 모델 읽기 1·2에서 본 모델들을 정해진 시간 안에 돌리는 방법을 다룹니다.
목차
- 1. 「실시간」 의 두 숫자
- 2. 단계를 겹치면: 처리량은 늘고 지연은 그대로
- 3. 밀릴 때: 줄을 세울까, 버릴까
- 4. 늦은 정답은 틀린 답: Streaming Perception
- 5. 우리 실측: 공 찾기 한 프레임의 일생
- 6. 숫자를 읽는 습관
- 7. 한계
- 8. 우리 프로젝트와의 연결
- 참고 문헌
1. 「실시간」 의 두 숫자
탁구 점수판 앱이 「실시간」 이려면 두 가지가 동시에 맞아야 합니다.
- 처리량(throughput): 1초에 몇 프레임을 처리하나. 카메라가 30fps 로 주면 30 이상이어야 밀리지 않습니다.
- 끝단 지연(end-to-end latency): 한 프레임이 찍힌 순간부터 그 결과가 화면에 나올 때까지. 공이 튄 뒤 점수판이 언제 바뀌나를 정합니다.
둘은 자주 섞여 쓰이지만 다른 숫자입니다. 처리량은 「가장 느린 한 단계」 가 정하고, 지연은 「모든 단계의 합 + 줄에서 기다린 시간」 입니다. 30fps 의 프레임 간격 1000 ÷ 30 = 33.3ms 는 처리량의 예산이지, 지연의 상한이 아닙니다. 단계를 겹치면 지연이 33ms 를 넘어도 30fps 를 낼 수 있고, 반대로 지연이 10ms 여도 줄이 쌓이면 1초 뒤의 결과를 보게 됩니다.
2. 단계를 겹치면: 처리량은 늘고 지연은 그대로
전처리 8ms, 추론 25ms 를 차례로 하면 한 프레임에 33ms, 1초에 30.3장입니다. 전처리를 다른 스레드에서 미리 해 두면(파이프라인) GPU 는 쉬지 않고 추론만 하므로 1초에 40장까지 됩니다. 그런데 한 프레임이 겪는 시간은 여전히 8 + 25 = 33ms 입니다.
처리량 = 1 ÷ (가장 느린 단계의 시간)
지연 = 단계 시간의 합 + 줄에서 기다린 시간
리틀의 법칙: 처리 중인 프레임 수 = 처리량 × 지연 (40장/초 × 0.033초 = 1.32장)
리틀의 법칙(대기열 이론의 기본 공식)은 거꾸로도 씁니다. 처리량이 정해져 있는데 「지금 처리 중이거나 기다리는 프레임」 이 늘었다면, 그만큼 지연이 늘었다는 뜻입니다.
3. 밀릴 때: 줄을 세울까, 버릴까
추론 시간은 매번 조금씩 다릅니다. 평균 25ms 면 대개 예산 안이지만, 평균이 36ms 로 예산을 넘으면 어떻게 될까요. 30fps 카메라 60초를 흉내 냈습니다(code/01_latency_queue.out.txt, 추론 시간은 로그정규 분포, 숫자는 설명용 가정).
| 추론 평균 | 규칙 | 처리 fps | 버린 프레임 | 지연 중앙 · p99 | 60초 뒤 마지막 프레임 지연 |
|---|---|---|---|---|---|
| 25ms | 줄 세우기 | 30.0 | 0% | 24.6 · 44.1ms | 21.3ms |
| 25ms | 최신만 | 30.0 | 0% | 24.6 · 44.1ms | 21.3ms |
| 36ms | 줄 세우기 | 27.9 | 0% | 2,068.7 · 4,465.7ms | 4,500.7ms |
| 36ms | 최신만 | 27.4 | 8.6% | 48.0 · 82.3ms | 75.9ms |
예산 안에서는 두 규칙이 같습니다. 예산을 넘으면 줄 세우기는 처리량을 거의 유지하면서 지연이 끝없이 쌓입니다(1분 뒤 4.5초 늦은 화면). 「최신만」 은 처리기가 비는 순간 가장 새 프레임만 집고 밀린 것을 버립니다. 8.6% 를 버리는 대신 지연이 묶입니다. 실시간 영상에서 몇 초 전 프레임의 결과는 쓸모가 없으니, 버리는 쪽이 맞습니다. TT Score 의 실시간 작업자와 브라우저도 이렇게 합니다(8편).
4. 늦은 정답은 틀린 답: Streaming Perception
Li·Wang·Ramanan 의 「Towards Streaming Perception」(ECCV 2020)은 이 생각을 평가 방법으로 만들었습니다. 보통의 검출 정확도(AP)는 세상이 모델을 기다려 준다고 가정합니다. 이 논문의 streaming AP 는 시각 t 의 정답을 「t 이전에 계산이 끝난 가장 최근 출력」 과 비교합니다. 늦게 나온 출력은 이미 움직인 세상과 비교되므로 점수가 자연히 깎입니다.
| 검출기(자율주행 영상 30fps, 1920×1200) | 한 프레임 | AP |
|---|---|---|
| HTC(정확하지만 느림), 보통 평가 | 700.5ms | 38.0 |
| 같은 모델, streaming 평가 | 700.5ms | 6.2 |
| RetinaNet(가장 빠름), 0.2배 입력 | 31–36ms | 5.5–6.0 |
| Mask R-CNN, 0.5배 입력, 전처리 GPU | 56.7ms | 12.0 |
| + 일정 조정(가끔 쉬기) | 56.7ms | 13.0 |
| + 칼만 예측, 0.75배 입력으로 다시 고름 | 92.7ms | 17.8 |
(논문 표 1·2, 2017년 소비자용 상위 GPU 한 장.) 세 가지가 보입니다.
- 정확하지만 느린 모델은 실시간에서 무너집니다(38.0 → 6.2).
- 그렇다고 가장 빠른 모델이 이기지도 않습니다. 중간 어딘가가 최적입니다.
- 예측(지연 동안 물체가 움직였을 만큼 앞으로 밀기)을 붙이면, 더 느리지만 정확한 모델이 다시 이깁니다. 지연을 「줄일」 수 없으면 「메울」 수 있습니다.
「일정 조정」 이 흥미롭습니다. 다음 프레임이 곧 올 것 같으면 지금 프레임을 시작하지 않고 기다립니다. 낡은 프레임을 붙잡고 있느라 새 프레임을 놓치는 것보다 낫기 때문입니다. 3절의 「최신만」 과 같은 정신입니다.
5. 우리 실측: 공 찾기 한 프레임의 일생
TT Score 의 실시간 경로를 서버 쪽에서 단계별로 쟀습니다. 실제 경기 화면 한 장(1920×1080 을 카메라 크기 1280×720 으로 줄인 것)을 300번 돌렸습니다. 폰이 하는 일(줄이기·인코딩)도 같은 서버 CPU 에서 흉내만 냈습니다.
| 단계 | 어디서 | 중앙값 | p90 |
|---|---|---|---|
| 1280×720 → 512×288 줄이기 | 폰(서버 CPU 로 흉내) | 0.27ms | 0.30ms |
| JPEG 인코딩(품질 70) | 폰(서버 CPU 로 흉내) | 0.30ms | 0.31ms |
| (전송: JPEG 중앙 12,252바이트) | 인터넷 | 미실측 | 미실측 |
| JPEG 디코딩 | 서버 CPU | 0.31ms | 0.32ms |
| BGR → RGB, 0–1 정규화, 평균·표준편차 빼기 | 서버 CPU | 1.35ms | 1.38ms |
| 3프레임 창 쌓기 + GPU 로 옮기기 | CPU → GPU | 1.76ms | 1.86ms |
| 공 모델 추론(FP32, 운영 방식) | GPU | 15.17ms | 15.19ms |
| (같은 추론을 FP16 으로) | GPU | 9.98ms | 10.03ms |
| sigmoid + 히트맵을 CPU 로 | GPU → CPU | 0.18ms | 0.20ms |
| 공 덩어리 찾기 | 서버 CPU | 0.07ms | 0.07ms |
서버에서 한 프레임에 드는 시간은 약 19ms(FP32)이고 그중 추론이 80% 입니다. 남은 몫에서 큰 것은 디코딩이 아니라 정규화(1.35ms)와 GPU 로 옮기기(1.76ms) 였습니다. 512×288×3 을 float32 로 바꾸면 한 장에 1.77MB, 창 세 장이면 5.3MB 를 매번 만들어 옮깁니다. uint8 그대로 GPU 에 보내 GPU 에서 정규화하면 옮길 양이 4분의 1 이 되고 정규화도 GPU 가 합니다. 아직 바꾸지 않았고 효과는 미실측입니다.
이 표에는 폰의 카메라 잡기·실제 인코딩, 인터넷 왕복, 웹 서버 중계, 화면 그리기가 빠져 있습니다. 그 부분은 8편에서 실제 브라우저 시험으로 잰 끝단 값과 함께 정리합니다.
6. 숫자를 읽는 습관
- 중앙값과 꼬리를 함께 봅니다. 사용자가 「끊긴다」 고 느끼는 것은 p99 쪽입니다. 3절에서 예산 안이어도 p99 는 44ms 로 예산을 넘었습니다.
- 조건을 함께 옮깁니다. 「추론 10ms」 는 어떤 GPU 에서, 배치 몇으로, 다른 작업이 함께 돌 때인가에 따라 3배까지 달라집니다(6편).
- 예열 뒤를 잽니다. GPU 의 첫 실행은 커널 고르기·메모리 잡기로 길게는 수 초가 걸립니다. 이 시리즈의 측정은 모두 예열 뒤의 반복 중앙값입니다.
7. 한계
- 3절의 흉내는 추론 한 단계짜리이고 시간 분포는 가정입니다. 실제 시스템은 단계가 여럿이고 각 단계에 줄이 있습니다.
- 5절은 서버 쪽만 쟀습니다. 폰 CPU 에서 같은 줄이기·인코딩이 얼마나 걸리는지는 미실측입니다.
- 사람이 어느 정도 지연부터 「늦다」 고 느끼는지에 대한 1차 자료는 이번에 원문을 확인하지 못해 쓰지 않았습니다.
8. 우리 프로젝트와의 연결
- 탁구에서 「늦은 정답」 은 점수판이 다음 랠리가 시작된 뒤에 바뀌는 것입니다. TT Score 는 판정을 랠리 끝 뒤 1.5초 안에 내는 것을 목표로 두고 재고 있습니다(8편).
- 브라우저는 답을 못 받은 프레임이 3장이면 보내기를 쉬고, 작업자는 밀린 창을 새 창으로 덮습니다. 3절의 「최신만」 을 양쪽 끝에서 하는 셈입니다.
- 이 시리즈의 다음 글들은 표의 가장 큰 칸(추론)을 줄이는 방법입니다. 정밀도(2편), 컴파일러(3편), 작은 모델(4편), 덜 자주 돌리기(5편), 여럿이 나눠 쓰기(6편), 폰에서 돌리기(7편).
티티 한마디. 공이 튄 지 4초 뒤에 「바운드!」 라고 외치면, 아무리 정확해도 저는 이미 다음 공을 치고 있어요. 늦은 정답은 틀린 답이에요!
참고 문헌
- Li, M., Wang, Y.-X., Ramanan, D. (2020). Towards Streaming Perception. ECCV 2020. https://arxiv.org/abs/2005.10420
- 우리 측정: 이 시리즈의 실측(원자료는 저장소 밖 연구 기록), 저장소
docs/tt/PERFORMANCE.md4절 - 예제 코드와 출력: 이 폴더의
code/01_latency_queue.py,code/01_latency_queue.out.txt