AEGLOSLAB ROS 2 Jazzy 대전

제12부 실시간·성능

12.6 성능 안티패턴 카탈로그

12.6.1 이 장에서 배우는 것 / 선수 지식

이 장은 참조용 카탈로그다. 앞의 106개 장에서 흩어져 나온 성능 함정을 한자리에 모아, 증상 → 원인 → 조치 → 해당 장으로 찾을 수 있게 한다.

카탈로그가 필요한 이유는 실무의 순서 때문이다. 성능 문제를 만났을 때 우리는 "어느 장에서 봤더라"부터 시작하는데, 증상에서 출발하는 색인이 있으면 그 시간이 사라진다. 11.1이 진단 전반의 색인이었다면, 이 장은 성능에 특화된 색인이다.

배우는 것:

  • 통신·복사 안티패턴.
  • 실행 모델 안티패턴.
  • QoS·큐 안티패턴.
  • tf·시간 안티패턴.
  • 그래프 구조 안티패턴.
  • 데이터·알고리즘 안티패턴.
  • 관측성·설정 안티패턴.
  • 선별 순서 — 무엇부터 볼 것인가.

선수 지식: 이 책 전반. 특히 제2부(실행 모델)·제3부(미들웨어)·11.7(측정)·12.4(지연 예산).

정직: 여기 모은 것은 흔한 패턴이며 완전한 목록이 아니다. 그리고 "안티패턴"이 항상 불리하다는 뜻은 아니다 — 맥락에 따라 정당한 선택인 경우가 있으므로, 각 항목의 "언제 괜찮은가"를 함께 읽어야 한다. 성능 수치는 제시하지 않는다(3.6.1). 이 환경에서 검증하지 않았다(목차 3).(→ 187차 확인: 대부분의 항목은 다른 장을 가리키는 색인이라 그 장들이 이미 개별적으로 실측됐다(목차 6). 이 장 자체에서는 "성능 수치를 제시하지 않는다"는 원칙을 지키는 항목 하나(12.6.8의 9·10번, 빌드 타입)에 한해 실측 수치를 직접 측정해서 채워 넣었다 — 이 값은 특정 벤치마크에 한정되며 일반화하지 않는다. 아래 실행 확인(187차) 참고. → 190차 점검: "다른 장을 가리키는 색인"이라는 위 서술을 실제로 검증했다 — 인용 대상 절을 하나씩 대조해 잘못된 인용 하나(12.6.7의 11번, GPU·CPU 왕복에 잘못된 7.8.5를 인용)와 더 정확한 인용으로 바꿔야 할 것 하나(12.6.8의 4번)를 찾아 고쳤고, 이 장의 두 항목(인트라프로세스·노드 분할)에 14.1.7의 실측 근거를 추가했다. 컨테이너 실행은 없었다 — 문서 대조만으로 확인 가능한 범위였다. → 193차 점검: 190차가 표본만 보고 남겨 둔 나머지 인용을 마저 전수 대조했다(약 49개 확인) — 같은 종류의 잘못된 인용을 6곳 더 찾아 고쳤다(12.6.3·12.6.4·12.6.5·12.6.6·12.6.7의 총 8개 셀 — 1.9.7→1.7.3, 2.7.5→2.7.4, 5.1.5→5.1.9/5.1.6, 5.2.3→5.1.9(2곳), 1.6.4→1.6.6). 새로 놓친 인용은 찾지 못했다 — 이번엔 전부 "잘못된 절을 가리킴" 유형이었다. 나머지 약 39개는 정합적이었다. 이번에도 컨테이너 실행은 없었다.)

12.6.2 선별 순서

증상을 만났을 때 보는 순서다.

전달 자체 대기가 김 처리가 김 불규칙 성능 문제 1. 측정 · 구간 분해 11.7.4 어디가 느린가? 통신 · QoS · 복사 실행 모델 알고리즘 · 데이터 지터 원천 · 자원 해당 절 참조
증상먼저 볼 절
데이터가 안 옴·유실12.6.5(QoS)
지연이 큼12.6.3(복사)·12.6.4(실행)
CPU가 높음12.6.3·12.6.7
주기가 불규칙12.6.4·12.6.8
네트워크 포화12.6.3·12.6.7
메모리 증가12.6.5·12.6.7
시작이 느림12.6.6

