• Measuring performance
    • performance workflow
      • 문제 재현
      • 툴을 통한 프로파일링
      • 가정을 하고 수정
      • 수정 결과가 제대로 되었다면 실제 반영
  • Fast App Launch
    • 런치 타임: 메인에서 DidFinishLaunching 직후까지
    • 앱 런치 단계
      • 링킹 & 로딩
        • 필요 없는 라이브러리는 빼자
        • 꼭 필요한 라이브러리를 옵셔널로 만들지 말자
        • 정적 생성자를 쓰지 말자
          • 전역 C++ 오브젝트
          • 로드 타임에 돌아가는 코드 -> 런타임으로 변경해서 필요할 때 로드 하도록 하자.
      • UIKit 초기화
        • 폰트, 상태바, 유저디폴트, 메인Nib 초기화 등
          • 메인 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는 모든 시스템 콜을 기록하고, 각 시스템 콜의 소요 시간을 측정한다.