apple/iOS, UIKit, Documentation

iOS 히치(hitch)에 대해서 알아보기

lgvv 2024. 12. 26. 15:59

Understanding hitches in your app

 


사용자 인터페이스에서의 중단과 히치(hitch)에 대해 알아보기

인간의 인식은 화면에서의 움직임 중단에 매우 민감하며, 화면에서 부드러운 움직임이 짧은 시간 동안 멈추는 경우, 단 몇 밀리초라도 쉽게 눈에 띌 수 있음. 이러한 중단을 히치(hitch)라고 부름

  • 히치는 스크롤이나 드래그 같은 지속적인 인터랙션 또는 애니메이션 중에 발생
  • 각 히치는 사용자 경험에 영향을 미치므로 앱에서 가능한 한 히치를 최소화하는 것이 중요.

히치란?

  • 움직임 중단은 화면이 예상한 속도로 업데이트되지 않을 때 발생하며, 프레임이 준비되지 않아 화면이 제때 업데이트되지 않으면 프레임이 지연됨.
  • 지연된 프레임은 종종 시스템이 이후 한두 개 이상의 프레임을 건너뛰게 만드는데, 이를 프레임 드롭(frame drop)이라고도 함. 하지만 프레임 드롭은 시스템이 지연된 프레임을 처리하는 여러 방법 중 하나일 뿐이며, 모든 히치가 프레임 드롭을 유발하는 것은 아님.

히치가 발생하는 이유

  • 프레임이 늦어지는 주요 원인은 렌더 루프의 어느 지점에서 지연이 발생하기 때문.
  • 이러한 지연은 대부분 메인 스레드의 커밋(commit) 단계에서 발생하며, 이를 커밋 히치(commit hitch)라고 부름
  • 렌더(render) 단계에서 발생하는 경우, 이를 렌더 히치(render hitch)라고 부름

 

Tip

히치는 행(hang)과 관련이 있음. 메인 스레드가 응답하지 않으면 둘 다 발생할 수 있기 때문.

  • 메인 스레드에서 오래 실행되는 작업이 커밋 히치(commit hitch)를 유발할지 아니면 행(hang)을 유발할지는 메인 스레드 작업이 어떤 사용자 상호작용과 겹치는지에 따라 달라짐.
  • 행 감지 도구는 메인 스레드가 장시간 응답하지 않는 모든 경우를 감지하므로, 주요 커밋 히치의 원인도 함께 감지할 수 있음. 앱의 특정 영역에서 발생하는 모든 행 문제를 해결하면 많은 히치를 제거할 가능성이 높음

 

렌더루프(Render Loop) 이해하기

 
메인 스레드는 한 번에 하나의 이벤트만 처리하지만, 렌더 루프는 여러 단계를 통해 프레임을 준비함.

  • 그럼에도 불구하고 각 스테이지 마다 한 번에 하나의 프레임만 처리하지만, 모든 단계가 협력하여 여러 프레임을 병렬로 작업.
  • 각 스테이지는 화면 새로 고침에 맞춰 프레임을 준비하기 위해 스테이지에 단계에 프레임을 전달하는 엄격한(tight) 데드라인이 존재함.

 

애플 공식 사이트

 
120Hz 화면 새로 고침률 예시

  • 120Hz로 새로 고침되는 디스플레이는 초당 120번 업데이트되며, 이는 8.3ms마다 한번 갱신된다는 의미
  • 앱은 UI를 8.3ms마다 업데이트하고, 새로운 프레임을 렌더링 함.
  • 기본적으로 각 단계도 작업을 완료하기 위해 최대 8.3ms의 시간이 주어짐.
  • 60Hz의 경우에는 초당 60번이 업데이트 되므로 16.6ms당 한번 갱신된다는 의미.

