본문 바로가기
WWDC

WWDC 19 - Optimizing App Launch

by vapor3965 2021. 6. 22.

목차


    • 세션보면서 정리한 내용입니다. 해석이 잘못된 경우가 있을수있으니 발견하시면 댓글로 남겨주시면 감사하겠습니다🙏🏻

     

     

    https://developer.apple.com/videos/play/wwdc2019/423/ 

     

    Optimizing App Launch - WWDC 2019 - Videos - Apple Developer

    Slow app launches are frustrating. Learn about the new app launch instrument and discover how to make your app launch fast. Gain insights...

    developer.apple.com

     

     

     


     

    관련내용

    • 새로운 앱 launch instrument 와, 어떻게 앱launch를 빠르게할지 배워보자.
    • 그리고 앱이launch하는동안 무슨일이일어나는지 개략적으로보자.
    • 팁을배워보자 .

    🤔swift는 struct-static, class-class는괜찮나?

    👍요약

    • 앱상태는 cold, warm, resume이있다.
    • 이상적인 시간은 0.4초다
    • 런칭과정은 아래와같이 6단계로나뉜다.

    • 1.systeminterface는 dylib을 로드한다.
    • 2.runtimeinit은 swift,objective-c Runtime을 초기화한다.
      • +static 이니셜라이저는 main함수가호출되기전에 Runtime init때 호출된다.
      • 그러므로, static 이니셜라이저를 추천하지않는다.
      1. UIkit init 은 UIAppliation, UIApplicationDelegate 초기화한다.
    • 4.ApplicationInit은 ApplicationDelegate callback 호출및 뷰컨트롤러를 초기화한다.
    • 5.initial framerender는 view를만들고,layout을계산하고draw한다.
    • Profile - App Launch 를 통해, 어떤것이 오래걸리는지를 통해 고민하고, 해결할수있게해준다.
    • XCTest의 API를 통하여, warm 5번 런치를 자동적으로하게해준다.

     

     


     

     

    launch에는굉장히많은 일을한다.

    만약 느리다면? 너의코드가전반적인 성능이좋지않다는 뜻이다.

    Launch Type


    Cold
    리붓햇거나 앱이꽤오랬동안 실행되지않았을때.

     

    이상적인 앱 시작시간은 0.4초다

     

    런칭과정

    시스템 이니셜라이저부터 앱이니셜라이저까지.

    생각해보면, 0.4초까지 모든화면을 다 정확하게보여줄필요는없다.

    • 아래와같이 세부적인 맵 로딩은 0.4초이후에 해도괜찮다.
    • 즉 필수적인레이아웃만 보여주는것으로 0.4초면좋다.

    dyld

     

    1.첫번째 system interface

    • 부분은 dynamic linker 가 shard libraries, frameworks를 load한다.
    • 2017에, dyld3 를 소개했고, 굉장한 최적화를 추가했다.
    • 그리고! iOS13부터는, ㅇ새로운 caching runtime dependencies 를 추가했다. 굉장히빨라지도록하기위해

    이점을 잘 얻기위해서는 아래와같이 추천한다.

    • 🤔hard linking?

    1-2. 두번쨰 system interface 는
    libSystem Init

    • 이부분은 거의고정된 비용이여서 개발자는 여기에 포커스하지않아도된다.



    2. static runtime initialization


    ( main function이 호출되기전인가봐. )

    • swift, objective-C runtime을 이니셜라이즈한다.
    • static initialization을 추천하지않는다.

    static initialization은 다음과같이 추천한다.


    3. UIKit Initialization

    • UIApplication, UIApplicationDelegate를 초기화한다.
    • 대부분 system-side work이여서 비용이 고정되어있지만, 만약 UIApplication을 subclass하거나, UIApplicationDelegate initialization안에서 많은일을한다면 영향을 주게된다.

    그러므로 아래를 추천한다.


    4 Application initialization

    이단계에서 우리,개발자가 영향을 가장많이줄수있는부분이다.

    만약, UIScene을 사용하지않거나, iOS12이하라면

    • 아래와같이, UIApplicationDelegate callback를 호출한다.

    👍UIScene을 사용한다면,

    • 아래와같이호출하고, ViewController를 create하게된다.

    UIScene을사용하건안하건 공통적으로 추천하는바는,

    • 관련없는일을 최대한 미루어라. - 백그라운드큐에 넣거나, 나중에해라.

    UIScene을사용한다면,

    • scene간에 resource를 공유하라.!!!
      • 불필요한많은시간을 줄이도록해준다!

    만약 UIScene에 관심있따면,아래 2개세션을 보아라.


    5. inital Frame Render

    • view를 만들고, layout을 실행하고, draw한다.

    추천방법으로는.

    • 뷰계층구조에 뷰를 줄이는 것.
    • 또는 당장런칭에필요하지않은 뷰들은 lazy하게 로딩하도록한다.
    • 또한, 오토레이아웃을 잘 사용한다.
      • 이는 HighPerformance Auto Layout 세션에나와있다.
      • 즉, 최대한공통적인건 냅두고, 바뀌는값만으로 바꾸도록하는것.
      •  
      •  

    6.Extened

    • 첫번째프레임나온것부터, 모든프레임이다 나오기전까지의 구간을의미한다.
    • 비동기적인 데이터를 로드하여 보여주는 구간이다.
    • 모든앱이 이구간이있는건아니지만, 만약있다면, 꼭 interactive 하고 responsive하도록해라.

    추천방법은, 아래를봐라….


     

     


    How to properly measure ?

     

    다양한 디바이스에따라 환경조건이다르기때문에,

    우선, 네트워킹이나, 백그라운드작업을 제외한 작업들을 예측하도록해본다.

    그러므로 우선! 아래와같은 환경을 준비하여 테스트하도록한다.
    네트워킹은 굉장히변수를만들어낼수있기때문
    iCloud는 백그라운드작업에서하기때문,

    그리고, 다양한 크기의 Mock 데이터를 준비하도록한다.

    그리고 타겟중에 가장오래된기기로 준비한다.

    XCTest는 단 몇줄로, 앱을 반복적으로 런칭하고, 통계를제공해준다.

     

     

     

     

    Use Instruments to profile your launch

     

    작업을최소화하고, 우선순위를정하고, 작업을최적화한다.

    메인쓰레드말고 백그라운드쓰레드로 옮겨라
    메모리할당조작은 시간을잡아먹는다.

     

     

     


    👍Profile - App Launch

     

    profile을눌러준다.

    • xcode는 앱을 releasemode로 recompile해준다.
    • 그러므로, 컴파일타임최적화 이점을얻는다.

    누르고나면 build하고, template를선택하는 공간이뜬다.

    그리고, iOS13, Xcode11부터 AppLaunchTemplate를 추가했다.


    record를 누르고,


    그러면, 알아서 통계를내기위해 분석한다.


    다된다면 아래와같이나타나는데,


    purple은 main function이 호출되기전인 단계이고,


    초록색은 main function이 호출되고, 앱이 런칭을마치고, 첫번째프레임을 그리는단계다.


    그리고 확대해보면, 각 단계에 사용된 쓰레드정보들이나타난다.


    이렇게 + 눌러서 원하는 쓰레드,들만 아래에추가하여 볼수도있다.



    👍각 색깔별상태


    gray는 블락된것으로, 아무런작업을하지않았고,

    red는 runnable한상태인데, 작업이스케쥴되어있고, CPU자원을 lacking하고있다. 일을해야하는데못하고있는,

    orange는 preempt한상태로 ( 선취? ) 일을하고있는상태이고, 더높은우선순위의 작업을 실행하기위해 가로채당한상태?

    blue는 실제로 running하는 상태이며, CPU core에서 일하고있는상태다.

     

     

    그럼 각 단계를 더블클릭하면 아래에 그단계에해당하는 작업들이나타난다.
    각걸린시간을볼수있따.

     

     

    여기서중요한점은, 측정값은 6ms로나왔는데, 그래프에서는 149ms로 나온다.
    이는 다양한정보를 제공하기위한 profiling 메커니즘자체의 오버헤드까지 발생한 시간으로, 분명히 구별해야한다.

    • 첫단계만그런듯, 다음단계는 그리큰차이가나지않음.


    Runtime Initialization


    375ms는 꽤 상당히길다.

    👍이렇게 강조된 곳들은 소스안에 선언된 코드들을 가리켜준다.


    그리고이놈을클릭하면,
    Logger setUpLogger - SLSuperFasatLogger 가 나타는데,

    한번 소스에서 찾아보면,
    해당 Logger는 외부프레임워크이고,
    logging 하기위한 프레임워크다.

     

    그런데, 이것이 사용되는 지점은 테이블뷰컨트롤러가 셀을터치했을때만 발생하는 부분이다.
    즉, 이는 런칭타임때 필요없다는것이 분명하다!

     

    근데왜 호출됐을까?

     

    한번 setupLogger를 찾아보자.


    👍+ 는 static initializer 로써, main function이 호출되기도전에, 이른 launch타입에 이미 된다.

    • swift,objective c runtime만드는과정인듯

    그러므로, 알수있는 사실은! 우리가사용하는 프레임워크의 내부코드가 미치는 영향을 이해하는것이 실로 중요하다라고 할수있따.

    그러므로, 외부프레임워크,라이브러리들은 정말편안하고, 좋겠지만, 많은비용을초례할수있다.

    물론, 적당한비용이라면감수하겟지만, 300ms는 너무오래걸린다.

     

     

    그러므로,
    가벼우면서 효과적인 logging 메커니즘인 os.log를 import하고,


    바꾸자.

    그리고 가장중요한점이, 바꾸는것뿐만아니라,
    linkage에서도 제거해줘야한다.

     

     

     

    UIKit initialization은 28ms밖에안걸렸고, 거의고정된비용이다.

    • UIApplication을 subclass하지않고, UIApplicationDelegate안에서 작업을안한다면 무시할만한 속도다.

     

     

     

    그다음 application initailization을 보면, 너무많이걸린다.

    요 4개들이많이걸린다.

    그리고 메인쓰레드가 블락되었다.
    즉, launch를 지연시켰다라고볼수있따.

    priority도볼수있는데,
    47이다.
    47은 interactive QoS

    그리고, 하고있는 작업드를보면,
    4로, backgroundQos 에준하는 작업이 스케쥴되어있다.


    즉, 쓰레드가 더 낮은 우선순위에의해 block되어있다.

    이것을살펴보면, StarProvider는 DB에서 데이터를 가져오는데,
    백그라운드쓰레드로, qos는 백그라운드로, 가져오게잘해놨다.



    또한, UITalbleView가 초기화되기전에 미리데이터를 가져오도록했다.
    그러기위해 세마포를사용했다.


    그러므로, 우선순위가 더 낮더래도, 세마포로 강제해놔기때무넹, 우선순위가 더 낮더라도, 먼저실행되게해놨ㄷ.

    그러므로, 동시성에서 적절하게하기위해 , 세마포를없앤다.

     

    근데 다조은데, 데이터ㅡㄹ 가져오는양이 문제다.
    데이터를 다가져오도록했다.
    테이블뷰의 셀을고려하면 많아봐야15개일텐데, 런칭타임을 늦추면서까지 다 가져오는것은 정말 안좋다라고볼수있다!

     

    그러므로, 최소한으로만 받아놓고, 나머지는 lazily하게백그라운드에서 받아오도록한다.

     

     

    Initial Frame

    미치도록 오래걸린다.

    보면, cellForRowAt이 많이,많이이이ㅣ호출된것을볼수있다.


    더보면, StartDetailViewController 이니셜라이저가 많이차지하는걸볼수있따.

    잘보면, cellForRow에서,
    캐시를 이용하고있다.
    나중에 더빠르게사용하기이해서,
    이건 뷰컨트롤러간에 전환에서 미리 저장해놓은것이다.
    이부분은 아까볼수있듯이, 그렇게 큰 비용은아니였다. ( 800ms이나걸렸지만, 전체cell에대한 viewcontroller면 ㄱ많은건아니겠다. )

    그리고 이것은 처음 런칳라때, 첫 frame에 필요한부분은 아니다.
    그러므로, didSelect할때로 미뤄주자. 바꿔주자.

    자, 다끝났다.
    다시 Profile 하자.
    빌드가끝나면, 다시 record하자.

    약 500ms로 준것을볼수있다.


    아까도말했듯이, profiling은 메커니즘이적용되어 오버헤드가있으므로,
    더 사용자가느낄수있는 시간으로 이해하기위해서는,
    XCTest API를 사용해보저ㅏ

     

     


    XCTest

     

    단2줄의코드로 쉽게 테스트할수있다.
    이는 cold 런치상태에서 오는 다양한변수들을 상쇄하고, 실행하도록해준다. (👍 물론 실제기기와연결한상태에서 )
    한번런치를 버리고 ( 이건아마콜드한상태? )
    defaultfh, 5번실행한다. - warm상태에서

     

    ( iOS 14에서부터는 deprecated 됌

    XCTOSSignpostMetrit.applicationLaunch ->  XCTApplicationLaunchMetric()  ) 


    아주긴 로그들이나타나고,
    테스트가끝나면,
    화긴할수있다.
    평균 300ms인걸호가인할수이삳.

     

     

     



    Tip And Trick

    나중에생각하지말고 항상 개발하는단계에서 매번고려하라.

    Xcode Organizer 를 통해서 한번확인해봐라.

    • 24시간주기로 organizer로보내준다.
    • 사용자가 사용한앱의 런치타임을보여준다?
    • 소프트웨어버전과, 디바이스버전에따라 봅여준다.

    MetricKit을 이용해라.

    • 앱으로 24시간마다 전달해준다는데?????
    • 이게뭐지~?

    'WWDC' 카테고리의 다른 글

    WWDC 20 - What's new in Swift  (0) 2021.06.22
    WWDC 19 - Modern Swift API Design  (0) 2021.06.22
    WWDC 19 - Combine In Practice  (0) 2021.06.19
    WWDC 19 - Introducing Combine  (0) 2021.06.19
    WWDC 20 - App essentials in SwiftUI  (0) 2021.06.19

    댓글