12.6.3 통신·복사

#안티패턴증상조치장
1대용량 원본을 그대로 발행대역 포화·CPU압축·다운샘플·전송 방식7.2.7·7.8
2인트라프로세스 미사용불필요한 직렬화컴포넌트 + use_intra_process_comms2.7.5(→ 190차 점검: 14.1.7에 이 패턴의 가장 구체적인 실측 근거가 있다 — 별도 프로세스 배치는 p99 60,626~73,272µs의 거대한 꼬리 지연을 보였지만 같은 컴포넌트를 컨테이너로 묶으니 p99 194µs로 약 310~370배 줄었다(180·185~187차).)
3shared_ptr 발행이동 최적화 상실unique_ptr2.7.5·12.3.5(→ 193차 정정: 2.7.5는 틀린 인용이다 — 그 절은 "알려진 제약"(TRANSIENT_LOCAL·deadline 등)을 다루지 shared_ptr/unique_ptr와 무관하다. 정확한 출처는 2.7.4("복사 0의 정확한 조건") — "규칙 1번: unique_ptr로 발행하라"가 바로 이 절에 있다.)
4불필요한 구독자대역 배수 증가필요한 것만·운영 중 도구 끄기11.4.6
5큰 메시지를 고빈도로대역·직렬화빈도 감소·ROI7.8.2
6이미 압축된 것을 재압축CPU 낭비판단11.2.5
7포인트클라우드 전 필드 전송대역필요 필드만7.4.4
8제로 카피 조건 미충족이득 없음고정 크기·설정 확인12.3.5
9이미지 원본 기록디스크 용량 급증압축·선별11.3.2

언제 괜찮은가: 개발·디버깅 중 일시적으로는 원본 전송이 편리하다. 문제는 그것이 운영에 남는 것이다.

12.6.4 실행 모델

#안티패턴증상조치장
1콜백에서 긴 작업다른 콜백이 막힘작업 분리·별도 스레드2.3.4
2콜백에서 블로킹 대기교착·정지비동기·별도 그룹1.9.7(→ 193차 정정: 틀린 인용이다 — 1.9.7은 YAML 파일 문법을 다루는 절이라 교착과 무관하다. 정확한 출처는 1.7.3("데드락 — ROS 2에서 가장 비용이 큰 실수") — 15.3도 같은 주장에 이 절을 정확히 인용한다.)
3콜백 안에서 서비스 동기 호출교착비동기 호출1.9.7(→ 193차 정정: 위와 같은 오인용, 정확한 출처는 1.7.3.)
4멀티스레드인데 기본 그룹병렬 안 됨Reentrant 그룹 분리2.3.3
5모든 콜백 한 실행기우선순위 없음경로별 실행기2.4·12.4.6
6스레드 과다문맥 전환·경합필요한 만큼2.2.6
7타이머 주기 < 콜백 시간적체·지터주기 조정·최적화2.5.8
8spin_some 오용처리 누락·바쁜 대기spin·구조 재검토2.2.4
9RT 스레드에서 로깅·할당지터카운터·비RT 발행12.3.6
10주기 구동으로 지연 추가반주기 손실사건 구동(해당 경로)12.4.5
11콜백에서 sleep실행기 점유타이머2.5.8

언제 괜찮은가: 긴 콜백도 그것이 유일한 콜백이고 주기가 충분하면 문제없다. 노드가 커지면서 문제가 되므로, 구조를 미리 설계해 두는 것이 예방이다.

12.6.5 QoS·큐

#안티패턴증상조치장
1센서에 RELIABLE재전송·지연·적체BEST_EFFORT1.6.3.1
2큐 깊이 과다오래된 데이터·메모리depth=1(최신 중요 시)1.4.4·12.4.4
3큐 깊이 과소유실소비 속도 확인1.4.4
4TRANSIENT_LOCAL에 대용량메모리·재전송VOLATILE·크기 축소1.6.3.3
5무한 이력메모리 증가KEEP_LAST1.6.3
6QoS 불일치데이터 0topic info -v1.5.4
7무선에서 RELIABLE 대용량꼬리 지연 폭증(p99)압축·다운샘플링이 먼저 — 큰 메시지엔 BEST_EFFORT도 무효3.9.13
8데드라인·라이브라인 남용오버헤드·복잡필요한 곳만1.6.4(→ 193차 정정: 1.6.4는 큐 깊이(depth) 설계를 다루는 절이라 데드라인·라이브라인과 무관하다. 더 정확한 출처는 1.6.6("시스템 차원의 QoS 관리") — deadline() 체이닝 실사용 예가 있다.)

