Posts 임베디드 보드 추론 벤치마크에서 실제로 재야 하는 것
Post
Cancel

임베디드 보드 추론 벤치마크에서 실제로 재야 하는 것

모델이 느린 게 아니었다

Jetson 계열 보드에 검출 모델을 올리고 fps를 재면 처음에는 숫자 하나만 본다. 그런데 같은 모델, 같은 엔진으로 두 보드에서 잰 값이 이랬다.

보드입력데이터셋관측 fps
NanoUSB 카메라 (YUY2, 640×480)COCO 4클래스4 fps
NXRTSP (640×480)마스크·얼굴26~29 fps

보드 성능 차이만으로 설명하기에는 7배가 너무 크다. 실제 원인은 모델이 아니라 입력 경로에 있었다. USB 카메라에서 YUY2로 받으면 프레임마다 CPU에서 컬러 변환이 일어나고, Nano의 CPU는 그걸 감당하지 못한다. RTSP는 H.264를 하드웨어 디코더가 처리하므로 CPU가 거의 놀고 있다.

즉 “모델 추론 속도”를 재려던 벤치마크가 실제로는 컬러 변환 속도를 재고 있었다.

단계를 쪼개서 재야 한다

이후로는 항상 파이프라인을 네 구간으로 나눠 각각의 소요 시간을 따로 기록한다.

1
캡처/디코드 → 전처리(리사이즈·정규화) → 추론 → 후처리(NMS·디코딩)

각 구간의 평균 소요 시간 t를 알면 1000/t가 “이 구간 때문에 넘을 수 없는 상한”이다. 전체 fps는 그중 가장 낮은 값을 넘지 못한다. 이 표를 만들어 두면 어디를 고쳐야 하는지가 논쟁 없이 정해진다. 반대로 전체 fps 하나만 들고 있으면 모델을 더 작게 만드는 쪽으로 손이 가는데, 대개 그건 병목이 아니다.

측정할 때 주의할 점 두 가지가 있다.

첫째, GPU 동기화를 맞추고 재야 한다. CUDA 호출은 비동기라 커널 실행을 큐에 넣자마자 반환된다. 동기화 없이 감싸면 추론 시간이 비현실적으로 짧게 나오고, 그 시간은 다음 단계의 첫 동기 지점으로 밀려 엉뚱한 구간이 느려 보인다.

1
2
3
4
5
torch.cuda.synchronize()
t0 = time.perf_counter()
out = model(x)
torch.cuda.synchronize()
dt = time.perf_counter() - t0

둘째, 워밍업을 버려야 한다. TensorRT는 첫 몇 회 실행에서 워크스페이스 할당과 커널 자동 튜닝이 일어난다. 초기 10~20회를 빼지 않으면 평균이 2~3배로 부풀고, 그 상태에서 “엔진이 느리다”는 잘못된 결론이 나온다.

입력 해상도를 바꿔도 정확도가 그대로일 때

같은 4클래스 모델을 320과 640으로 각각 학습해 재봤는데 지표가 이렇게 나왔다.

입력PrecisionRecallmAP
320×32084.28%53.51%58.53%
640×64084.28%53.51%58.53%

소수점까지 완전히 같다. 해상도를 두 배로 올렸는데 지표가 한 자리도 안 바뀌는 일은 정상적으로는 일어나지 않는다. 확인해보니 평가 스크립트가 두 경우 모두 같은 캐시된 결과 파일을 읽고 있었다.

이건 벤치마크 자체보다 더 중요한 교훈이었다. 지표가 “너무 깨끗하면” 그건 좋은 신호가 아니라 파이프라인이 끊어졌다는 신호다. 조건을 바꿨는데 결과가 정확히 같다면, 결과를 믿기 전에 조건이 실제로 반영됐는지부터 확인해야 한다. 이후 평가 스크립트에는 실행 시점의 설정 해시를 결과 파일에 같이 쓰고, 읽을 때 대조하도록 바꿨다.

보드에서 재는 값과 PC에서 재는 값은 다른 것이다

PC에서 잰 mAP와 보드에서 잰 mAP가 다르면 대개 양자화 탓으로 돌리지만, 그 전에 걸러야 할 것이 두 개 더 있다.

  • 전처리 불일치: 리사이즈 보간 방식(bilinear vs nearest), 정규화 순서 (BGR/RGB, mean/std 적용 시점), letterbox 패딩 색상. 이 셋은 아무 오류 없이 조용히 정확도를 깎는다.
  • 후처리 불일치: NMS의 IoU 임계와 score 임계, 클래스별 NMS 여부. 프레임워크 기본값이 서로 다르다.

그래서 보드 성능을 논하기 전에 같은 입력 텐서 한 장을 PC와 보드에 각각 넣고 출력 텐서의 코사인 유사도를 먼저 본다. 여기서 1에 가깝게 나오지 않으면 mAP를 비교하는 것 자체가 의미가 없다. 이 단계에서 걸러지는 문제가 실무에서는 양자화 손실보다 훨씬 흔했다.

전력과 클럭 상태를 같이 기록한다

Jetson은 전력 모드에 따라 CPU 코어 수와 GPU 클럭이 달라진다. 같은 보드에서 잰 두 값이 안 맞을 때 대부분의 원인이 이것이었다.

1
2
sudo nvpmodel -q          # 현재 전력 모드
sudo jetson_clocks --show # 클럭 고정 여부

벤치마크 전에 nvpmodel로 모드를 고정하고 jetson_clocks로 클럭을 최대에 묶은 뒤, 그 상태를 결과와 함께 남긴다. 이 두 줄을 기록에 안 남기면 몇 주 뒤 같은 숫자를 재현할 수 없다.

This post is licensed under CC BY 4.0 by the author.