히치의 원인

  • 단 몇 밀리초의 지연이 발생해도 해당 스테이지에서 데드라인을 놓칠 수 있음.
  • 렌더 서버는 별도의 프로세스이지만, 앱을 대신하여 작업을 수행하며, 작업이 너무 복잡한 경우 시간 내에 완료할 수 없음.
  • 히치를 찾고 이해하려면 메인 스레드에서 수행되는 작업렌더 서버에서 수행되는 작업을 모두 살펴볼 필요가 있음


디스플레이 새로 고침 간격과 연관된 마감 시간 이해하기

Apple 디바이스는 reguler(정기적으로) 혹은 vaibale(동적으로)인 형태로 디스플레이를 갱신함.

  • 유저 인터렉션이 발생한 동안에는 디스플레이가 기기가 지원하는 최대 새로 고침 속도로 갱신
  • 새로운 화면 콘텐츠가 없을 때는 일부 디스플레이가 갱신 속도를 줄이기도 함.
  • 디스플레이가 업데이트되는 시점을 수직 동기화(vsync)라고 함.
  • vsync가 발생할 때마다 새 프레임이 준비되어 있어야 디스플레이 드라이버가 픽셀 업데이트를 시작할 수 있음.

프레임 준비 과정

  • 각 프레임에서 렌더 루프는 새로운 프레임을 표시하기 위해 미래의 특정 vsync를 대상으로 함.
  • 이를 프레임 표시 시간(presentation time)이라고 부름.
  • 이 목표 시간부터 거꾸로 계산하여 중간 마감 시간들이 설정됨.
  • 렌더 루프는 여러 스테이지로 구성되며, 각 스테이지는 고유한 데드라인을 가짐.
  • 이 과정은 앱의 메인 스레드에서 UI 업데이트를 시작으로 진행.

 

애플 문서

 
메인 스레드의 역할

  • 앱의 메인 스레드는 이벤트를 처리하고 새로운 UI 업데이트를 계산
  • UI 프레임워크는 UI의 모든 변경 사항을 수집하고 새 상태를 Core Animation commit을 통해 렌더 서버로 전달함.
  • 명확한 사용자 상호작용에서 100ms 이상의 지연만 눈에 띄는 경우와 달리, 유동적인 움직임에서는 단 몇 밀리초의 지연도 사용자가 즉각적으로 느낄 수 있음.
  • 몇 밀리초의 지연으로 인해 메인 스레드 작업이 커밋 마감 시간을 놓치면, 눈에 띄는 히치(hitch)가 발생.


커밋 마감 시간 이후의 렌더링 프로세스 이해하기

커밋 데드라인이 지난 직후 렌더 서버는 앱의 UI 표현을 화면에 표시할 비트맵으로 변환하는 작업을 시작하며 이 단계는 두 가지로 나뉨.

  • CPU 단계: 렌더링에 필요한 작업을 사전 계산
  • GPU 단계: 실제 렌더링 작업을 실행하고 새 프레임을 렌더 버퍼에 렌더링

렌더 서버가 렌더링을 완료한 후, 다음 vsync 간격 동안 디스플레이 드라이버가 준비된 렌더 버퍼를 읽고 이를 기반으로 화면을 업데이트를 함.

  • Presentation time
    • 특정 프레임이 화면에 표시되도록 예정된 시간.
    • 이 시간에 맞추지 못하면 프레임이 다음 vsync까지 대기하게 되며, 결과적으로 화면에 늦게 나타남.
  • Commit deadline
    • 렌더 서버가 프레임을 렌더링할 충분한 시간을 확보하기 위해 커밋이 완료되어야 하는 시간.
    • 렌더 서버는 커밋 마감 시간 이후 즉시 렌더링을 시작.
    • 앱이 커밋 마감 시간을 놓치면 렌더 서버는 다음 커밋 마감 시간(보통 다음 vsync)까지 대기하며, 이로 인해 프레임이 다시 늦어짐.
  • Begin time
    • 특정 프레임에 대해 UI 업데이트를 커밋할 수 있는 가장 빠른 시간.
    • 이 시간 이전에 커밋하면 이전 프레임의 UI 업데이트에 간섭할 수 있음.
    • 일반적으로 프레임 B의 Begin time은 앞선 프레임 A의 Commit deadline과 일치.
  • 프레임에 대한 자세한 개념은 멀티미디어 공학 이론 참고


