- What is Launch
- User Interaction Interruption
- 런치 타임이 1ms줄면, 전체적으로 약 162일의 기다림이 줄어든다.
- 런치가 중요한 이유
- 앱에 대한 첫 경험이 일어나는 시간이고, 이것이 즐거워야 하기 때문이다.
- 전반적인 성능에 대한 기대치가 되기 때문이다
- 시스템 성능과 배터리에 영향이 많이 가기 때문이다.
- 런치 타입
- 콜드 런치: 리부트 후에 일어나는 런치, 메모리에 앱이 없는 상태
- 웜 런치: 최근에 종료된 상태에서 다시 실행하는 것으로 앱의 일부가 메모리에 남아 있어서 콜드보다 빠르다.
- 리줌 런치: 서스펜드 상태에서 다시 실행되는 것으로 메모리에 완전히 존재하고 있으며, 프로세스도 있다.
- 앱 런치 단계
- 앱 터치
- 시스템 사이드 초기화(약 100ms)
- 시스템 인터페이스
- dyld :2017년에 3.0이 나와서 웜런치 성능이 좋아졌다.
- 사용하지 않는 의존성을 제거해라
- 실행시 동적 라이브러리 로딩(dlopen 등)을 줄이라
- 모든 의존성을 하드링크하라.
- 런타임 초기화
- 로우 레벨 시스템 컴포넌트들을 초기화하는 단계
- 고정 비용으로 일어나는 시스템 영역의 작업
- 언어 런타임 초기화, 정적 생성자 호출 등의 작업 -> 정적 생성자 호출을 줄여야 된다.
- API를 고칠수 있다면, 정적 생성자는 초기화 API로 수정하라.
- 그래도 써야된다면 [Class load] 안에서라도 빼내라
- [Class Initializie]를 써서 lazy하게 초기화가 일어나도록 해라
- 사용자 영역 및 뷰계층 초기화(약 300ms)
- UIKit 초기화
- UIApplication과 UIApplicationDelegate를 초기화하는 과정
- 이벤트 처리가 시작되고, 시스템과 통합되는 과정
- UIApplication과 UIApplicationDelegate에서 하는 작업은 최소화하라
- Application 초기화
- AppDelegate의 라이프 사이클 메소드들을 호출하는 단계 -> 13부터는 Scene도 고려해야 한다.
- 당장 할 필요 없는 작업은 최대한 미루자.
- 가능하면 Scene사이에 리소스를 공유하자.
- 첫 프레임 렌더링
- loadView -> viewDidLoad -> layoutSubviews
- 뷰 계층을 최소화하고, 뷰를 최대한 동적으로 로딩하자
- 오토레이아웃도 최적화해야 한다.
- 비동기 로딩된 데이터 초기화
- 첫 프레임 이후 앱마다 다르게 가져가는 부분
- 비동기적으로 로딩된 데이터를 보여준다.
- 이 단계에서는 상호작용이 가능해야 한다. -> 이 때의 성능을 signpost API로 측정해서 최적화하라
- How to properly measure Launch Time?
- 대표성을 확보해야 한다. -> 기준이 필요하다.
- 일관적인 결과를 위해 불확실한 부분은 제거해야 한다. -> 네트워크, 백그라운드
- 테스트 환경은 깔끔하고 일관적이게 유지해야 된다.
- 리부팅한 뒤에 실행할 것
- 에어플레인 모드를 키거나 네트워크를 모킹하라
- 아이클라우드 사용도 하지 마라
- 릴리즈 빌드를 사용하라
- 앱은 스케일링 될 수 있어야 한다
- 작은 데이터를 쓰던 큰 데이터를 쓰던 둘 다 런치는 빨라야 된다.
- 그래서 첫 프레임에서는 꼭 필요한 정보만 로딩되어야 한다.
- 테스트 기기는 지원하는 OS버전이 돌아가는 가장 오래된 기기를 포함할 것
- 앱 런치를 테스트 하라 -> XCTest로 테스트 가능
@available(iOS 13.0)
measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
XCTApplication(bundleIdentifier:
"com.example.apple-samplecode.StartSearcher").launch
}
-
최적화 테크닉
- Minimizie work
- 첫 프레임에 필요없는 동작은 최대한 미루라
- 메인스레드에서 필요 없는 동작 하지 말고 백그라운드로 미루라
- 메모리 사용량을 줄이라 -> 메모리 할당과 조작은 시간이 많이 걸린다.
- Prioritize work
- 작업마다 적절한 QoS를 식별하라
- 스케쥴러를 활용하라 -> iOS 13
- Qos를 유지하라(막 바꾸지 마라)
- Optimize work
- 업무를 단순화하거나 제한하라
- 알고리즘과 자료 구조를 최적화하라
- 리소스와 연산 결과를 캐싱하라
-
Use Instrument to profile launch
- Analyze로 빌드하라
- 이번에 app intialization 템플릿을 새로 만들었다.
- 각 항목 설명
- Initializing 페이즈(보라색): main()이 호출되기 이전 단계
- 시스템 영역 초기화는 dyld 3에 의해 굉장히 빠르게 일어난다.
- 다만 프로파일링 과정에서 영역이 실제보다 과장될 수 있음에 유의하자
- 때문에 실제 시간 측정은 XCTest를 활용하면 가능하다 -> 하지만 iOS13부터 지원
- 정적 초기화 영역을 체크하자
- stack trace에서 파란색이 우리 코드영역이다.
- Launching 페이즈(초록색): main()의 앞부분으로 첫번째 프레임을 그리기 직전까지의 단계
- didFinishLaunching
- draw first Frame
- 다른 스레드보다 메인 스레드를 중요하게 봐야한다.
- 스레드 영역 설명
- blocked(회색): 아무일도하지않고 있는 상태
- runnable(빨간색): 일이 있지만, CPU 리소스가 모자라서 시작은 못한 상태
- preempted(주황색): 일을 하다가 다른 우선순위 높은 스레드에 의해 밀려난 상태
- running(파란색): 실제 CPU 코어에 의해 작업이 실행되고 있는 상태
-
System Improvement to reduce app launch time
-
Track your launch over time
- 개발할 때에 성능 개선을 우선순위에 놓자
- 정기적으로 측정하자.
- Xcode Organizer로 사용자의 런치 타임을 모니터링하자
- MetricKit으로 커스텀 정보를 수집할 수 있다.