[IT 심층 분석]
ARM NEON SIMD 명령어로 영상 처리 루프를 8배 가속한 실전 저수준 최적화 기법
김민준 · IT 시스템 엔지니어|
모바일 기기에서 실시간으로 동작하는 영상 미화 필터 파이프라인의 처리 속도가 목표치인 30fps를 안정적으로 충족시키지 못하는 문제가 있었습니다. C++로 작성된 코어 알고리즘은 이미 컴파일러 최적화 플래그를 최대로 켜둔 상태였고 알고리즘 자체의 시간 복잡도도 나쁘지 않았습니다. 그럼에도 프로파일러는 특정 픽셀 색상 공간 변환 루프가 전체 처리 시간의 40% 이상을 잡아먹고 있다는 사실을 지목했습니다. 이 수준에서의 최적화는 더 이상 고수준 알고리즘 개선의 영역이 아니었습니다. 직접 CPU 명령어 수준으로 내려가는 SIMD(Single Instruction Multiple Data) 최적화가 필요한 시점이었습니다.
ARM 계열 프로세서에서 SIMD 연산을 활용하는 도구가 바로 NEON 인트린직(Intrinsics)입니다. NEON은 128비트 너비의 벡터 레지스터를 제공하는데 이를 이용하면 예컨대 uint8 자료형의 픽셀 값 16개를 단 하나의 레지스터에 담아두고 하나의 명령어로 동시에 동일한 연산을 처리하는 것이 가능합니다. 기존 스칼라 방식의 for 루프가 픽셀 1개씩 순차 처리하던 것을 NEON을 사용하면 한 번에 16개씩 병렬 처리하는 셈이니 이론적 최대 가속비는 16배에 달합니다.
색상 공간 변환 루프를 NEON 인트린직 코드로 재작성하는 과정은 쉽지 않았습니다. vld1q_u8 함수로 메모리에서 픽셀 16개를 벡터 레지스터에 한꺼번에 적재하고 vmulq_s16, vaddq_s16 같은 벡터 연산 함수들로 YUV-RGB 변환 행렬 계산을 수행하며 vst1q_u8로 결과를 다시 메모리에 저장하는 방식이었습니다. 데이터 정렬(Alignment)에 주의를 기울이고 벡터 로드-스토어 오버헤드를 최소화하기 위해 루프 언롤링(Loop Unrolling)을 결합하자 같은 알고리즘임에도 스칼라 버전 대비 정확히 8.3배의 처리 속도 향상이 측정되었습니다. 30fps를 안정적으로 넘어 60fps까지 구현 가능한 여유 마진까지 확보할 수 있었습니다. 저수준 최적화는 고통스럽지만 알고리즘과 하드웨어 사이의 가장 가까운 거리에서 가장 강력한 성과를 끌어낸다는 것을 체험하는 희열이 있습니다.