렌더 서버는 커밋 마감 시간 직후 작업을 시작하고 디스플레이 드라이버는 vsync에 맞춰 디스플레이 업데이트를 시작하지만, 다른 작업들이 해당 시간과 반드시 정렬되어 수행되는건 아님.

  • 렌더 서버 작업의 동기화
    • 렌더 서버는 커밋 마감 시간 직후 작업을 시작.
    • 디스플레이 드라이버는 vsync 간격에서 화면을 업데이트하기 시작.
    • 그러나 렌더 서버의 작업은 반드시 Presentation time과 정렬될 필요는 없음.
    • 렌더 서버가 작업을 일찍 완료하면 다음 커밋 마감 시간까지 대기함.
  • 메인 스레드 작업
    • 플랫폼에 따라 메인 스레드 작업이 Begin time 또는 Commit deadline과 정렬되지 않을 수 있음.
    • 입력 지연을 줄이기 위해, 시스템은 렌더링 모드, 입력 장치, 플랫폼에 따라 이벤트를 들어오는 즉시 처리할 수 있음.
    • 반대로, 앱이 다음 커밋 마감 시간 전까지 UI 업데이트를 최대한 오래 계산할 수 있도록, Begin time에 이벤트를 전달하기 위해 이벤트를 대기시킬 수도 있음.
  • 병렬 처리
    • 앱 자체는 한 번에 하나의 프레임만 처리하지만, 렌더링 루프의 개별 단계는 병렬로 실행.
    • 예를 들어, 디스플레이가 특정 프레임을 표시하고 있는 동안, 렌더 서버는 다음 프레임을 렌더링하며, 앱은 그 이후 프레임 작업을 시작할 수 있음.

 

타임라인 일러스트레이션: 더블 버퍼 모드 (Render Pipeline — Double-Buffer Mode)

 
 
마감 시간은 모두 개별 프레임에 상대적임.

  • 한 프레임의 커밋 마감 시간(commit deadline)은 다음 프레임의 시작 시간(begin time)에 해당

그럼에도 불구하고 시작 시간(begin time)과 커밋 마감 시간(commit deadline)이 종종 vsync와 정렬되지만, 항상 그런 것은 아님.

  • 예를 들어, 가변 재생률(variable refresh rate)을 지원하는 디스플레이에서는 앱 실행 중에 재생률이 변경될 수 있어 이러한 시간이 디스플레이 업데이트와 일치하지 않을 수 있음.
  • 또한, 디바이스가 저지연 모드(low-latency mode)로 전환될 수도 있는데, 저지연 모드에서는 렌더 서버가 한 프레임을 처리하는 데 걸리는 시간이 vsync 간격보다 짧아지므로, 앱은 사용자 입력과 화면 업데이트 간의 지연을 최소화하기 위해 이벤트를 최대한 빠르게 처리하려고 시도함.
  • 저지연 모드에서 발생하는 부분은 주로 주로 드로잉 앱과 같은 경우에 발생.

 

프레임 수명(Frame Lifetime)과 히치 지속 시간(Hitch Duration) 이해하기

대부분의 Apple 디바이스는 더블 버퍼(double-buffer) 모드로 동작

  • 더블 버퍼 모드에서는 렌더 서버와 디스플레이 드라이버가 두 개의 버퍼를 공유.
  • 디스플레이 드라이버는 한 버퍼에서 픽셀 값을 읽어 화면을 업데이트하고, 렌더 서버는 다른 버퍼에서 다음 프레임을 렌더링.
  • vsync가 발생하면 렌더 서버와 디스플레이 드라이버는 버퍼를 교체하며, 디스플레이 드라이버는 새로 렌더링된 버퍼를 사용해 화면을 업데이트하고, 렌더 서버는 이전에 디스플레이 드라이버가 사용했던 버퍼에 새 프레임을 렌더링함.

