19.8.2.2. 메시지 큐 오버플로우(Message Drop) 현상 식별 및 병목 구간 분석
uorb top 명령어 화면에서 개발자의 등골을 서늘하게 만드는, 피처럼 시뻘건 텍스트로 깜빡이는 최우측 열이 있다. 바로 LOSS (또는 PX4 버전에 따라 DROP) 열이다. 이 끔찍한 숫자는 드론의 두뇌(VFS) 어딘가 혈관이 막혀, 금쪽같은 센서 데이터들이 EKF 항법 필터에 가닿기도 전에 버려져 바닥에서 썩어가고 있다는 파멸의 징조이다.
우리가 19.2.1 단원에서 퍼블리셔를 정의할 때 ORB_DECLARE와 큐(Queue) 사이즈 개념을 배웠다. uORB 링 버퍼 매커니즘은 본질적으로 데이터를 임시로 담아두는 ‘물항아리(Queue length)’ 크기가 물리적으로 제한되어 있다. 예를 들어 센서 퍼블리셔가 1000Hz로 1초에 천 번 물을 부어버리는데, 저 멀리서 물을 퍼내야 할 구독자 데몬(Subscriber) 스레드가 인터럽트 락(Lock)에 걸려 1초에 백 번밖에 물을 퍼가지 못하면 어떻게 될까?
당연하게도 항아리에서 물이 넘쳐흘러(Overflow) 바닥에 버려진다. uORB 커널은 이 넘쳐버려 영구 삭제된 데이터 패킷 구조체들의 개수를 런타임에 소름 끼치도록 정확히 카운팅하여 이 LOSS 열에 전시한다.
1. LOSS(Drop) 열의 현장 디버깅 해부학
uorb top 화면에서 만약 다음과 같은 라인을 목격했다고 가정해 보자.
TOPIC NAME INST #SUB RATE #VAL SIZE LOSS
sensor_gps 0 3 10 1 124 0
vehicle_local_position 0 12 100 12 124 0
my_custom_sensor 0 1 1000 1 40 14502 <-- !!!
my_custom_sensor 방의 LOSS 카운트가 무려 14502번을 찍고 계속해서 미친 듯이 올라가고 있다. 이 1만 4천 개의 텍스트 숫자는 14,000개의 부동소수점(float) 센서 스냅샷 구조체가 메모리 바닥에 버려졌음을 뜻한다.
이 병목 현상을 타개하고 큐 오버플로우를 봉합하기 위해, 하드코어 아키텍트는 즉각적으로 세 가지의 범인 색출 심문 절차에 돌입한다.
- 퍼블리셔의 광기 (Publisher Frequency Out of Control)
RATE열을 보라. 퍼블리셔가 무려1000Hz로 데이터를 쏘아대고 있다. 이 센서가 그렇게 빠른 주파수를 가질 필요가 있는가? 타이머 세팅의 버그로 인해 퍼블리셔가 데드 루프를 돌며 쓸데없이 1000번씩 동일한 데이터를 중복 발송하고 있지 않은가?
- 구독자의 직무유기 (Subscriber Polling Bottleneck)
- 내가 짠 구독자 데몬(
#SUB: 1)의Run()콜백 함수 내부 로직이 너무 무겁다. 수백 줄짜리 수학 연산 행렬(Matrix) 계산을 콜백 안에 때려 넣었기 때문에, 1번 데이터를 빼먹고 연산을 마치는 데 50ms가 걸려버린다. 그사이 퍼블리셔가 50개의 새 데이터를 쏴버려 큐 항아리가 터져 나간 것이다. 이럴 땐 무조건 수학 연산 블록을 꺼내어 다른 백그라운드 Worker 스레드로 던져버려야(Offloading) 한다.
- 큐 길이의 물리적 한계 (Queue Length Tuning)
#VAL(큐 사이즈 설정값)이 너무 작을 수 있다. 퍼블리셔가 가끔 한 번씩 데이터 10개를 1밀리초 구구단(Burst)으로 쏴버리는데, 큐 사이즈가 1로 설정되어 있다면 구독자가 아무리 빨라도 9개의 데이터는 무조건 유실된다. 해당 센서의 피크 트래픽을 감당할 수 있도록 구조체 선언 부의 큐 사이즈를 늘려(Tuning) 여유 버퍼 공간을 확보해야 한다.
uorb top은 이처럼 코드만 봐서는 전 세계 제일의 해커라도 죽었다 깨어나도 알 수 없는, **‘실행 시간(Runtime) 레이스 컨디션의 물리학적 충돌’**을 색출하는 단 하나의 나침반이다. 기체가 하늘에서 불안정하게 흔들린다면, 소스 코드를 뒤적거리기 전에 당장 NSH쉘에 접속해 uorb top의 핏빛 LOSS 열부터 스캐닝하며 어떤 모듈의 혈관이 막혔는지 찾아내는 것이 진정한 PX4 엔지니어의 생존 공식이다.