- Measuring performance
- performance workflow
- 문제 재현
- 툴을 통한 프로파일링
- 가정을 하고 수정
- 수정 결과가 제대로 되었다면 실제 반영
- Fast App Launch
- 런치 타임: 메인에서 DidFinishLaunching 직후까지
- 앱 런치 단계
- 링킹 & 로딩
- 필요 없는 라이브러리는 빼자
- 꼭 필요한 라이브러리를 옵셔널로 만들지 말자
- 정적 생성자를 쓰지 말자
- 전역 C++ 오브젝트
- 로드 타임에 돌아가는 코드 -> 런타임으로 변경해서 필요할 때 로드 하도록 하자.
- UIKit 초기화
- 폰트, 상태바, 유저디폴트, 메인Nib 초기화 등
- UserDefault같은 곳에 너무 많은 데이터를 넣지 말자
- property list 파일로 저장되고, 첫 로딩때 한꺼번에 불러온다
- 애플리케이션 콜백
- willFinishLaunching -> 앱 상태 복원 -> DidFinishLaunching
- 첫번째 Core Animation 트랜지션
- CA:: Transaction:: commit이 호출되면 화면이 움직이게 된다.
- 대부분은 runLoop 끝에서 자동으로 호출된다.
- 호출 시점은 [UIApplication _reportAppLaunchFinished] 안에서. 앱이 런치된 이후에 호출되는 메소드다.
- commit의 실행 단계
- 준비: 이미지 압축 해제
- 레이아웃: (layoutSubviews)
- 그리기: drawRect
- 400ms(아이폰), 500ms(아이패드)가 권장 한계선
- 릴리즈 빌드에서는 감시자가 있어서 너무 느리게 초기화 되면 프로세스를 죽인다.
- 감시자는 첫번째 CATransition(첫번째 레이아웃이 정해지고 그려지는 단계)까지를 측정한다.
- 이 당시에는 [UIApplication _reportAppLaunchFinished]안에 있었음 -> 지금도 그런지는 모르겠다.
- 감시자가 보는 기준과 유저의 기준은 다를 수도 있다. -> 카메라 앱 같은 건 카메라 셔터가 초기화되는 시간까지 포함해야 되니까
- 시간 재는 API로 찍어봐도 되고(CFAbsoluteTimeGetCurrent), Time Profiler로 측정해도 된다. -> 현재는 main에 직접 접근할 수 없으니 Time profiler를 쓰자.
- Performance Strategies
- Don’t do it : 불필요한 일은 하지 말라
- Don’t do in again: 다시 만들지 말고 재사용하라
- 만드는 비용이 많이 드는 것들: TableView Cell, Formatter, Calendar, Regular Expression, SQLite statement 등
- NSLog도 내부적으로 Calendar를 쓰기 때문에 너무 과하게 쓰면 안된다.
- Do it faster: 적절한 자료구조와 알고리즘을 쓰자
- Property List는 작은 데이터 용: 한꺼번에 로딩해야 함
- Preference(UserDefault), Serialization via NSCoding 등은 내부적으로 Property List를 사용
- Core Data나 SQLite는 incremental loading이 가능하기 때문에 큰 데이터에 적합하다.
- 데이터베이스 쿼리 최적화도 필요하다.
- Do it beforehand: 결과를 미리 계산해놓자.
- 다만 선 계산과 캐싱은 메모리 영향이 크므로 적절한 타협이 필요하다.
- Do it afterwards: 비동기적으로 로딩하자
- 동기적인 로딩이 사용자 경험에는 더 좋지만, 그게 어렵다면 GCD등으로 비동기 로딩을 수행하자.
- Do it at scale: 규모를 고려하자
- 큰 데이터를 받을 가능성이 있다면 큰 데이터를 처리할 수 있도록 해야 한다.
- Speedy event Handling
- 사용자 이벤트는 메인 스레드의 런루프에서 실행된다.
- 메인 스레드는 이벤트 처리를 위해서 최대한 프리하게 냅둬야 한다.
- Time Profiler를 통해서 메인 스레드의 작업량이 많은 부분을 찾아서 수정해줘야 한다.
- 메인 스레드에서 하지 않아야 할 일은 다 빼자
- 간접적인 concurrency: View&Layer 애니메이션, Layer 합성, PNG 디코딩
- 명시적인 concurrentcy: GCD, NSOperationQueue, NSThread
- GCD가 thread를 너무 많이 만들 수도 있다. -> concurrent한 큐에 넣은 작업이 스레드에서 너무 오래 실행 되는 경우에
- 해결법은 Serial, DispatchSource, NSOperationQueue, NSURLConnection의 비동기 메소 등이 있다.
- 스레드 안정성 주의
- UIKit의 요소들은 메인스레드에서만 접근할 수 있다.
- 예외: UIGraphics, UIBezierPath, UIImage
- CG, CA, Foundation 객체들은 대부분 스레드 제한이 없다.
- 하지만 배타 제어는 안되어 있으니, 여러 스레드에서 접근은 안된다.
- 스레드 안정성: 여러 스레드에서 접근해도 된다.
- 락을 사용하기 때문에 경쟁 상태가 생기며, 이는 System Trace로 감지할 수 있다.
- DispatchQueue에 Background 우선순위 추가
- 극단적으로 낮은 우선순위(I/O 스로틀, 몇초동안 안 돌아갈 수도 있다.)
- 이 상황에서 메인 스레드가 가져야 할 락을 가지고 있다면?
- 진짜 백그라운드에서 돌아도 되는 그런 작업에만 쓸 것
- 메인 스레드를 블록하지 말라
- Time Profiler로 프로파일링: 그냥 해도 되지만(CPU strategy View -> 메인 스레드 하이라이팅), Record Waiting Thread 옵션을 쓰면 더 좋다. -> 근데 최신 Instrument에서는 안보이네?
- System Trace로 프로파일링: 스레드를 블록하는 이벤트 대부분은 시스템 콜이다.(read/write, send/recv, psynch_mutex_wait, mach_msg 등)
- System Trace는 모든 시스템 콜을 기록하고, 각 시스템 콜의 소요 시간을 측정한다.