디스플레이 드라이버가 화면의 모든 픽셀을 업데이트하는 데는 최대 한 vsync 간격이 걸릴 수 있음

  • 따라서 화면 업데이트를 트리거한 시점부터 마지막 픽셀이 업데이트되는 시점까지는 세 개의 vsync 간격이 필요.
  • 이 중 하나는 메인 스레드에서, 하나는 렌더 서버에서, 그리고 하나는 디스플레이 드라이버에서 처리.
  • 그러나 앱의 메인 스레드와 렌더 서버는 각각 단일 vsync 간격 내에서 작업을 완료해야 함.
  • 하나의 vsync 간격은 60 Hz 재생률에서는 16.7 ms(1초/60), 120 Hz 재생률에서는 8.3 ms(1초/120)만큼 짧을 수 있음.
  • 더블 버퍼 모드에서 화면 업데이트 작업을 시작한 시점과 화면이 실제로 업데이트되는 시점 간의 지연은 60 Hz에서는 50 ms, 120 Hz에서는 25 ms이지만, 개별 프레임은 이 시간의 1/3만 화면에 표시됨.

프레임 수명 정의

  • 예상 프레임 수명(Expected Frame Lifetime)
    • 시작 시간(begin time)과 표시 시간(presentation time) 간의 시간으로, 앱과 렌더 서버가 해당 프레임 작업을 수행하는 데 필요한 시간
  • 실제 프레임 수명(Actual Frame Lifetime):
    • 시작 시간부터 해당 프레임이 화면에 표시되기 위해 버퍼가 전환되는 vsync까지의 시간

이 두 프레임 수명에는 디스플레이 드라이버가 화면을 업데이트하는 데 걸리는 시간은 포함되지 않음. 따라서, 앱이 화면 업데이트 작업을 시작한 시점부터 마지막 픽셀이 화면에 표시되는 데 필요한 예상 지연 시간은 세 개의 vsync 간격이지만, 예상 프레임 수명은 처음 두 개의 vsync 간격만 측정함.
 
히치 발생과 히치 지속 시간

  • 시스템이 정상적으로 작동하면 예상 프레임 수명과 실제 프레임 수명은 동일.
  • 하지만 실제 프레임 수명이 예상 프레임 수명보다 길어질 경우, 새 프레임이 제시간에 화면에 나타나지 못하고 이전 프레임이 화면에 더 오래 표시.
  • 이로 인하여 히치(hitch)가 발생

히치 지속 시간(Hitch Duration)

  • 실제 프레임 수명과 예상 프레임 수명 간의 차이로, 프레임이 지연된 시간을 나타냄.
커밋 히치

 
트리플 버퍼 모드(Triple-Buffer Mode) 이해하기

  • 트리플 버퍼 모드는 더블 버퍼 모드와 비슷하게 동작하지만, 몇 가지 차이점이 있음.
  • 트리플 버퍼 모드에서는 렌더 서버가 현재 프레임과 다음 두 프레임을 동시에 렌더링할 수 있음.
  • 즉, 렌더 서버는 두 개의 별도의 버퍼를 사용하여 두 프레임을 병렬로 처리하며, 디스플레이 드라이버는 화면에 세 번째 프레임을 표시

트리플 버퍼 모드의 작동 방식

  • 더블 버퍼 모드
    •  렌더 서버는 하나의 버퍼에서 새 프레임을 렌더링하고, 디스플레이 드라이버는 다른 버퍼에서 화면을 업데이트.
  • 트리플 버퍼 모드
    • 렌더 서버는 두 개의 버퍼를 사용하여 병렬로 두 프레임을 렌더링할 수 있음.
    • 디스플레이 드라이버는 세 번째 버퍼에서 이미 렌더링된 프레임을 화면에 표시함.
  • 트리플 버퍼 방식은 더블 버퍼 모드보다 유연하게 작동하며, 프레임 렌더링과 화면 업데이트가 더욱 원활하게 이루어질 수 있음.
  • 이로 인해 렌더링이 지연되거나 화면이 끊어지는 현상(hitch)이 발생할 확률이 줄어들 수 있음

 
 