언제 괜찮은가: 기록·안전 명령은 RELIABLE이 맞다. 절충의 기준은 "최신이 중요한가, 전부가 중요한가"다(12.4.7).

12.6.6 tf·시간·그래프 구조

#안티패턴증상조치장
1tf 고빈도 발행대역·CPU필요 주기로·정적은 static5.1.5(→ 193차 정정: 5.1.5는 "시간 축과 보간"(tf 조회의 시간 보간)을 다루지 발행 빈도와 무관하다. 정확한 출처는 5.1.9("성능 고려") — "조회 빈도 감소" 완화책이 표에 정확히 있다.)
2정적 변환을 /tf로 반복낭비/tf_static5.1.5(→ 193차 정정: 같은 오인용, 정확한 출처는 5.1.6("정적 변환 — 왜 별도인가") — "변하지 않는 변환을 계속 발행하는 것은 낭비다"로 시작한다.)
3tf 조회를 콜백마다 반복CPU캐시·일괄5.2.3(→ 193차 정정: 5.2.3은 Time(0)의 조회 정확도 의미론을 다루지 캐싱과 무관하다. 정확한 출처는 5.1.9 — "조회 결과 캐싱"이 완화책 표에 정확히 있다.)
4버퍼 시간 과다메모리필요한 만큼5.2.2
5프레임 과다조회 비용·시각화정리5.1.8
6노드를 과도하게 분할경계 비용 누적컴포넌트화2.7.3·12.4.5(→ 190차 점검: 14.1.7도 함께 보라 — 컴포넌트 컨테이너로 묶었을 때의 실측 개선폭(p99 약 310~370배)이 여기 있다.)
7노드 수 과다디스커버리 폭증통합·Discovery Server3.3.6·3.4
8디스커버리 설정 방치시작 지연·대역설정·서버3.10.4
9큰 파라미터를 자주 갱신오버헤드필요 시만1.9.6
10서비스로 스트리밍부적합토픽·액션1.8.3

언제 괜찮은가: 노드 분할은 개발·재사용에 이롭다. 지연이 문제가 되는 경로만 합치면 되며, 컴포넌트화가 두 목적을 함께 만족시킨다.

12.6.7 데이터·알고리즘·자원

#안티패턴증상조치장
1전 해상도로 전 처리CPU·지연다운샘플·ROI7.4.5·7.8.4
2매 프레임 전체 재계산CPU증분·캐시8.3.5
3코스트맵 과대·고해상도CPU·메모리크기·해상도 조정8.3.8
4무제한 계획 시간지연 폭증시간 제한(anytime)9.2.8·12.1.3
5불필요한 좌표 변환 반복CPU한 번에·일괄5.2.3(→ 193차 정정: 12.6.6 1~3번과 같은 오인용 — 5.2.3은 캐싱과 무관하다. 정확한 출처는 5.1.9("성능 고려", "조회 결과 캐싱" 완화책).)
6복사 남발(값 전달)CPU·메모리참조·이동12.3
7RT 경로 동적 할당지터사전 예약12.3.6
8메모리 누수장기 운전 실패RSS 추이 감시11.7.6
9CPU 여유 없음지터 급증여유 확보·부하 분산12.2.7
10디스크 I/O 병목기록 유실·정지SSD·기록량11.2.6
11GPU·CPU 왕복 과다지연파이프라인 유지7.8.5(→ 190차 점검: 이 인용이 틀렸다 — 7.8.5는 역압·큐 설계(드롭-if-busy)를 다루지 GPU·CPU 왕복과 무관하다. GPU 가속 파이프라인 자체는 이 책이 명시적으로 범위 밖에 두고 자매서 nvidia에 넘긴 주제라(7.8.1), 이 책 안에는 해당하는 절이 없다.)
12열 스로틀링 방치장시간 열화냉각·측정12.2.7

