TrotVote
[IT 심층 분석]

셔터 릴리즈 랙(Shutter Release Lag)을 0.05초 이하로 줄이기 위한 스레드 우선순위 재할당과 OS 개조

김민준 · IT 시스템 엔지니어|
스포츠 역학 촬영이나 기자의 사진 현장에서 셔터를 누르고 실제 센서가 프레임을 기록하기까지 걸리는 미세한 딜레이 시간 즉 셔터 릴리즈 랙은 카메라의 생명과도 직결된 척도입니다. 당사 신규 어플리케이션은 사용자 경험(UX) 관점에서 치명적인 단점이 존재했는데 UI 버튼을 터치한 후 센서 캡처 콜백이 불리기까지 대략 250밀리초라는 길고도 지루한 정적 시간이 존재한다는 보고가 끊이지 않았습니다. 피사체는 이미 화각을 벗어나고 난 뒤에야 사진이 찍히는 촌극을 막기 위해서 목표 수치를 극한의 영역인 0.05초 50밀리초 이하로 끌어내리는 강도 높은 실시간 스케줄링 리팩터링 미션이 시작되었습니다. 이는 단순히 코드를 예쁘게 고치는 문제가 아니라 운영체제의 태스크 스케줄링 심장부를 헤집어놔야만 가능한 고난도 작업이었습니다. 가장 최우선적인 적군은 메인 스레드인 UI 스레드의 비대함이었습니다. 셔터 버튼을 누르는 이벤트 핸들러가 수많은 터치 피드백 애니메이션과 뷰 업데이트 로직에 엉켜 있어서 터치 인터럽트를 OS 프레임워크가 전달받고 이를 카메라 백엔드로 패스다운해 센서 캡처 플래그를 올리는 일련의 과정에 엄청난 순차 블로킹이 발생하고 있음이 안드로이드 시스템 트레이스(Systrace) 분석으로 자명해졌습니다. 저는 기존에 엉겨 붙어 있던 UI 응답 스레드에서 카메라 구동 트리거 메서드를 완벽하게 물리적으로 도려내는 분리 수술을 감행했습니다. 셔터 터치 좌표 이벤트가 발생하자마자 리스너는 그 어떤 애니메이션 그리기 연산도 제쳐두고 오프 스크린의 백그라운드 전담 카메라 제어 스레드로 신호를 바이패스 해버리도록 이벤트 파이프라인의 구조를 변혁했습니다. 그럼에도 불구하고 OS 자체 스케줄러가 데몬 스레드들 간의 타임 슬라이스(Time Slice)를 공평하게 나누어 먹다 보니 수 밀리초 단위의 미세한 문맥 교환 지연은 계속해서 발목을 잡았습니다. 저는 C++ 네이티브 레벨의 하단 프레임워크 코드로 곧바로 잠입했습니다. 셔터 캡처 요청 인터럽트를 처리하는 핵심 스레드와 센서의 하프 인터럽트를 담당하는 커널 데몬들의 스케줄링 클래스를 일반 데스크탑 수준인 SCHED_OTHER에서 경직된 실시간 클래스인 SCHED_RR 즉 라운드 로빈 계열 최상위 정책으로 강제 승격시켜버리는 시스템 콜을 명시했습니다. 커널 내에서 다른 백그라운드 작업들이 얼마나 급박하게 CPU를 원하든 카메라 캡처 스레드가 깨어나는 순간 다른 모든 평민 스레드들은 즉시 선점당하고 멈추게 되는 지독한 우대 정책을 수립해버린 것입니다. 결과는 가히 충격적이었습니다. 방해물들이 모조리 제거된 메인 도로를 질주하듯 화면 UI 측 터치 신호가 센서 레지스터의 캡처 핀을 활성화시키기까지 소요되는 총 딜레이가 42밀리초까지 폭발적으로 떨어졌습니다. 250밀리초의 답답한 거북이 같던 앱은 화면에 손가락이 닿기도 무섭게 즉각적으로 찰나의 장면을 바이트 스트림으로 동상처럼 얽어매는 맹수로 거듭났습니다. 이번 프로젝트를 통해 소프트웨어 스택이 두꺼워진 현대 OS에서 밀리초의 승리를 거두기 위해서는 코더의 차원을 넘어서 커널 스케줄러를 직접 윽박지르고 협상하는 투견 같은 시스템 엔지니어링 감각이 없이는 절대 불가능하다는 진리를 뼈저리게 도출해낼 수 있었습니다.