트리플 버퍼 모드

 
트리플 버퍼 모드에서의 렌더링 동작

  • 트리플 버퍼 모드에서는 앱이 여전히 단일 vsync 간격 내에서 모든 작업을 완료해야 하지만, 렌더 서버는 두 개의 vsync 간격을 사용하여 작업을 마칠 수 있음.
  • 즉, 렌더 서버는 두 개의 프레임을 병렬로 처리하면서 각 프레임에 대해 두 개의 버퍼를 사용할 수 있음

예상 프레임 생애주기(Expected Frame Lifetime)

  • 트리플 버퍼 모드에서의 예상 프레임 생애주기
    • 각 프레임의 시작 시간(begin time)과 프레임이 화면에 표시될 예정인 시간(presentation time) 사이의 간격은 세 개의 vsync 간격
    • 120Hz에서는 25ms, 60Hz에서는 50ms에 해당
  • 화면 업데이트까지의 총 지연
    • 앱이 프레임 작업을 시작한 시점부터 마지막 픽셀이 화면에 업데이트되는 데까지의 지연은 네 개의 vsync 간격
    • 120Hz에서는 33.3ms, 60Hz에서는 66.7ms에 해당

트리플 버퍼 모드의 장점

  • 예상보다 늦은 프레임은 없음
    • 트리플 버퍼 모드에서는 지연 시간이 더 길지만, 각 프레임이 예상보다 늦게 표시되는 일은 없음.
    • 디스플레이는 예상되는 새 프레임을 각 vsync마다 화면에 업데이트하며, 화면에서 부드러운 동작이 지속됨
  • 따라서 트리플 버퍼 모드는 더블 버퍼 모드보다 지연 시간이 길지만, 화면 업데이트가 일정하게 이루어져 끊김 없이 부드러운 애니메이션을 제공할 수 있음.

 

커밋 히치와 렌더 히치 이해하기

 
화면을 업데이트하는 작업은 두 부분으로 나눠짐.

  • 앱 프로세스가 수행하는 작업과 렌더 서버가 수행하는 작업.
  • 각 작업에는 자체적인 기한이 있기 때문에, 다양한 유형의 히치를 구분할 수 있음.

커밋 히치 (Commit Hitch)

  • 발생 원인: 앱의 프로세스가 커밋 기한을 맞추지 못할 때 발생함.
  • 이로 인해 렌더 서버가 한 개의 vsync 간격을 놓쳐 다음 vsync에서 시작하게 되어 히치가 발생
  • 특징: 앱 프로세스에서 Core Animation 커밋이 기한 내에 완료되지 않으면 커밋 히치가 발생하며, 렌더 서버는 새로운 프레임을 시작할 수 없고, 결과적으로 화면에 새로운 프레임을 표시하는 데 지연이 생김.

렌더 히치 (Render Hitch)

  • 발생 원인: 앱의 프로세스 작업이 기한 내에 완료되었지만, 렌더 서버의 작업이 너무 복잡하여 시간이 부족해지면 발생.
  • 이로 인해 렌더 서버는 렌더링을 완료하지 못하고, 기한을 놓쳐 히치가 발생.
  • 특징: 복잡한 렌더 요청이 있을 경우, 렌더 서버는 할당된 vsync 간격 내에 렌더링 작업을 완료하지 못할 수 있으며, 앱 프로세스가 정상적으로 작업을 마친 상황에서 렌더 서버가 문제를 일으키는 경우

트리플 버퍼링을 통한 렌더 히치 복구

  • 시스템은 렌더 히치에서 회복하기 위해 트리플 버퍼 모드로 전환할 수 있음.
  • 예를 들어, 렌더 서버가 하나의 vsync 간격 내에 프레임 렌더링을 완료하지 못할 경우, 시스템은 세 번째 버퍼를 사용하여 트리플 버퍼 모드로 전환하고, 다음 프레임 렌더링을 시작하면서 이전 프레임의 작업이 끝나도록 함.
  • 이 방식은 그린 프레임을 떨어뜨리지 않고 화면에 표시되지만, 여전히 예상보다 1vsync 늦게 나타날 수 있어 화면에서의 동작에 중단이 발생할 수 있음.
  • 이 경우도 히치로 간주


