WWDC/iOS

[iOS] WWDC21 테크톡 - Find and fix hitches in the commit phase

유정주 2023. 9. 10. 13:38
반응형

서론

WWDC21 테크톡 - Find and fix hitches in the commit phase에서는 Commit phase의 hitch를 찾고 고치는 내용을 다룹니다.

hitch와 commit phase가 무엇인지는 WWDC21 테크톡 - Explore UI animation hitches and the render loop를 확인해 주세요.

 

 

What is a Commit Transaction

터치 이벤트를 받으면 UI를 업데이트하게 됩니다.

시스템은 레이아웃이나 디스플레이 변경이 필요한 서브 뷰들을 기록합니다.

 

그리고 다음 Commit phase에서 시스템에 의해 draw와 layoutSubviews가 호출되면서 업데이트 됩니다.

 

Commit Transaction은 4단계로 이루어집니다.

layout -> display -> prepare -> commit 순서로 동작합니다.

 

1. Layout phase

레이아웃 단계에서는 레이아웃이 필요한 모든 뷰의 layoutSubviews()가 호출됩니다.

그럼 모든 서브뷰들의 레이아웃이 업데이트 됩니다.

 

레이아웃 단계는 다음과 같은 상황에서 발생합니다.

  • 뷰 포지션을 변경할 때 (frame, bounds, transform)
  • 뷰를 추가하거나 삭제할 때
  • setNeedsLayout을 직접 호출할 때

 

2. Display phase

디스플레이 단계에서는 컨텐츠 업데이트가 필요한 모든 뷰들의 draw()가 호출됩니다.

디스플레이 단계는 아래 상황에서 발생합니다.

  • 오버라이드한 draw(rect:)를 호출할 때
  • setNeedsDisplay()를 직접 호출할 때

 

3. Prepare phase

Prepare 단계는 이미지에 대한 작업을 처리합니다.

  • 디코드되지 않은 이미지를 디코딩합니다. 만약 이미지가 크다면 많은 시간이 걸릴 수 있습니다.
  • GPU에서 지원하지 않는 Color 포맷이 있다면 변환합니다. 

이 과정을 위해선 원본으로 포인터를 보내는 대신 이미지를 복사해야 하므로 추가 시간과 메모리가 필요합니다.

이미지 성능을 최적화하려면 WWDC18 - Image and Graphics Best Practices를 참고하세요.

(원본 WWDC 영상은 내려갔지만, 내용을 보고 싶다면 WWDC18 - Image and Graphics Best Practices를 봐주세요.)

 

 

4. Commit phase

Commit 단계에서는 뷰 레이어 트리가 상위뷰부터 하위뷰까지 재귀적으로 패키징되고, 렌더 서버에게 보냅니다.

 

 

Find hitches with Instruments

Xcode 12부터 Animation Hitches 템플릿을 사용해 Hitch를 찾고 시각적으로 분석할 수 있습니다.

 

앱을 실행해서 스크롤한 뒤 Instrument를 봅니다.

그러면 Hitches라는 트랙에서 감지된 hitch들을 보여줍니다.

 

hitch 트랙을 펼치면 더 다양한 정보를 볼 수 있습니다.

맨 위부터 순서대로

  1. hitch 지속 시간
  2. hitch 프레임과 함께 수신된 사용자 이벤트
  3. Commit 단계
  4. Render 단계
  5. GPU
  6. Frame 수명
  7. 모든 프레임의 VSYNC

입니다.

 

Frame 수명과 hitch 지속 시간을 비교하면 프레임이 준비되어야 할 예상 간격을 시각화해서 볼 수 있습니다.

 

이 시간을 허용 가능한 지연(Acceptable Latency)이라고 하고,

 

이후의 hitch 지속 시간을 hitch duration이라고 합니다.

 

Animation hitch 템플릿은 Time profiler template를 포함하고 있기 때문에 이 시점에 어떤 코드가 실행되었는지 알 수 있습니다.

hitch duration, Acceptable latency, hitch type 등을 볼 수 있는데

특히 hitch type은 프레임이 어느 단계에서 지연되었는지, 어디에서 조사를 시작해야 하는지 힌트를 얻을 수 있습니다.

 

자신의 앱 정보만 필터링하고, Main Thread를 선택해서 Call tree를 살펴보면 호출 비용을 분석할 수 있습니다.

 

위 예시에서는 CollectionViewCell의 updateTags(_:)가 가장 비싼 연산이네요.

 

이 문제를 해결하고 다시 살펴보면 

hitch 수가 대폭 줄어들었습니다.

 

 

Recommendations

Commit hitch를 줄이는 방법을 알아봅시다.

 

Keep view lightweight

뷰를 가볍게 쓰면 commit hitch를 줄일 수 있습니다.

 

CALayer를 최대한 활용하면 좋습니다.

draw는 CPU를 사용하지만 CALayer는 GPU를 가속해서 사용합니다.

CPU에서 연산을 하면 메인 스레드 부하가 증가하여 성능에 악영향을 줍니다.

따라서 CALayer 프로퍼티로 해결 가능하면 CALayer를, 불가능하다면 draw의 성능을 측정하세요.

 

불필요하게 draw(rect:)를 오버라이드하지 마세요.

draw(rect:)를 오버라이딩하는 것만으로도 Commit transaction동안 더 많은 메모리와 시간을 사용합니다.

 

뷰를 재활용하세요.

뷰 계층 구조를 변경하는 것은 비싼 연산입니다.

무조건 뷰를 추가하거나 제거하지 말고 가능하다면 뷰를 재활용하세요.

 

hidden을 사용하세요.

hidden은 뷰를 제거하는 것보다 훨씬 저렴한 연산입니다.

 

 

Reduce expensive or redundant layout

비싸거나 중복되는 레이아웃을 줄이세요.

 

레이아웃 업데이트는 layoutIfNeeded보다 setNeedsLayout을 사용하세요.

layoutIfNeeded는 Commit transaction 시간을 늘리고 hitch를 발생시킵니다.

(layoutIfNeeded는 런루프 상관없이 바로 레이아웃 업데이트를, setNeedsLayout은 다음 런루프에 레이아웃 업데이트를 합니다.)

 

최소한의 constraint만 사용하세요.

제약 계산이 복잡해지면 성능에 영향을 줍니다.

 

자기 자신의 뷰 레이아웃만 진행하세요.

부모 뷰의 레이아웃을 invalidate하면 자식 뷰까지 재귀적으로 레이아웃 됩니다.

재귀적인 레이아웃은 비용이 비쌉니다.

 

(WWDC 두 개를 추천하는데 두 개 다 사라졌네요..

Image and Graphics Best Practices는 WWDC18 - Image and Graphics Best Practices에 정리했습니다.)

 

 

감사합니다.

 

참고

https://developer.apple.com/videos/play/tech-talks/10856

 


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

 

반응형