언제 괜찮은가: 정확도가 최우선인 오프라인 처리에서는 전 해상도 처리가 옳다. 문제는 실시간 경로에 그것을 두는 것이다.

12.6.8 관측성·설정

#안티패턴증상조치장
1고빈도 콜백에서 로깅CPU·I/O·지터스로틀 로깅·레벨1.11.5
2DEBUG를 운영에로그 폭주·디스크레벨 관리1.11.4
3운영 중 RViz2 구독대역·CPU필요 시만11.4.6
4온보드에서 GUI제어 방해원격11.4.5(→ 190차 점검: 더 정확한 출처는 11.4.6이다 — 이 항목과 정확히 일치하는 "개발 PC에서 실행 + 네트워크로 토픽(권장)" 권고표가 그쪽에 있다. 11.4.5는 RViz2가 느려지는 원인 목록이라 방향은 맞지만 이 항목의 직접 출처는 아니다.)
5모든 토픽 상시 기록디스크·CPU계층적 기록11.3.2
6기록 중 압축CPU 부족 → 유실후처리11.2.5
7트레이싱 상시오버헤드필요 시만11.6.7
8측정 도구가 대상 교란오판유무 비교11.7.8
9디버그 빌드로 운영큰 성능 손실Release·RelWithDebInfo4.5.6
10최적화 플래그 미설정성능 손실빌드 설정 확인4.5.6
11use_sim_time 오설정이상 동작전체 일관1.10.9
12절전·거버너 방치지터performance12.2.7

실행 확인(187차) — 9·10번("디버그 빌드로 운영"·"최적화 플래그 미설정")에 이 장이 원칙상 제시하지 않는 성능 수치(12.6.1)를, 이 항목 하나에 한해 실측으로 채워 넣었다. CPU 위주 연산(350×350 순수 삼중 루프 행렬곱, BLAS 미사용)을 하는 최소 rclcpp 노드를 만들어, CMAKE_BUILD_TYPE을 ① 미지정 ② Debug ③ Release로 각각 별도 워크스페이스에서 빌드했다. 실제 컴파일 플래그를 flags.make로 직접 확인하니 — 미지정은 -std=gnu++17뿐(최적화 플래그 전무), Debug는 -g -std=gnu++17(역시 최적화 플래그 없음, 디버그 심벌만 추가), Release는 -O3 -DNDEBUG -std=gnu++17 — 미지정이 Release가 아니라 사실상 Debug와 같은 컴파일 결과물임을 소스 증거로 확인했다(4.2.10·4.5.13의 "CMAKE_BUILD_TYPE 미지정" 함정이 정확히 이것). 실행 결과: 미지정 301.796ms, Debug 299.717ms(둘이 0.7% 이내로 사실상 동일), Release 47.730ms — Release가 미지정·Debug 대비 약 6.3배 빠르다. 정직한 단서: 이 6.3배는 이 특정 CPU 위주 합성 벤치마크(순수 삼중 루프 행렬곱)에서 측정한 값이며, 이 장의 "성능 수치를 제시하지 않는다"는 원칙대로 일반화하지 않는다 — I/O 위주이거나 이미 최적화된 라이브러리 호출이 지배하는 워크로드는 격차가 훨씬 작을 수 있다. 원본: tools/gap_harness/gap187/build_type_bench/(pkg/·results/compiler_flags.log·results/run_ws_{none,debug,release}.log·findings_summary.txt).

견해: 9번(디버그 빌드로 운영)이 의외로 자주 일어난다. 개발 중 디버그로 빌드해 놓고 그대로 현장에 가져가면, 원인 불명의 전반적 성능 저하를 겪는다. 빌드 타입을 확인하는 것을 배포 점검에 넣으라(4.7).

견해: 1번(고빈도 로깅)은 이 책이 여러 번 경고했지만 계속 나온다. 개발 중 넣은 RCLCPP_INFO가 남아 1 kHz로 실행되는 경우가 흔하다. RCLCPP_*_THROTTLE을 기본 습관으로 삼으면 예방된다(1.11.5).

12.6.9 오해와 진실

성능에 관한 흔한 오해를 정리한다.

