안녕하세요, 내 이름은 블라디미르 Pchelyakov, 나는 inDrive의 iOS 엔지니어입니다.이 기사에서는 앱 시작 시간이 회사 수익에 어떻게 영향을 미치는지, 어떻게 앱을 가속화했는지에 대해 이야기 할 것이며, 도구, 접근 방식을 다루고 실제 상황과 물론 결과를 다루겠습니다. 이 문서는 앱 출시 시간과 사용자의 중요한 경로를 가속화하고자하는 모든 팀을위한 계획으로 봉사 할 수 있습니다.The problems we solved most likely exist in most unoptimized projects. 컨텍스트를 위해, inDrive 앱에서 당신은 승객으로 택시를 주문할 수 있습니다, 또는 당신은 운전사로 주문을하고 여행을 할 수 있습니다. 이 기사에서, 나는 덮을 것입니다 : 스타트업 시간은 회사 수익에 어떻게 영향을 미치는가 App Launch의 종류 App Launch Time, TTI 및 Hang Time 평균값 및 비율 스타트업 시간에 영향을 미치는 것 App Acceleration 사이클 가속화하는 가장 효과적인 방법 도구, 프로세스, 상황 결과 Startup Time가 회사 수익에 영향을 미치는 방법 대부분의 사람들은 개발자가 실제 가치를 가져다주지 않는 최적화에 너무 집착하고 있다고 생각하지만 데이터에 따르면 수백 밀리 초의 속도를 높이는 것은 새로운 기능을 출시하거나 새로운 시장에 진입하는 것만큼 비즈니스에 영향을 미칠 수 있습니다. InDrive 연구 회사 내에서 우리는 승객의 중요한 경로를 750ms로 가속화하면 주문 수가 1.5 % 증가한다는 것을 보여주는 대규모 연구를 실시했습니다.이 앱이 인터페이스를 더 빨리 표시하기 시작했기 때문에 앱에 수백만 명의 주문이 있다면 하루에 수십, 심지어 수백 만 명의 추가 주문을받을 수 있습니다! 델로이트 연구 Deloitte는 매우 인상적인 숫자를 발표 : 100 ms로 모바일 웹 사이트를 가속화하면 전자 상거래에서 +8.5% 변환과 여행에서 +10%. 또한 일부 회사는 특정 사용자 그룹을 위해 애플리케이션을 의도적으로 느리게 만들었다: 애플리케이션이 더 빨리 시작할수록 사용자가 더 많은 주문을합니다.그리고 가설이 확인되었습니다.당신이 항상 애플리케이션을 가속화하기 위해 노력하고 싶었지만 강한 논쟁이 없었다면 이제 그들이 있습니다.부팅 시간은 회사 수익에 직접적으로 영향을 미칩니다. App Launch의 종류 앱 출시 유형은 메트릭에 직접적인 영향을 미치므로 그들을 분리하는 것이 중요합니다. 애플은 출시 유형을 결정하기 위해 직접적인 API를 제공하지 않으므로 각 팀은 자신의 방식으로 그것을합니다. 차가운 발사 앱의 완전한 로딩은 처음부터, 가장 느린 유형의 시작입니다. 앱 프로세스는 존재하지 않았고, 캐시가 없으며, 모든 것이 처음부터 초기화됩니다.예를 들어, 이것은 스토어에서 다운로드 한 후 처음으로 시작되거나 메모리에서 앱을 다운로드 한 후 몇 시간 또는 그 이상 후입니다. Cold Launch는 결론을 내리기 위해 버전마다 비교할 수 있는 유일한 정직한 측정입니다.이 기사에서 모든 측정은 Cold Launch에서만 수행됩니다. 뜨거운 발사 앱은 최근에 종료되었으므로 프로세스가 존재하지 않습니다.그러나 앱의 일부는 부분적으로 메모리에 남아 있습니다.시스템 캐시와 동적 링커 캐시는 여전히 따뜻합니다.시스템은 새로운 프로세스를 생성하고 실행 시간을 재시작해야하지만 차가운 시작에서 필요한 I/O 작업의 대부분을 건너 뛰어넘어 눈에 띄게 빨라집니다. 이것은 당신이 메모리에서 응용 프로그램을 다운로드하고 거의 즉시 다시 열 때 발생합니다, 그것은 눈에 띄게 빠르게 열립니다. 뜨거운 발사 애플리케이션은 단순히 최소화되고 다시 열렸습니다.여기에 측정할 것이 없습니다.애플리케이션은 즉시 열리고 어떤 경로도 통과하지 않으며 이미 올바른 장소에 있습니다. 뜨거운 그것은 유용하게 들리지만, 그것은 많은 문제를 일으키고있다 : 우리는 15 분과 10 시간의 "출발"을했다, 왜냐하면 사전 가열이 과정을 사전에 만들었기 때문에, 우리는 주 파일에 만들어진 변수에서 시간을 계산했으며, 사용자는 앱을 즉시 시작하지 않았지만 수십 시간 후. 처음에는 오류가 어디에 있었는지, 발사 시간이 시간과 동일 할 수있는 방법을 이해할 수 없었으며, 다른 데이터가 ms로 측정되었기 때문에 통계가 심하게 손상되었으며 시간을 가진 지표가 실제로 그림을 혼란스럽게 만들었습니다. 조심하십시오, Prewarm는 통계를 완전히 망칠 수 있습니다. App Launch Time, TTI 및 Hang Time 우리가 일을 가속화하기 위해 일하기 시작했을 때, 기본적인 용어조차도 모두가 다르게 이해되었다는 것이 밝혀졌습니다. App Launch 시간 중요: 이것은 즉시 나타나는 시스템 시작 화면에 관한 것이 아니라 루트 뷰 컨트롤러에 관한 것입니다 - 라이브러리 링크가 완료 된 순간, AppDelegate 방법이 완료되었으며, 뷰 컨트롤러가 생성되었으며, 뷰DidAppear 방법이 켜졌습니다. TTI (Time to Interactive) 앱 아이콘을 누르면 사용자가 앱과 상호 작용할 수 있는 순간까지의 시간.당신은 또한 이것을 중요한 경로라고 부를 수 있습니다.이 경로는 주문을 생성하거나 처리하는 시작에 영향을 미치고 비즈니스 측정에 영향을 미치는 것입니다.이 경로는 각 앱마다 다릅니다.이 경로는 모두 다른 최종 지점을 가지고 있기 때문에 비즈니스에 미치는 영향에 따라 선택됩니다. 예를 들어, 우리의 경우에 : 승객 - 지도가 나타나고 사용자가 "어디로 가야할지"를 탭할 수있을 때.이 일이 일어날수록 승객이 주문을 만들 가능성이 높습니다. 운전자의 경우 - 승객 명령 스크린이 표시될 때.이 일이 더 빨리 발생할수록 운전자가 시간에 따라 명령을 볼 수 있고 수행할 가능성이 높습니다. 시간 이것은 인터페이스가 "냉동"되는 시간입니다 - 버튼이 누르면, 그러나 행동은 즉시 일어나지 않습니다, 예를 들어, 그것은 초가 소요됩니다. 일반적으로 이것은 주요 스레드에 대한 무거운 계산의 결과입니다. 그리고 예, 타임은 TTI에 직접 영향을 미치기 때문에 냉동은 사용자가 버튼을 누르면뿐만 아니라 중요한 경로에있는 무언가가 주요 스레드를 차지하고 UI가 렌더링하는 동안 냉동 할 때 나타납니다. 평균값 및 퍼센티일 수백만 명의 사용자로부터 데이터를 수집 한 후에 이러한 지표를 어떻게 든 해석해야하며 적어도 2 가지 옵션이 있습니다 : 평균 값과 비율. Why the Average Does Not Give the Full Picture 평균은 편리하고 계산하기 쉽지만 때로는 결론을 내리는 것이 어렵습니다. 여기에 하나의 예가 있습니다: 우리는 5 사용자와 5 사용자의 시작 시간 = 1, 2, 3, 9, 12 초. 평균 = 5.4 sec. 그러나 이것은 현실을 반영하지 않습니다 : 사용자 5명 중 3명은 3초 또는 그보다 빨리 앱을 실행합니다. 2 사용자는 매우 느리고 전체 통계를 왜곡합니다. 그리고 한 발사가 "1 시간"으로 기록되면 (또는 메트릭 오류로 인해 무한이 발생하면) 평균이 완전히 의미가 없습니다. Percentiles가 작동하는 방법과 왜 그들이 더 정확한지 센티일은 사용자의 X %가 그 시간 내에 적합하도록 앱이 얼마나 많은 시간을 필요로 하는지 보여줍니다. 같은 5 값에 : 1, 2, 3, 9, 12 50번째 센티일 = 3초 → 사용자의 절반은 3초 또는 그보다 빨리 앱을 시작합니다. 80번째 센티일 = 9초 → 80%의 사용자가 ≤9초 만에 앱을 볼 수 있습니다. 99번째 센티일 = 12초 → 가장 "무거운"사용자 : 약한 장치, 가난한 인터넷. 우리는 50번째, 75번째, 90번째, 95번째 센티일을 살펴보고 있습니다.우리는 사용자의 주량 외에도 항상 95번째 센티일을 가속화하려고 노력하며, 애플리케이션을 가장 오래 로드하는 사람들의 삶을 개선하기 위해, 가장 가능성이 장치 또는 그들의 국가의 인터넷 부족 때문입니다. 그건 그렇고, 우리는 전 세계 47 개국에서 활동하고 있으며, 많은 사용자가 약한 장치와 불안정한 인터넷을 가지고있는 많은 국가가 있습니다. Startup Time에 영향을 미치는 것 앱을 크게 느릴 수있는 주요 영역을 살펴보자. 네트워크 요청 체인에서 요청의 수, 요청의 순서, 캐시 할 수있는 능력, 인터넷 속도. App 초기화 스타트업에서 도서관 연결, Objective-C 실행 시간, Swift Reflection, SDK 초기화 (analytics, ads, Firebase), loading dylib, dyld, rebase, symbol binding, DI 컨테이너 초기화. 주요 위협 주요 스레드에 대한 무거운 작업은 TTI를 증가시킵니다 : JSON 크고 복잡한 모델의 디코딩, 동기화 통화, 불필요한 계산 "부팅"이 편리했기 때문에 다른 곳으로 이동할 수 있습니다. Device Characteristics 우리는 오래된 장치를 가속화 할 수는 없지만 논리를 적응하거나 부하의 양을 줄일 수 있습니다. 흥미로운 사실 : 약한 장치와 느린 인터넷을 가진 국가의 경우 Meta는 Facebook Lite 앱을 출시했습니다.The Facebook Lite app is small, allowing you to save space on your phone and use Facebook in 2G conditions. App Acceleration 사이클 앱을 가속화하려면 체계적으로 측정하고 개선해야하며 "눈으로 물건을 고치지 않아야합니다." Our cycle looks like this: Mark up the app for data collection App Launch Splash TTI of all verticals (critical paths) Steps of the critical path Collect and visualize data Raw telemetry data Long-term storage, Big Query 모니터 Xcode Organizer Find places of slowdown Instruments: LaunchTime, Profile, Network, Hangs Timeline analysis Fix problems Restructure requests Cache Offload the main thread Remove everything unnecessary from startup 실험을 실행 결과를 비교, 가설을 확인 Control startup time before a new release UI-performance tests on CI, alerts on degradations 이렇게 하면, 버전마다 앱이 더 빠르고, 더 안정적이고, 더 예측할 수 있게 됩니다.물론, 마크업과 시각화는 모든 사이클에서 이루어지지 않습니다. The Most Effective Ways to Speed Up 가장 큰 효과는 몇 가지 명백하지만 체계적으로 수행 된 단계에서 발생합니다. 네트워크 요청의 순서 변경 Caching Network 요청 주요 트레일러 Offloading the Main thread removing heavy computations at startup 이것들은 우리에게 최소한의 노력과 무언가를 깨뜨릴 위험이있는 최대 TTI 개선을 제공 한 변화입니다. 도구, 프로세스, 상황 이 장에서 나는 당신에게 우리의 도구에 대해 이야기하고 몇 가지 실제 예를 통해 걸어 갈 것입니다.이 장에서는 하나의 도구가 다른 도구를 보완하는 방법과 각각의 모습을 보여줄 것입니다. 측정 없이는 최적화가 불가능합니다.처음에는 프로젝트를 표시해야합니다.우리의 앱 내에서 주요 출시 단계가 표시됩니다. AppLaunchTracker — 첫 번째 컨트롤러가 나타날 때까지 앱을 시작합니다. SplashTracker - 다른 스플래시 스크린 단계의 시간 (기능 전환, 프로필, 위치 결정). CriticalPathTracker - 단계 분해와 그 사이의 시간과 함께 사용자의 중요한 경로 VerticalsTTITracker — 각 제품에 대한 TTI vertical. 모든 데이터는 차트와 대시보드 (Kibana, Redash)에서 시각화됩니다. 또한, 우리는 프로세스를 설립했습니다 : 성능에 의한 버전 비교 CI에 대한 UI 성능 테스트 악화에 대한 경고 정기적 인 주간 회의 및 가속화를위한 도로지도 우리는 Native 도구를 사용합니다 : Xcode Instruments - LaunchTime, 프로필, 네트워크, Hangs Xcode Organizer - Hangs, 앱 출시 나는 또한 작업 실행의 순서를 이해하기 위해 도로지도를 만드는 것이 좋습니다.Ours는 이렇게 보입니다: 이제 행동 속의 모든 것을 살펴보자. 네트워크 요청 명령어 작업 TTI를 가속화하는 가장 효과적인 방법 중 하나는 네트워크 요청의 순서를 재조정하고 독립적 인 요청을 동시에 실행하는 것입니다. 분석을 위해 우리는 Xcode Instruments → Network를 사용하여 요청이 무엇을하고 있는지 및 그 의존성을 명확하게 볼 수 있습니다.This is faster than digging through the code base. 우리가 한 것 : 특정 흐름을 실행 네트워크 요청의 흔적을 캡처 화면이 그 위에 나타날 때의 순간을 덮어 그런 다음 우리는 제품 팀에 갔고, 결과를 보여주고, 네트워크 요청의 순서를 최적화하는 방법을 논의 한 다음 작업을 만들었습니다. 간단한 사례 첫 번째 화면이 의존하는 요청은 너무 늦게 전송되었습니다. 실제로 의존성이 없었기 때문에 화면 수명 주기의 시작으로 이동할 수 있습니다. 결과적으로 사용자는 더 빨리 준비 된 인터페이스를 볼 수 있습니다. 안드로이드에서 우리는 고전적인 " 계단"을 발견했습니다. toggles → driver config → reasons → offers 모든 요청은 엄격하게 순차적으로 진행되었습니다.그 후에 주문의 로딩이 시작되었습니다. 해결책 : 의존성이없는 경우 여러 요청을 하나로 결합하거나 동시에 요청을 실행합니다.Combine several requests into one or run requests in parallel if there are no dependencies. 가장 간단하고 효과적인 방법은 필요하지 않은 곳에서 인공 시퀀스를 제거하는 것입니다.이 시퀀스를 개발자가 편의 또는 다른 이유로 오래 전에 썼을 가능성이 있으며, 그것은 아무도 괴롭히지 않았기 때문에 그렇게 남아 있습니다. 더 복잡한 사례: Driver feed 원래, 로딩 주문 전에 여러 요청의 체인이 실행되었습니다 : 일반 택시 설정 택시 운전기사 설정 Courier 설정 그들 모두는 순차적으로 진행되었고, 각 요청은 특히 느린 인터넷에서 TTI를 느리게 만들었으며, 이는 세 요청의 각각이 총 경로 시간을 늘렸기 때문에 눈에 띄었습니다. 우리는 팀과 함께 일했고 체계를 크게 단순화했습니다 : 독립적 인 요청이 동시에 시작되었습니다. 두 개의 설정 요청을 하나의 요청으로 결합 주문 요청은 최소한의 필요한 데이터를받은 직후에 시작되었습니다.그리고 희귀한 경우에만 우리는 병렬 요청을 기다릴 필요가 있었지만, 심지어는 첫 번째와 동시에 시작되었으며 첫 번째 후부터 시작되지 않았습니다. 결과적으로 : 세 가지 연속적인 요청 대신에, 최선의 경우에 하나가있었습니다. in the worst case — one fewer request than before 그 후에 우리는 실험을 실행했습니다.실험 결과 : TTI 가속도 평균 0.5~0.8초 95번 센티일 - 최대 2초 타임라인 버전별 차트는 앱이 빠르거나 느려졌음을 보여 주지만, 그 이유에 대한 질문에 대답하지 않습니다. 이 문제를 해결하기 위해 우리는 타임라인을 사용합니다.TTI는 단계별로 분할됩니다.즉, 우리는 중요한 경로의 모든 주요 단계를 수집합니다. App 출시 Delegate 방법 Toggles 가져오기 위치를 얻기 프로필 만들기 모듈로의 항해 모듈 초기화 스크린 Rendering 이것은 우리가 정확하게 볼 수있게합니다 : 어느 단계가 빨라졌는가 회귀가 나타난 곳 what exactly affected the final TTI 그것은 우리가 안전하게 큰 변화를 만들고 그 결과를 이해할 수있는 타임라인입니다.이것은 또한 의사 결정을 내리고 회귀 또는 가속화 장소를 찾을 수있는 기초가되었습니다. 이것은 매우 좋은 수준의 세부 사항이지만, 나중에 변경하는 것이 불편 할 것이며 이전의 분해와 차트를 비교하는 것이 불가능하기 때문에 즉시 상세하고 고품질을 계획하십시오. 앱 출시 보고서 및 태풍 제거 Xcode App Launch Report는 어떤 코드가 출시를 느리게 하는지 보여줍니다. 우리의 경우에 : DI 컨테이너 타이핑은 발사 시간의 ~30 %를 차지했습니다. 여러 개의 초기화가 가장 느린 곳에서 일관되게 나타났다. 태풍을 제거한 후: 첫 번째 발사 단계는 ~1.3초에서 ~445ms로 버전 5.122에서 거의 3배 줄어들었다. 타임라인에서 첫 번째 단계가 3 배로 가속화 된 방법을 볼 수 있지만 전체 TTI가 더 커졌습니다. 만약 우리가 붕괴가 없었다면, 우리는 DI를 제거하는 것이 어떻게 모든 것을 느리게 할 수 있었는지 이해하지 못했을 것입니다.하지만 가속화와 함께 버그가이 버전으로 만들었습니다. 밴드 Rate Now we can see how one tool complements another — timelines gave a picture that the launch sped up, but further something broke and affected several initial stages. The next tool that helped us — Xcode Organizer Hangs. 그것은 앱의 새로운 버전이 상당히 느려지기 시작했다는 것을 보여주었습니다.무언가가 주요 스레드를 차단하고 있었습니다.이 도구는 중지 시간의 92%가 제 3 자 SDK에서 온 것을 보여줍니다. 또한, 그것은 구체적인 라인을 지적합니다. 원인 - 주 스레드에 제 3 자 도서관의 네트워크 요청 최대 10 초까지 서버 응답을 기다리고 있습니다.이 문제는 느린 인터넷에서만 재생되었으므로 모든 사람이 노트북과 장치에서 빠른 인터넷을 가지고 있기 때문에 테스터와 개발자에 의해 눈에 띄지 않았다. 우리는 도서관에 문제를 제출했으며, 해결 후에 율이 정상으로 돌아 왔습니다. Research 때때로 우리는 다른 장치에서 실행하여 주요 사용자 경로를 프로파일하고, 그것은 대략 이렇게 보입니다. 여기서 우리는 마이크로 냉각기를 발견했으며, 그들이 영향을 미치는 중요한 경로의 어떤 단계를 즉시 볼 수 있습니다. 사용자 위치 작업을위한 특정 코드가 주요 스레드에 중대한 부하를 부과하고 있음이 밝혀졌습니다.우리는 문제를 해결했으며 무작위 마이크로 냉각이 사라졌고 속도는 더 낮아졌습니다. Also, the profile fetching stage was reduced from 480 ms → 21 ms. UI 성능 테스트 on CI 문제를 찾고 해결하는 것은 좋지만, 앱이 App Store로 이동하기 전에, 그리고 버전 회귀가 시작되기 전에 연속화에 대해 배우는 것이 더 좋습니다. 우리는 생산 서버에서 실제 시나리오를 통과하고 생산 버전과 동일한 지표를 전송하는 네이티브 UI 테스트를 수행했습니다. 실제 사용자 경로를 통해 키 화면으로 이동 밤에 달리기 네트워크 모크 없이 여러 번 실행 sends metrics to telemetry 시간 제한을 초과하면 Slack에 알림이 나타납니다.If the time exceeds the threshold - an alert comes to Slack. 이것은 현재 전체 시스템의 가장 중요한 요소 인 출시 전에 문제를 잡을 수있는 능력을 제공합니다.우리는 또한 모든 동일한 타임 라인을보고 있으며 테스트는 실제 사용 조건에 가능한 한 가깝습니다. 또한, 우리의 캐시가 잘 작동하는지 확인하기 위해, 우리는 CI에 인터넷 throttling을 추가했습니다 - 먼저 테스트는 정상 속도로 실행, 모든 것이 캐시, 다음 3G 느린 인터넷에서 동일한 것입니다. 내 의견으로는, 이것은 스타트업 속도에 대한 작업의 가장 중요한 구성 요소 중 하나입니다 - 그러한 버전이 출시되지 않도록하십시오. Next we want to run this on every Pull Request asynchronously, so as not to block the merge, since performance tests run on the same Mac Mini to keep measurements honest, and with a large team there would be a queue. But we can do this after the merge and understand which PR broke the startup time, so we can quickly revert changes. 버전 비교 프로젝트에 새로운 의존성을 추가하기 전에, 우리는 버전 비교 프로세스 - 개발자에게 앱의 현재 버전과 새로운 의존성을 추가한 버전을 비교하는 방법에 대한 지침을 제공합니다. 그런 실행 중에 한 번 우리는 애플리케이션 부팅에서 250ms 증가를 보았고 이 기능을 일시적으로 그리고 사용자의 일부에만 배포하기로 결정했으므로 애플리케이션을 느리게하지 않도록합니다. Toggle Caching 근처 음식점 마지막 사례를 살펴보자 - 타임라인을 살펴보면, 우리의 경로에서 가장 긴 단계 중 하나는 기능 전환 (700 ms), 차트의 블랙 바를 얻는 것입니다. 우리는 진보적 인 캐시를 구현했습니다 : 전환자가 특정 시간보다 신선한 경우 - 우리는 캐시를 사용합니다. 당연히, 우리는 개선을 보여주는 A/B 실험을 실행합니다. 결과 : 전환 단계는 버전 126–127에서 줄어들었다. TTI는 앱을 자주 열고 느린 인터넷을 가진 사용자가 가장 많이 이익을 얻는 사용자가 눈에 띄게 빠르게되었습니다. 차트는 드라이버의 흔들림을 보여줍니다.그것은 매우 자주 앱을 열기 때문에 너무 작아졌습니다. 교통 조명 및 빠른 상태 제어 수치가 너무 많으면 집계가 필요합니다.우리는 "교통 조명"을 사용합니다. 그들은 매일 복잡한 집계 수식, 개선 및 악화의 자동 평가를 사용합니다.나는 많은 시간이 걸릴 것이기 때문에 지금이 모든 것을 설명하지 않을 것입니다.하지만 전반적으로 "버전에 무슨 일이 있었는지"라는 질문에 대한 빠른 대답을위한 것입니다. 예를 들어, 버전 중 하나에서 TTI에서 눈에 띄는 예외적 인 부스트가있었습니다 - 주요 스레드의 해부의 결과. 최종 결과 6개월간의 노력의 결과를 살펴보자. From the beginning of the year to September (versions 115 → 140): the time from app launch to transition to the product vertical (50th percentile) was reduced by almost 3 times: 2.8 sec → 1 sec 5초 이상 시작하는 사용자의 비율: 58% → 84% 사용자의 75 %가 4 초 만에 앱을 시작했으며 이전에는 6.2 초였습니다. 동시에, 개선은 버전에서 버전으로 점차적으로 발생했지만, 우리는 도구 덕분에 빠르게 잡았던 하나의 버그 스픽을 제외하고 있습니다.프로그램을 추운 상태로 실행하는 사용자의 비율은 5 초보다 빨리 시작됩니다. 결론 앱을 가속화하는 것은 일시적인 최적화가 아니라 지속적인 과정입니다. 명확한 Metrics 이해할 수 있는 타임라인 릴리스 전에 자동 제어 정기적인 분석 및 실험 그리고 가장 중요한 것은 - 수백 밀리 초의 속도는 실제로 비즈니스에 영향을 미칩니다.