지금까지 개념에 대한 요약

  • 커밋 히치: 앱 프로세스의 작업이 기한 내에 완료되지 않아서 발생.
  • 렌더 히치: 렌더 서버의 작업이 기한 내에 완료되지 않아서 발생.
  • 트리플 버퍼링: 렌더 히치에서 회복을 위한 방법으로 사용될 수 있으며, 화면 업데이트가 예상보다 늦어질 수 있음.

 

트리플 버퍼 모드의 회복

다음 일러스트에서는 렌더 서버가 트리플 버퍼 모드에서 작동하고 있기 때문에 프레임의 실제 프레임 생애 주기는 세 개의 vsync에 해당.

  • 렌더 서버가 다음 프레임을 준비하면서 현재 프레임을 렌더링하고 있기 때문에 히치(hitch)가 발생하지 않음.

트리플 버퍼 모드에서 렌더 서버는 두 개의 프레임을 동시에 처리할 수 있어 현재 프레임을 렌더링하면서 동시에 다음 프레임을 준비할 수 있음.

  • 렌더 서버는 세 개의 vsync 간격을 통해 작업을 완료할 수 있기 때문에 각 vsync에서 화면을 업데이트할 수 있으며, 지연이나 중단 없이 원활하게 화면에 표시
  • 프레임 생애 주기가 세 개의 vsync 간격을 지속하더라도 이는 예상되는 동작으로, 히치가 발생하지 않음.
  • 화면은 각 vsync마다 계속해서 업데이트되며 시스템은 렌더링 작업을 성공적으로 처리
  • 따라서 이 시나리오는 렌더 서버가 작업을 제시간에 완료하지 못하고 프레임이 지연되는 경우 발생하는 렌더 히치와는 다름.
  • 이 시나리오에서는 화면이 끊기지 않고 부드럽게 렌더링되므로 히치가 발생하지 않음.

 

렌더 히치 - 트리플 버퍼 모드 회복

 
 
 
 
시스템은 트리플 버퍼 모드를 정기적으로 사용할 수도 있음.

  • 예를 들어, GPU를 저전력 모드로 실행하여 에너지를 절약하거나, 특정 사용 사례에서 CPU와 GPU가 하나의 vsync 간격 내에 작업을 완료하기 위해 더 많은 시간이 필요할 때 트리플 버퍼 모드를 사용할 수 있음

렌더링의 준비 작업은 CPU에서 수행되고, 실제 렌더링은 GPU에서 이루어짐

  • GPU는 작업을 병렬로 처리할 수 있지만, CPU는 병렬 처리가 불가능 함.
  • 따라서 트리플 버퍼 모드에서도 렌더 서버의 CPU 작업은 여전히 하나의 vsync 간격 내에 완료되어야 함.
  • 그렇지 않으면 다음 프레임의 CPU 작업을 늦게 시작하게 되어, GPU가 발표 시간 전에 작업을 끝낼 충분한 시간을 확보하지 못함.

렌더링이 앱 자체가 아니라 렌더 서버 내에서 발생한다고 해도, 렌더 히치는 대개 앱에 의해 발생

  • 예를 들어, 예정된 UI 업데이트가 너무 복잡하여 시간 내에 렌더링을 완료할 수 없는 경우
  • 따라서 커밋 히치와 렌더 히치를 모두 주의 깊게 살펴봐야 함.
  • 히치의 유형은 문제의 근본 원인과, 향후 히치를 피하기 위해 최적화해야 할 앱의 부분을 알려주는 힌트를 제공

 


(참고)
https://developer.apple.com/documentation/xcode/understanding-hitches-in-your-app?changes=__2#Understand-frame-lifetime-and-hitch-duration