오해진실
"멀티스레드로 하면 빨라진다"콜백 그룹이 맞아야(2.3.3)
"노드를 나누면 병렬이 된다"경계 비용이 추가된다(12.4.5)
"RELIABLE이 안전하다"센서에는 해롭다(1.6.3.1)
"큐를 키우면 안전하다"지연이 커진다(12.4.4)
"RT 커널이면 실시간"전 층이 필요(12.1.4)
"알고리즘이 병목"대개 아니다 — 측정하라(12.4.5)
"평균이 좋으면 괜찮다"꼬리가 문제(11.7.3)
"시뮬 성능 = 실기 성능"다르다(10.6.2)
"개발 PC에서 되니까 괜찮다"임베디드는 다르다(11.7.7)

견해: "알고리즘이 병목"이라는 오해가 가장 많은 시간을 낭비시킨다. 실제로는 복사·직렬화·실행기 대기·QoS 재전송이 지배하는 경우가 많다. 11.7.4의 구간 분해를 먼저 하면 이 낭비가 사라진다.

12.6.10 제12부를 마치며

실시간 · 성능 정의 · 한계 12.1 OS 층 12.2 메모리 층 12.3 설계: 지연 예산 12.4 결정성 12.5 안티패턴 회피 12.6

제12부가 반복한 것은 셋이다.

  1. 실시간은 빠름이 아니라 보장이다. 평균이 아니라 최악값이 사양이고, 그 보장은 전 경로에서 나온다. 한 층만 수정해서는 얻어지지 않는다(12.1·12.2·12.3).
  2. 요구를 계층으로 나누고, 하드 요구는 아래로 밀어라. 모든 것을 ROS 2에서 실시간으로 만들려는 시도가 어려움을 자초한다. 하드 루프는 MCU·드라이브에, 안전은 독립 경로에 두는 것이 표준적 방법이다(12.1.7·12.1.8).
  3. 설계하고, 측정하고, 그다음 최적화하라. 지연 예산이 목표를 주고, 구간 측정이 순서를 정하고, 그 결과가 어느 층을 고칠지 알려준다. 순서를 뒤집으면 비용만 든다(12.4·11.7).

그리고 이 부가 앞부분에 부여하는 의미 — 제2부(실행 모델)에서 다룬 실행기·콜백 그룹, 제3부(미들웨어)의 QoS, 2.7의 컴포넌트는 모두 여기서 성능 도구가 된다. 구조를 이해한 것이 성능을 통제하는 힘이 되는 것이 ROS 2의 성격이다.

12.6.11 체크포인트

  • 성능 문제 선별의 첫 단계와, 증상별로 먼저 볼 절을 말할 수 있는가.
  • 통신·복사 안티패턴 아홉 가지 중, 인트라프로세스 관련 셋은?
  • 실행 모델 안티패턴에서 "교착"으로 이어지는 것 둘은?
  • "멀티스레드인데 병렬이 안 되는" 원인과 조치는?
  • QoS 안티패턴 여덟 가지와, 절충의 판단 기준은?
  • tf 관련 안티패턴 다섯 가지를 들 수 있는가.
  • 노드 분할의 비용과, 그것을 완화하는 방법은?
  • 데이터·알고리즘 안티패턴에서 "장기 운전 실패"로 이어지는 것은?
  • 관측성 안티패턴 중, 배포 점검에 넣어야 할 것은?
  • 고빈도 로깅을 예방하는 습관은?
  • 성능에 관한 아홉 가지 오해를 반박할 수 있는가.
  • "가장 많은 시간을 낭비시키는 오해"와 그 대처는?
  • 제12부가 반복한 세 원칙을 말할 수 있는가.

여기까지가 제12부 실시간·성능이다. 정의와 한계(12.1), OS(12.2), 메모리(12.3), 설계(12.4), 결정성(12.5), 안티패턴(12.6).

지금까지 로봇을 동작하게 만들고, 관측하고, 시간을 지키게 했다. 그런데 아직 다루지 않은 질문이 있다 — 이 시스템은 안전한가? 여기서 안전은 물리적 안전(8.7)이 아니라 보안이다. 로봇이 네트워크에 연결되어 있다면 누가 명령을 보낼 수 있는가, 데이터를 볼 수 있는가, 노드를 사칭할 수 있는가. 제13부 보안(13.1~13.5) 의 주제다.