Android 앱을 최적화하려면 동시 워크플로와 코루틴 정지에 관한 심층적인 가시성이 필요합니다.
로컬 프로파일링은 유용한 스냅샷을 제공하지만, 오버헤드 없이 이러한 인사이트를 프로덕션으로 확장하는 것은 여전히 어려운 과제입니다.
이 세션에서는 지속적인 성능 피드백 루프를 위한 아키텍처 청사진을 제공합니다.
androidx.tracing 2.0: 백엔드를 동적으로 교체하고 스레드 간에 코루틴 컨텍스트 (Perfetto 흐름)를 전파합니다.
스마트 계측: 로컬 ART 추적을 사용하여 핫스팟을 찾은 다음 오버헤드가 낮은 추적 지점의 삽입을 자동화합니다.
매크로 벤치마킹: CI의 실제 기기에서 회귀를 추적하고 성능 향상을 확인합니다.
필드 추적: ProfilingManager를 사용하여 로컬 및 CI 추적을 넘어섭니다.
traceCoroutine를 사용하여 비동기 실행을 추적하거나 전파 토큰을 콜백 기반 코드에 전달할 수 있습니다.스크립트
라훌 라비쿠마르
여러분, 안녕하세요.
제 이름은 라훌입니다.
저는 Android 개발자 환경팀의 엔지니어입니다.
저는 앱 및 라이브러리 성능에 중점을 두고 있으며, 이 강연은 추적에 관한 것입니다.
추적에 대해 자세히 알아보기 전에 먼저 여러분이 따라올 수 있도록 몇 가지 공통 용어를 정립하고자 합니다.
따라서 이 강연에서 추적을 언급할 때는 명시적 추적, 즉 앱이 무엇을 하고 있는지 알 수 있도록 코드를 수동으로 계측하는 것을 의미합니다.
제가 설명할 자동 프로파일러를 말하는 것이 아닙니다.
따라서 수동 추적은 코드베이스에 브레드크럼을 남기는 기술입니다. 이를 통해 나중에 성능 문제나 기타 문제가 발생했을 때 원인을 파악할 수 있습니다.
일반적으로 로그보다 훨씬 더 좋습니다.
대규모 앱의 Logcat 출력을 본 적이 있으신가요?
일반적으로 쉽게 추론할 수 없는 거대한 텍스트 벽입니다.
반면에 추적은 훨씬 더 좋습니다.
시각화하기가 더 쉽습니다.
기간이 있습니다.
구조가 있습니다.
도구가 있으므로 데이터를 세분화하여 언제 어떤 일이 발생했는지 정확히 확인할 수도 있습니다.
훨씬 더 좋습니다.
그리고 에이전트에게 트레이스를 가리키며 질문을 할 수 있습니다.
Trace Processor, 벤치마크와 같은 도구를 사용하고 트레이스를 측정항목으로 변환할 수도 있습니다.
이러한 작업은 모두 Logcat으로는 하기 어렵지만 트레이스로는 할 수 있습니다.
반면 프로파일링(자동 프로파일링)은 일반적으로 알려진 문제에 대응하는 데 사용됩니다.
예를 들어 앱이 느리게 시작되거나 버벅거리는 현상이 발생하여 원인을 파악하고 싶을 수 있습니다.
이 시점에서 수동 trace가 없는 경우 자동 프로파일링을 사용하면 됩니다.
모든 메서드 시작과 종료를 계측하는 ART 메서드 트레이스와 같은 것을 사용할 수 있습니다.
또는 Simpleperf와 같은 도구를 사용하여 스택 샘플링을 살펴볼 수도 있습니다.
따라서 모든 메서드 항목과 종료를 사용하여 오버헤드가 높아지는 대신 호출 스택 샘플을 살펴보고 앱의 핫 경로에 무엇이 있는지 확인합니다.
2026년에는 RAM이 어디서나 문제가 되므로 메모리, 메모리 누수, 힙 프로필을 살펴볼 수 있습니다.
이 프로파일러도 활용할 수 있습니다.
힙 유지 경로를 살펴볼 수 있습니다.
메모리를 할당하는 코드를 확인할 수 있으며, 이 도구로 수행할 수 있는 분석의 종류는 다양합니다.
실제로
이제 이 어휘를 갖게 되었습니다.
이제 새 라이브러리를 빌드한 이유를 알아보겠습니다.
추적 API, 1.0 API는 원래 Android Jelly Bean에서 도입되었습니다.
그때가 2012년이었습니다.
14년도 더 된 일입니다.
그 이후로 API는 생각만큼 발전하지 않았습니다.
몇 가지 확장 프로그램을 추가했습니다.
비동기 이벤트 및 카운터는 Android 10에 추가되었습니다.
2019년의 일입니다.
그것으로 충분합니다.
이후 API는 발전하지 않았습니다.
하지만 생태계는 많이 발전했습니다.
플랫폼 추적은 비약적으로 개선되었지만 API 추가 측면에서 개발자에게 전달된 것은 없습니다.
YouTube는 이러한 현실을 바꾸고자 합니다.
또 다른 큰 문제는 추적에 1.0의 고정된 추적 백엔드가 있다는 것입니다. 즉, 코드에 추적 섹션을 추가할 때마다 항상 시스템 트레이스 버퍼에 저장됩니다.
Atrace는 해당 데이터를 소유하는 Android 추적 하위 시스템입니다.
따라서 트레이스를 가져와 필드 원격 분석으로 변환하려면 정말 어렵습니다. 이제 atrace가 데이터를 저장하는 방식을 파악해야 하기 때문입니다.
문서화가 잘 되어 있지만 사용하기 어려울 수 있는 형식입니다.
이 문제는 해결해야 합니다.
따라서 Google은 추적 데이터를 원하는 곳으로 가져갈 수 있는 도구를 제공하고자 합니다.
또한 Tracing 1.0에서는 라벨이 128자(영문 기준)로 제한되었습니다.
2012년에는 합리적인 제한이었을지 모르지만 2026년에는 다소 이상해 보입니다.
더 나쁜 것은 트레이스에 추가 메타데이터를 추가할 수 없다는 점입니다. 이는 추가적인 문제입니다.
그리고 오버헤드가 있습니다.
따라서 앱을 추적할 때는 항상 오버헤드를 매우 잘 알고 있어야 합니다. 시스템을 관찰하는 행위가 시스템이 내부적으로 실행하는 작업을 변경해서는 안 되기 때문입니다.
따라서 재현하려는 경합 상태가 있는 경우 추적으로 인해 경합 상태가 사라지지 않아야 합니다.
따라서 다른 방식으로 수행하는 모든 시맨틱을 보존해야 합니다.
Tracing 1.0은 오버헤드가 비교적 높았기 때문에 이를 개선하고자 했습니다.
좋습니다.
문제에 관해 많은 이야기를 나눴습니다.
이제 몇 가지 문제를 시각적으로 살펴보고 실제로 어떻게 복합적으로 발생하는지 살펴보겠습니다.
여기 Perfetto 트레이스가 있습니다.
com.example.app 앱에 대한 것입니다.
기본 스레드가 있고 initializeComponent라는 단일 슬라이스가 있습니다.
이제 더 자세한 정보를 원합니다.
세부정보를 추가해 보겠습니다.
이제 initializeComponent가 performInitialize를 호출한다는 것을 알 수 있습니다.
아직 어떤 구성요소가 초기화되고 있는지 알 수 없습니다.
이제 Tracing 1이 있는 도구 벨트의 유일한 도구는 이름이므로 슬라이스 이름에 추가합니다.
추적 슬라이스 이름은 128자로 제한되므로 추적에서 잘립니다.
설상가상으로, 이제 자르기 오버헤드는 추적 시 발생하는 오버헤드이기도 합니다.
이제 이 문제가 꽤 빨리 발생합니다.
이 문제는 꼭 해결하고 싶었습니다.
Perfetto 트레이스를 확인하고 시각적으로 어떻게 표시되는지 확인했습니다.
이제 이러한 trace 섹션을 내보내는 코드를 작성하는 관점에서 살펴보겠습니다.
여기에도 initializeComponent API가 있습니다.
컨텍스트와 이름을 가져와 performInitialize를 호출합니다.
지금까지는 꽤 순조로웠습니다.
이제 Tracing 1.0 API를 사용하여 트레이스를 추가하려고 합니다. androidx.tracing.trace를 가져와 트레이스 블록을 사용하면 됩니다.
이름을 지정합니다.
여기서는 performInitialize입니다.
하지만 이전과 마찬가지로 구성요소의 이름을 알고 싶으므로 Kotlin의 문자열 보간을 사용하여 이름을 추가합니다.
여기서 첫 번째 문제가 발생합니다.
여기에는 128자(영문 기준)의 글자 수 제한이 있습니다.
라훌 라비쿠마르
Tracing 2를 사용하면 이 문제를 해결할 수 있습니다.
androidx.tracing.trace 대신 Tracer를 사용합니다.
API는 매우 유사합니다.
trace 대신 Tracer.global.trace가 있습니다.
카테고리가 있습니다.
카테고리에 대해서는 나중에 더 자세히 설명하겠지만 기본적으로 이름이 있습니다.
그런 다음 추적하려는 코드 블록이 있습니다.
이제 글자 수 제한이 없어졌습니다.
추적 오버헤드에 대해 설명했습니다.
이 부분을 조금 더 자세히 살펴보고 어떻게 개선했는지 알아보겠습니다.
Android 16을 실행하는 Pixel 6a에서 단일 시작 및 종료 섹션을 삽입하는 작업은 약 1,200나노초가 걸렸습니다.
그다지 많지 않은 것처럼 보일 수 있지만, 많은 트레이스 섹션을 추가하는 매우 활발한 코드가 있다면 앱이 프레임을 렌더링하는 데 사용할 수 있는 시간이 줄어듭니다.
따라서 이 시간을 빼앗고 싶지 않습니다.
AndroidX Perfetto SDK를 통해 많은 부분이 개선되었습니다.
이는 컴포지션 추적을 위해 도입한 실험용 SDK로, 실제로 상황을 많이 개선했습니다.
이것은 JNI 바인딩이 있는 Perfetto C SDK이고 C SDK는 APK 내에서 약 4MB이므로 이 SDK를 출시하는 것을 원하지 않았습니다.
이러한 제품을 배송해 드리고자 한 것은 아닙니다.
이제 Tracing 2를 사용하면 목적에 맞게 빌드된 라이브러리를 사용할 수 있습니다.
오버헤드를 훨씬 더 개선했으며, 이제 Tracing 2가 7배 더 빨라졌습니다. 즉, 원하는 작업을 할 시간이 더 많아지고 인프라에 소요되는 시간이 줄어듭니다.
이러한 기술은 이전에는 불가능했던 어떤 일을 가능하게 하나요?
예를 들어 Metro의 런타임 추적 기능이 있습니다.
Metro는 종속 항목 삽입 라이브러리입니다.
Kotlin 멀티플랫폼을 지원합니다.
Kotlin 컴파일러 플러그인을 사용합니다.
종속 항목 삽입을 위한 모든 코드를 생성하고 Tracing 2.0과 통합되어 코드의 모든 @Inject 비용을 더 쉽게 시각화할 수 있습니다.
예를 들어 보겠습니다.
앱 그래프가 있다고 가정해 보겠습니다.
Metro에서는 모든 항목의 범위가 그래프로 지정되므로 AnalyticsTracker라는 단일 필드가 있습니다.
제공 바인딩도 있습니다.
OkHttpClient를 impl로 사용하는 네트워크 클라이언트가 있고 AnalyticsTracker의 구현은 AnalyticsTrackerImpl에서 가져옵니다. 네트워크 클라이언트의 인스턴스가 필요한데, 그래프는 이를 제공하는 방법을 알고 있습니다.
따라서 내부적으로 컴파일러 플러그인은 다음과 같은 코드를 생성합니다.
따라서 AnalyticsTracker가 있는 경우 getter는 네트워크 클라이언트의 인스턴스를 만들고 이를 AnalyticsTrackerImpl의 생성자에 전달합니다. 이는 좋은 방법입니다.
하지만 이제 추적을 사용 설정하면 tracker impl뿐만 아니라 필요한 모든 종속 항목의 생성 비용을 확인할 수 있습니다.
이제 유형을 인스턴스화하는 비용뿐만 아니라 트레이스의 모든 종속 항목도 확인할 수 있습니다. 이는 매우 강력한 기능입니다.
금융 앱인 Wise는 이 정보를 사용하여 앱 시작 시간을 45% 개선했습니다.
앱 시작 중에 초기화할 필요가 없는 항목을 연기할 수 있었고, 잘못된 범위의 항목도 식별할 수 있었습니다. 싱글톤이어야 하는 항목이 실수로 싱글톤이 아니었던 경우도 있었는데, 이러한 항목은 코드에서 확인하고 쉽게 수정할 수 있습니다.
이들의 시작 측정항목을 보면 정말 인상적입니다.
초기 프레임 표시 시간뿐만 아니라 전체 표시 시간도 개선되었습니다.
또 다른 파트너인 BandLab은 CI에 동일한 정보를 사용합니다.
따라서 모든 @Inject 바인딩의 비용뿐만 아니라 해당 비용을 발생시킨 스레드도 알 수 있습니다.
이를 알게 되면 기본 스레드에서 비용이 많이 드는 @Inject 결합을 모두 찾을 수 있고 CI에서 쿼리를 실행하여 비용이 많이 드는 결합 목록을 모두 가져온 다음 이를 도입한 변경사항에 다시 귀속시킬 수 있습니다. 이제 실제 변경사항을 비난하고 수정할 수 있습니다.
라훌 라비쿠마르
좋습니다.
지금까지 Tracing 1과 비교하여 개선된 모든 사항을 설명했지만, 이제 Tracing 2에 포함된 이전에는 불가능했던 새로운 기능에 관해 설명하겠습니다.
핵심 기능 중 하나는 컨텍스트 전파입니다. 최신 앱은 점점 더 복잡해지고 있으므로 워크로드는 여러 스레드, 여러 코루틴, 여러 프로세스에서 실행됩니다.
모든 단일 스레드와 프로세스 간에 요청이 전파되는 방식을 시각화하려고 합니다.
컨텍스트 전파를 통해 이 작업이 가능해집니다.
따라서 원인에 대해 아무것도 알려주지 않고 슬라이스 목록만 제공한다면 유용하기는 하지만 추론하기가 쉽지 않습니다.
하지만 이와 같은 인과 관계가 있다고 말씀드리면 무슨 일이 일어나고 있는지 파악하고 각 슬라이스의 비용을 연결하기가 훨씬 더 쉬워집니다.
이것이 바로 컨텍스트 전파를 통해 가능해지는 것입니다.
컨텍스트 전파가 완전히 지원된다는 점을 알려드리게 되어 기쁩니다. 실제 예시는 다음과 같습니다.
com.example.app Perfetto 트레이스를 다시 살펴보면 performInitialize에 시간이 너무 오래 걸려 비용을 연기하거나 다른 스레드로 이동하려고 한다고 가정해 보겠습니다.
충분히 할 수 있는 일입니다.
performInitialize를 다른 대화목록으로 옮겼다고 가정해 보겠습니다.
이제 슬라이스의 계층적 특성이 사라졌습니다.
따라서 이제 코드를 살펴보지 않으면 initializeComponent가 performInitialize를 발생시킨 원인이라는 것을 알 수 없습니다.
이상적인 상황에서는 화살표를 통해 A가 B를 유발했음을 알 수 있고, 이를 통해 이벤트의 인과 관계를 추론할 수 있습니다.
플랫폼에서 바인더 트랜잭션에 대해 이 작업을 실제로 수행하는 시스템 트레이스를 본 적이 있을 수도 있습니다.
여기에서 Compose 벤치마크가 system_server에 대한 바인더 트랜잭션을 만들어 프레임을 준비하는 것을 볼 수 있습니다.
플랫폼 추적 관점에서는 항상 가능했던 일이지만 앱 개발자에게는 도구를 제공하지 않았습니다.
이제 Tracing 2.0에서 Kotlin 코루틴 컨텍스트 전파가 완전히 지원됩니다.
별도의 조치를 취하지 않아도 됩니다.
바로 사용할 수 있습니다.
여기에는 fetchProfile라는 단일 정지 메서드가 있습니다.
또 다른 정지 메서드인 client.get를 호출한 다음 Dispatchers.Default로 전환하여 응답을 역직렬화합니다.
매일 다루는 매우 전형적인 코루틴 코드입니다.
이제 이를 추적한다고 가정해 보겠습니다.
trace 섹션을 추가합니다.
여기서는 trace 대신 traceCoroutine API를 사용합니다. 정지 코드 블록이기 때문입니다. 그런 다음 Dispatchers.Default로 컨텍스트를 전환하기 전에 내부 호출을 위해 동일한 traceCoroutine API를 사용하여 다른 내부 trace 섹션을 추가합니다.
이제 트레이스에 다음과 같이 표시됩니다.
작업이 worker-1에서 시작된 후 실제로 컨텍스트 전환이 이루어져 worker-3으로 이동한 것을 확인할 수 있으며, 트레이스에서 이를 가리키는 유용한 화살표를 볼 수 있습니다.
뿐만 아니라 가운데 사각형을 확대하면 deserialize가 실제로 worker-1에서 시작되었고 withContext 호출로 인해 컨텍스트 전환이 발생한 다음 worker-3으로 계속되고 fetchProfile가 worker-3에서 재개되었음을 알 수 있습니다. 코루틴 메커니즘이 발생하는 컨텍스트 전환 수를 최소화하려고 하기 때문입니다.
코루틴이 정지된 시점과 재개된 시점을 정확하게 확인할 수 있으며 전체 인과 관계 이벤트 체인을 확인할 수 있습니다.
Tracing 2를 사용하면 트레이스에 커스텀 메타데이터를 추가할 수도 있습니다.
이전에는 트레이스 섹션의 이름만 사용할 수 있었지만 이제는 원하는 만큼 메타데이터 항목을 추가할 수 있는 메타데이터 블록을 추가할 수 있습니다.
그리고 이를 트레이스에 추가할 수 있습니다.
실제 trace를 보면 추가한 모든 메타데이터가 실제로 trace에 포함되어 있어 모든 도구에서 사용하고 활용할 수 있습니다.
이제 여러분이 알고 있는 모든 정보, 항상 추가하고 싶었던 모든 정보를 포함할 수 있습니다.
추적을 통해 가능한 추상적인 기능을 많이 소개해 드렸지만, 이 모든 기능이 빛을 발하는 곳은 재구성 추적이라는 Jetpack Compose의 새로운 기능입니다.
이 기능은 Compose 1.13의 일부로 제공되므로 지금 바로 사용할 수 있습니다.
예를 들어 보겠습니다.
이것은 여러분 모두에게 익숙한 컴포지션 추적입니다.
이는 Compose 초기부터 존재했습니다.
컴포저블과 모든 하위 슬라이스의 상대적 비용과 각 슬라이스가 발생하는 스레드의 비용을 알려줍니다.
이건 간단합니다.
하지만 맥락은 알 수 없습니다.
이러한 상황이 발생한 이유는 알 수 없습니다.
재구성된 이유는 알려주지 않습니다.
축소하면 이전 슬라이드에서 살펴본 관심 영역이 표시됩니다.
왼쪽에는 디스패치 입력 이벤트가 발생한 것을 확인할 수 있습니다.
이로 인해 일부 상태가 변경되고 리컴포지션이 발생했을 수 있습니다.
하지만 확실하지 않습니다.
확실히 알 수 없습니다.
하지만 이제 리컴포지션 추적을 사용하면 리컴포지션이 발생한 이유를 정확하게 알려주는 맞춤 메타데이터를 트레이스에 추가할 수 있습니다.
그뿐만 아니라 이제 이러한 이벤트가 서로 관련되어 있다는 것을 쉽게 알 수 있습니다.
클릭하면 정확히 어떤 상태가 변경되었는지 알려줍니다.
상태였으며, 이전에 true였던 변경 가능한 상태 값이 false로 전환되었다고 알려줍니다.
뿐만 아니라 이를 유발한 정확한 호출 스택도 알려줍니다.
여기서 AbstractClickableNode.performClick가 발생하여 변경 가능한 상태가 변경되고 이로 인해 리컴포지션이 발생한 것을 확인할 수 있습니다.
이것은 정말 멋진 기능이며, 이전에는 할 수 없었던 일입니다.
이러한 도구는 새로운 Android Performance Analyzer 도구에서도 사용할 수 있습니다. 에이전트 도구를 사용해 보고 싶다면 이 도구를 사용해 보세요.
맞습니다.
컨텍스트 전파는 Kotlin 코루틴에서 완전히 지원된다고 말씀드렸습니다.
하지만 코루틴과 비코루틴 코드를 혼합하여 사용하는 코드나 자체적으로 스레드를 실행하는 코드, 또는 어떤 이유로 콜백을 사용하는 너무 오래된 브라운필드 코드는 어떻게 해야 할까요?
좋습니다.
다행히도 컨텍스트 전파의 모든 이점을 계속 누릴 수 있습니다.
콜백과 실행자를 사용하는 브라운필드 API가 있다고 가정해 보겠습니다.
구현은 executor.execute를 사용하고 구현 세부정보인 runCatching를 호출하여 응답을 가져온 다음 콜백에 응답을 알립니다.
좋습니다.
이제 이를 추적하려면 Tracer.global.trace를 실행하고 라벨을 지정한 다음 블록을 둘러쌉니다.
하지만 이벤트의 인과 관계도 보존하고 싶으므로 전파 토큰이라는 새로운 선택적 유형을 허용하겠습니다.
그리고 토큰을 내부 trace API에 전달합니다.
따라서 이 메서드를 호출하는 사람은 선택적으로 전파 토큰을 제공할 수 있으므로 발생하는 이벤트의 인과 관계를 보존할 수 있습니다.
좋습니다.
이제 이 API의 호출자는 정지 API가 될 수 있습니다.
여기에는 정지 함수 profileRequest가 있으며, 이 함수는 콜백과 실행자를 사용하는 API를 호출합니다.
여기서는 정지 세계와 콜백 세계를 연결하는 두 세계를 연결하므로 suspendCoroutine 빌더를 사용합니다.
연속을 가져온 다음 API를 호출하고 응답을 받으면 콜백에서 연속을 재개합니다.
그리고 제가 정의하는 실행자는 profileRequestContext입니다.
단일 스레드 실행자입니다.
또한 traceCoroutine는 코루틴 컨텍스트 내에 전파 토큰을 삽입하므로 코루틴 컨텍스트 내에서 전파 토큰을 가져온 다음 브라운필드 API에 입력으로 전달하여 이벤트의 인과 관계를 유지할 수 있습니다.
그리고 trace에서 이를 매우 명확하게 보여줍니다.
여기서는 worker-3에서 시작된 작업이 컨텍스트 전환을 수행한 후 단일 스레드 실행기로 전환되었다가 다시 worker에서 재개됩니다.
꽤 깔끔하죠.
라훌 라비쿠마르
좋습니다.
Tracer.global를 맞춤설정할 수도 있습니다.
지금까지 Tracing 2.0 API는 Tracer.global에 대한 매우 좋은 기본값을 제공하지만, 원하는 경우 맞춤설정할 수 있습니다.
또한 Tracing 2.0은 JVM과 Android를 지원합니다. Java 및 Android 라이브러리 모두에서 추적을 완벽하게 지원하기 위해서입니다.
따라서 초기화의 세 단계는 trace 싱크를 만드는 것입니다.
트레이스 싱크는 트레이스 패킷이 저장되는 방식입니다.
유선 형식과 출력 트레이스가 저장되는 위치를 정의합니다.
Perfetto 형식을 지원하는 트레이스 싱크의 즉시 사용 가능한 구현을 제공하지만, 물론 자체 트레이스 싱크를 작성할 수도 있습니다.
이는 Tracing 1에서 사용할 수 있는 유일한 백엔드가 atrace였다는 문제를 해결하려는 시도로 거슬러 올라갑니다.
YouTube는 이 문제를 해결하고자 했습니다.
이제 OpenTelemetry 싱크를 작성하거나 자체 싱크를 가져오는 것이 가능합니다.
이 설정을 재정의할 수 있습니다.
싱크가 있으면 트레이스 드라이버를 만들고 싱크를 제공할 수 있습니다.
그런 다음 사용 설정할 카테고리를 선택할 수도 있습니다.
이 부분에서 카테고리가 진가를 발휘합니다.
따라서 출시 버전에서 디버그 버전과 비교하여 일부 카테고리가 다른 카테고리보다 더 중요하다고 판단하고, 사용 설정할 카테고리를 지정할 수 있습니다.
또는 '출처와 관계없이 모든 카테고리를 사용 설정하고 싶다'고 말할 수도 있습니다.
그래도 괜찮습니다.
trace에 전역인 맞춤 속성을 추가할 수도 있습니다.
따라서 빌드 버전, 버전, 트레이스에 연결하려는 기타 메타데이터와 같은 항목을 addAttribute API를 사용하여 삽입할 수 있습니다.
마지막으로 setGlobalTracer를 호출하여 추적기를 등록할 수 있습니다.
이것이 Tracer.global를 이 추적기 인스턴스에 바인딩하는 것이며, 이후에 프로세스에서 사용할 수 있는 것입니다.
Android에서는 프로세스의 수명 주기가 훨씬 더 잘 정의되어 있으므로 AbstractTraceDriver.Factory를 구현하고 create 메서드를 재정의하기만 하면 이전과 똑같이 작동합니다.
따라서 싱크를 만든 다음 드라이버를 만듭니다.
여기에서 등록하지 않아도 됩니다.
이것이 먼저 발생한 일이므로 등록을 진행해 드리겠습니다.
좋습니다.
좋습니다. 추적은 멀티 플랫폼을 지원한다는 점을 다시 한번 강조하고 싶습니다.
이는 Compose가 여러 플랫폼에서 실행되고 가능한 한 많은 위치에서 추적할 수 있도록 하기 위한 의도적인 선택입니다.
현재는 Android와 JVM을 지원하지만, 구현 방식이 완전히 개방되어 있으므로 원한다면 향후 다른 플랫폼도 지원할 수 있습니다.
라훌 라비쿠마르
Android에서 소스 코드를 축소하고 최적화하는 데 사용되는 도구인 R8은 Tracing 2.0을 사용합니다.
또한 트레이스를 활용하여 코드를 실제로 축소하고 최적화하는 속도가 25% 더 빨라지도록 R8을 최적화했습니다.
즉, 빌드 시간이 단축되고 최적화 패스를 기다리는 시간이 줄어듭니다.
R8에서 수행한 모든 최적화를 살펴보지는 않겠지만, 추적을 통해 무엇이 가능한지 보여드리기 위해 한 가지만 살펴보겠습니다.
R8은 기본적으로 소스 간 변환기입니다.
따라서 여러 JAR을 가져와 축소하고 최적화한 후 dex할 수 있는 또 다른 JAR 집합을 생성합니다.
여기서 AGP는 앱뿐만 아니라 모든 종속 항목인 JAR을 읽는 것으로 시작합니다.
따라서 Gradle 작업자를 사용하여 모든 입력 JAR을 읽기 시작합니다.
읽고 싶은 JAR이 아주 많습니다.
JAR 읽기가 완료되면 R8은 코드를 파싱하고 코드가 나타내는 내용과 모든 진입점의 모델을 빌드하여 코드 최적화 및 축소를 시작합니다.
이 슬라이스는 R8이 실제로 최적화 프로세스를 시작할 준비가 된 시점을 나타냅니다.
중간에 빈 공간이 보일 수 있으며, 사용되지 않는 CPU 시간이 왜 있는지 궁금할 수 있습니다.
그 이유는 이전 API의 작동 방식이 모든 작업자가 모든 파일을 읽을 때까지 기다린 후에 파싱을 수행했기 때문입니다.
물론 이 문제는 해결할 수 있으며, 실제로 해결했습니다.
AGP 9에서는 입력 JAR 읽기를 완료하는 순간 바로 처리를 시작하는 것을 볼 수 있습니다.
모든 작업자가 입력을 읽을 때까지 기다릴 필요가 없었습니다.
이로 인해 모든 입력을 읽기 위해 기다리는 시간이 크게 줄었습니다.
이는 트레이스를 보면 매우 명확하지만 소스 코드만 보고 최적화할 가치가 있는 항목을 찾을 때는 명확하지 않은 최적화입니다.
AndroidX 엔지니어가 매일 사용하는 또 다른 도구는 Metalava입니다.
Metalava는 Google에서 제공하는 모든 API가 개발자가 기대하는 API 호환성을 보장하고, Google에서 피하고자 하는 문제 없이 외부 사용자가 완전히 사용할 수 있도록 하는 데 사용하는 도구입니다.
이 도구는 모든 공개 API와 제한된 API에서 실행되므로, 여기서 절약하는 시간은 AndroidX 엔지니어에게 실제로 돌아가는 시간입니다.
Metalava는 추적을 사용하여 제출 후 시간을 무려 15%나 개선했습니다.
추적을 통해 어떤 일이 발생했는지 설명합니다. Metalava는 두 가지 유형의 검사를 실행합니다. 첫 번째는 제한된 API를 검사하는 것이고 두 번째는 공개 API를 검사하는 것입니다.
하지만 패턴이 보일 수도 있습니다.
이러한 검사를 두 번 실행하기 위해 많은 설정을 진행하고 있습니다.
그러면 모든 프로그램 입력이 읽히고, PSI(프로그램 구조 인터페이스)가 빌드됩니다. 즉, 추론할 수 있는 코드의 형태가 정확히 무엇인지 알 수 있습니다.
그런 다음 AST를 빌드하고 원하는 모든 린트 검사를 적용하기 시작합니다.
이러한 비용이 많이 드는 작업을 두 번 수행하고 있습니다.
이 문제를 어떻게 해결했나요?
한 번만 수행했습니다.
따라서 모든 설정 작업을 두 번 수행하는 대신 한 번만 수행하고 제한된 검사와 공개 검사 모두에 해당 입력을 재사용하여 비용을 절감했습니다.
네.
좋습니다.
라훌 라비쿠마르
Tracing 2.0으로 무엇이 가능한지 맛보기를 보여드렸으니, 이제 추적이 중요하다는 점을 아는 것만큼 무엇을 추적해야 하는지 아는 것도 중요하다는 점을 알아야 합니다.
모든 곳에 trace를 분산시키지 않는 것이 좋습니다.
추적할 대상을 신중하게 선택해야 합니다. 추적할 가치가 있는 대상에 관한 몇 가지 가이드라인을 알려드리겠습니다.
따라서 가장 먼저 해야 할 일은 트레이스에서 설명되지 않는 간격을 찾는 것입니다.
따라서 스레드가 실행 중이거나 실행 가능한 상태일 때 시스템 트레이스에 명백한 공백이 있고 그 이유를 알 수 없으며 앱이 무엇을 하고 있는지 알 수 없다면 조사해 볼 가치가 있습니다.
이때 시스템 트레이스에 메서드 트레이스를 오버레이하여 트레이스할 가치가 있는 항목을 확인하고 관련 트레이스 섹션을 추가할 수 있습니다.
사용자 여정을 통해 우선순위를 정해야 합니다.
따라서 앱 시작을 살펴봐야 합니다.
핵심 앱 사용자 여정을 살펴보고 추적을 통해 설명할 수 있는지 파악해야 합니다.
따라서 어떤 것에 대해 추론할 수 없을 때는 언제든지 추적할 가치가 있습니다.
또한 트레이스에서 깊은 호출 스택이나 설명되지 않는 CPU 사용량을 살펴보고 정확히 무슨 일이 일어나고 있는지 파악한 다음 코드베이스의 해당 부분을 트레이스로 변환해야 합니다.
따라서 추적할 수 있습니다.
그리고 스레드가 절전 모드일 때마다 절전 모드인 이유를 파악할 수 있어야 합니다.
I/O를 기다리거나 바인더 트랜잭션을 기다리거나 발생할 것으로 예상되는 외부 입력 이벤트를 기다려야 합니다.
하지만 설명할 수 없는 수면이 있다면, 분명히 무언가 숨어 있을 수 있으므로 조사할 가치가 충분히 있습니다.
그런 다음 코드에 모든 트레이스 섹션을 분산시킨 후에는 변경사항을 추적할 수 있도록 벤치마크로 변환해야 합니다.
따라서 코드베이스가 발전함에 따라 측정항목이 변화하는지, 대시보드가 실제로 큰 변화를 반영하는지 확인할 수 있습니다. 이를 통해 중요한 변화가 발생했음을 알 수 있습니다.
또는 이전 단계를 모두 건너뛰고 에이전트에게 요청할 수도 있습니다.
이러한 가이드라인을 가져와 스킬로 만들고, 원하는 에이전트에 지정한 다음 실행해 보세요.
네.
라훌 라비쿠마르
추적이 가치 있는 작업이며 꼭 해야 한다는 점을 설득했다면 이제 프로젝트 설정을 살펴보겠습니다.
쉽게 알 수 있습니다.
Tracing은 간단한 기존 Java 라이브러리이므로 Perfetto 호환 트레이스 드라이버와 트레이싱 콤보인 androidx.tracing:tracing-wire에 대한 종속 항목을 정의하면 됩니다. 현재 안정화 버전은 2.0.5입니다.
자바 라이브러리인 경우에도 완전히 동일합니다. 이 역시 완전한 멀티플랫폼 API입니다.
적절한 경우 다른 플랫폼으로도 확장할 계획입니다.
지금까지의 내용을 간단히 요약해 보겠습니다.
지금까지 새로 추가된 멋진 기능에 대해 설명해 드렸습니다.
따라서 컨텍스트 전파는 매우 강력합니다.
커스텀 정보, 메타데이터, 호출 스택 정보, 기타 모든 종류의 정보를 삽입할 수 있습니다.
아직 말씀드리지 못한 것이 많습니다.
추적 형식을 변경할 수 있고, 추적 싱크를 변경할 수 있으며, 추적 데이터를 OpenTelemetry로 가져갈 수 있습니다.
이 모든 것을 할 수 있습니다.
추적은 멀티플랫폼입니다.
최대한 많은 사람이 이 기능을 활용할 수 있도록 하기 위해 매우 신중하게 내린 결정입니다.
Perfetto, AndroidX Benchmarks, 새로운 성능 분석기, 이전 Android 프로파일러 등 Google에서 제공하는 모든 도구는 Tracing 2.0을 지원합니다.
지금까지 경청해 주셔서 감사합니다.
개발자 서밋에서 유익한 시간 보내시길 바랍니다. 감사합니다.