Melhorar o desempenho do app com os perfis de referência

1. Antes de começar

Este codelab mostra como gerar perfis de referência para otimizar o desempenho do aplicativo e como conferir os benefícios de usar esse tipo de perfil.

O que será necessário

O que você vai fazer

  • Configurar o projeto para usar geradores de perfis de referência.
  • Gerar perfis de referência para otimizar a inicialização e o desempenho de rolagem do app.
  • Analisar os ganhos de desempenho proporcionados pela biblioteca Jetpack Macrobenchmark.

O que você vai aprender

  • O que são perfis de referência e como eles podem melhorar o desempenho do app.
  • Como gerar perfis de referência.
  • Ganhos de desempenho com os perfis de referência.

2. Etapas da configuração

Para começar, clone o repositório do GitHub na linha de comando desta maneira:

$ git clone https://github.com/android/codelab-android-performance.git

Outra opção é fazer o download de dois arquivos ZIP:

Abrir o projeto no Android Studio

  1. Na janela "Welcome to Android Studio", selecione 61d0a4432ef6d396.png Open an Existing Project.
  2. Selecione a pasta [Download Location]/codelab-android-performance/baseline-profiles. Selecione o diretório baseline-profiles.
  3. Quando o Android Studio importar o projeto, confira se é possível executar o módulo app para criar o aplicativo de exemplo usado posteriormente.

App de exemplo

Neste codelab, você trabalhará com o aplicativo de exemplo JetSnack. Ele é um app virtual de pedidos de lanche que usa o Jetpack Compose.

Para avaliar o desempenho do aplicativo, você precisa entender a estrutura da interface e como o app se comporta para acessar os elementos da interface dos comparativos de mercado. Execute o app e navegue pelas telas básicas, simulando pedidos de lanches. Você não precisa saber os detalhes de como o app foi projetado.

23633b02ac7ce1bc.png

3. O que são os perfis de referência

Os perfis de referência melhoram a velocidade de execução do código em cerca de 30% desde a primeira inicialização, evitando a interpretação e as etapas de compilação just-in-time (JIT) para caminhos de código incluídos. Ao enviar um perfil de referência em um app ou biblioteca, o Android Runtime (ART) pode otimizar os caminhos de código incluídos pela compilação antecipada (AOT, na sigla em inglês), fornecendo melhorias de desempenho a cada novo usuário e a cada atualização do app. Essa otimização guiada por perfil (PGO, na sigla em inglês) permite que os apps otimizem a inicialização, reduzam a instabilidade de interação e melhorem o desempenho geral no momento da execução para usuários finais desde o primeiro uso.

Com um perfil de referência, todas as interações do usuário, como inicialização do app, navegação entre telas ou rolagem de conteúdo, ficam mais suaves desde a primeira vez que ele é executado. O aumento da velocidade e capacidade de resposta de um app leva a mais usuários ativos por dia e uma taxa média de visitas recorrentes mais alta.

Os perfis de referência ajudam a orientar a otimização além da inicialização do app, oferecendo interações comuns do usuário que melhoram o tempo de execução do app desde a primeira inicialização. A compilação guiada AOT não depende de dispositivos do usuário e pode ser feita uma vez por versão em uma máquina de desenvolvimento, em vez de um dispositivo móvel. Com o envio de versões com um perfil de referência, as otimizações de apps ficam disponíveis muito mais rápido do que usando apenas os perfis da nuvem.

Quando um perfil de referência não é usado, todo o código do app passa por compilação JIT na memória após ser interpretado ou em um arquivo odex em segundo plano quando o dispositivo está inativo. Os usuários podem ter uma experiência inferior ao executar um app depois da instalação ou de uma primeira atualização antes de os novos caminhos serem otimizados.

4. Configurar o módulo do gerador de perfis de referência

Você pode gerar perfis de referência com uma classe de teste de instrumentação que exige a adição de um novo módulo do Gradle ao projeto. A maneira mais fácil de fazer isso é com o assistente de módulo do Android Studio que vem com o Android Studio Hedgehog ou mais recente.

Abra a janela do assistente de novo módulo clicando com o botão direito do mouse no projeto ou módulo no painel Project e selecione New > Module.

232b04efef485e9c.png

Na janela aberta, selecione Baseline Profile Generator no painel "Templates".

b191fe07969e8c26.png

Além dos parâmetros usuais, como nome do módulo, nome do pacote, idioma ou linguagem de configuração do build, existem duas entradas que não são comuns em um novo módulo: Target application e Use Gradle Managed Device.

Target application é o módulo de app usado para gerar perfis de referência. Se você tiver mais de um módulo de app no projeto, selecione para qual deles os geradores serão executados.

A caixa de seleção Use Gradle Managed Device define o módulo para executar os geradores de perfis de referência em emuladores do Android gerenciados automaticamente. Saiba mais sobre dispositivos gerenciados pelo Gradle em Escalonar seus testes com dispositivos gerenciados pelo Gradle. Se você desmarcar essa opção, os geradores usarão qualquer dispositivo conectado.

Depois de definir todos os detalhes sobre o novo módulo, clique em Finish para continuar com a criação.

Mudanças implementadas pelo assistente de módulos

O assistente de módulos faz várias mudanças no projeto.

Ele adiciona um módulo do Gradle chamado baselineprofile ou com o nome selecionado no assistente.

Esse módulo usa o plug-in com.android.test, que instrui o Gradle a não incluí-lo no app. Por esse motivo, ele só pode conter código de teste ou comparações. Ele também aplica o plug-in androidx.baselineprofile, que permite a geração automática de perfis de referência.

O assistente também faz mudanças no módulo do app de destino selecionado. Especificamente, ele aplica o plug-in androidx.baselineprofile e adiciona as dependências androidx.profileinstaller e baselineProfile ao módulo recém-criado build.gradle(.kts):

plugins {
  id("androidx.baselineprofile")
}

dependencies {
  // ...
  implementation("androidx.profileinstaller:profileinstaller:1.3.0")
  "baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}

A adição da dependência androidx.profileinstaller permite que você:

  • verifique localmente os ganhos de desempenho dos perfis de referência gerados;
  • use perfis de referência no Android 7 (nível 24 da API) e Android 8 (nível 26 da API), que não oferecem suporte aos perfis de nuvem;
  • use perfis de referência em dispositivos que não têm o Google Play Services.

A dependência baselineProfile(project(":baselineprofile")) permite que o Gradle saiba de qual módulo ele precisa para extrair os perfis de referência gerados.

Agora que o projeto está definido, programe uma classe geradora de perfis de referência.

5. Criar um gerador de perfis de referência

Normalmente, você gera perfis de referência para as jornadas típicas dos usuários do seu app.

O assistente de módulo cria uma classe de teste BaselineProfileGenerator básica capaz de gerar o perfil de referência para a inicialização do app e tem a seguinte aparência:

@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
        rule.collect("com.example.baselineprofiles_codelab") {
            // This block defines the app's critical user journey. This is where you
            // optimize for app startup. You can also navigate and scroll
            // through your most important UI.

            // Start default activity for your app.
            pressHome()
            startActivityAndWait()

            // TODO Write more interactions to optimize advanced journeys of your app.
            // For example:
            // 1. Wait until the content is asynchronously loaded.
            // 2. Scroll the feed content.
            // 3. Navigate to detail screen.

            // Check UiAutomator documentation for more information about how to interact with the app.
            // https://d.android.com/training/testing/other-components/ui-automator
        }
    }
}

Essa classe usa uma regra de teste BaselineProfileRule e contém um método de teste para gerar o perfil. O ponto de entrada para gerar o perfil é a função collect(). Ela requer apenas dois parâmetros:

  • packageName: o pacote do app.
  • profileBlock: o último parâmetro lambda.

Na lambda profileBlock, especifique as interações que abrangem as jornadas típicas do usuário do app. A biblioteca executa o profileBlock várias vezes, coleta as classes e funções chamadas e gera o perfil de referência no dispositivo com o código a ser otimizado.

Por padrão, a classe de gerador criada contém interações para iniciar a Activity padrão e espera até que o primeiro frame do app seja renderizado usando o método startActivityAndWait().

Estender o gerador com jornadas personalizadas

A classe gerada também inclui algumas TODO para programar mais interações e otimizar jornadas avançadas do app. Isso é recomendado para que você possa otimizar o desempenho além da inicialização do aplicativo.

No app de exemplo, você pode identificar essas jornadas seguindo estas etapas:

  1. Inicie o aplicativo. Isso já está parcialmente coberto pela classe gerada.
  2. Aguarde o conteúdo ser carregado de forma assíncrona.
  3. Role a lista de lanches.
  4. Abra a página de detalhes do lanche.

Mude o gerador para incluir as funções descritas que abrangem as jornadas típicas no snippet a seguir:

// ...
rule.collect("com.example.baselineprofiles_codelab") {
    // This block defines the app's critical user journey. This is where you
    // optimize for app startup. You can also navigate and scroll
    // through your most important UI.

    // Start default activity for your app.
    pressHome()
    startActivityAndWait()

    // TODO Write more interactions to optimize advanced journeys of your app.
    // For example:
    // 1. Wait until the content is asynchronously loaded.
    waitForAsyncContent()
    // 2. Scroll the feed content.
    scrollSnackListJourney()
    // 3. Navigate to detail screen.
    goToSnackDetailJourney()

    // Check UiAutomator documentation for more information about how to interact with the app.
    // https://d.android.com/training/testing/other-components/ui-automator
}
// ...

Agora, programe interações para cada jornada mencionada. É possível programar as jornadas como uma função de extensão do MacrobenchmarkScope para que você tenha acesso aos parâmetros e às funções que esse elemento fornece. Dessa forma, você pode reutilizar as interações com os comparativos de mercado para verificar os ganhos de desempenho.

Aguardar conteúdo assíncrono

Muitos apps têm algum tipo de carregamento assíncrono na inicialização, também conhecido como estado totalmente exibido, que informa ao sistema quando o conteúdo é carregado e renderizado, e o usuário pode interagir com ele. Aguarde o estado no gerador (waitForAsyncContent) com estas interações:

  1. Encontre a lista de lanches do feed.
  2. Aguarde até que alguns itens da lista fiquem visíveis na tela.
fun MacrobenchmarkScope.waitForAsyncContent() {
   device.wait(Until.hasObject(By.res("snack_list")), 5_000)
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered.
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

Jornada de rolagem da lista

Para a jornada de rolagem da lista de lanches (scrollSnackListJourney), siga estas interações:

  1. Encontre o elemento da interface da lista de lanches.
  2. Defina as margens do gesto para não acionar a navegação do sistema.
  3. Role a lista e espere a interface se estabilizar.
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation.
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

Jornada de acesso à página de detalhes

A última jornada (goToSnackDetailJourney) envolve estas interações:

  1. Encontre a lista de lanches para conferir todos os itens disponíveis.
  2. Selecione um item da lista.
  3. Clique no item e aguarde até que a página de detalhes seja carregada. Você pode aproveitar o fato de que a lista de lanches não estará mais na tela.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
    val snackList = device.findObject(By.res("snack_list"))
    val snacks = snackList.findObjects(By.res("snack_item"))
    // Select snack from the list based on running iteration.
    val index = (iteration ?: 0) % snacks.size
    snacks[index].click()
    // Wait until the screen is gone = the detail is shown.
    device.wait(Until.gone(By.res("snack_list")), 5_000)
}

Depois de definir todas as interações necessárias para que o gerador de perfis de referência esteja pronto para execução, é necessário definir o dispositivo em que ele será executado.

6. Preparar um dispositivo para execução do gerador

Para gerar perfis de referência, recomendamos usar um emulador, como um dispositivo gerenciado pelo Gradle ou um dispositivo com o Android 13 (API 33) ou mais recente.

Para tornar o processo reproduzível e automatizar a geração de perfis de referência, você pode usar dispositivos gerenciados pelo Gradle. Eles permitem executar testes em um emulador do Android sem que seja necessário iniciar e desmontar manualmente. Saiba mais sobre dispositivos gerenciados pelo Gradle em Escalonar seus testes com dispositivos gerenciados pelo Gradle.

Para definir um dispositivo gerenciado pelo Gradle, adicione a definição dele ao arquivo build.gradle.kts do módulo :baselineprofile, conforme mostrado no snippet a seguir:

android {
  // ...

  testOptions.managedDevices.devices {
    create<ManagedVirtualDevice>("pixel6Api31") {
        device = "Pixel 6"
        apiLevel = 31
        systemImageSource = "aosp"
    }
  }
}

Nesse caso, usamos o Android 11 (nível 31 da API) e a imagem do sistema aosp tem acesso root.

Em seguida, configure o plug-in do Gradle do perfil de referência para usar o dispositivo gerenciado do Gradle definido. Para fazer isso, adicione o nome do dispositivo à propriedade managedDevices e desative useConnectedDevices, conforme mostrado no snippet a seguir:

android {
  // ...
}

baselineProfile {
   managedDevices += "pixel6Api31"
   useConnectedDevices = false
}

dependencies {
  // ...
}

Em seguida, gere o perfil de referência.

7. Gerar o perfil de referência

Quando o dispositivo estiver pronto, você poderá criar o perfil de referência. O plug-in do Gradle do perfil de referência cria tarefas do Gradle para automatizar todo o processo de execução da classe de teste do gerador e aplicar os perfis de referência gerados no app.

O assistente de novo módulo criou uma configuração para executar rapidamente a tarefa do Gradle com todos os parâmetros necessários para execução sem a necessidade de alternar entre o terminal e o Android Studio.

Localize a configuração Generate Baseline Profile e clique no botão Run 599be5a3531f863b.png para executar.

6911ecf1307a213f.png

A tarefa inicia a imagem do emulador definida anteriormente. Execute as interações da classe de teste BaselineProfileGenerator várias vezes e, em seguida, desmonte o emulador e envie a saída para o Android Studio.

Quando o gerador terminar, o plug-in do Gradle vai colocar automaticamente o baseline-prof.txt gerado no app de destino (módulo :app) na pasta src/release/generated/baselineProfile/.

fa0f52de5d2ce5e8.png

(Opcional) Executar o gerador na linha de comando

Como alternativa, execute o gerador na linha de comando. Você pode aproveitar a tarefa criada pelo dispositivo gerenciado pelo Gradle, :app:generateBaselineProfile. Esse comando executa todos os testes no projeto definido pela dependência baselineProfile(project(:baselineProfile)). Como o módulo também contém comparativos de mercado para a verificação posterior dos ganhos de desempenho, esses testes falham com um aviso sobre a execução de comparações em um emulador.

android
   .testInstrumentationRunnerArguments
   .androidx.benchmark.enabledRules=BaselineProfile

Para isso, filtre todos os geradores de perfis de referência com o seguinte argumento do executor de instrumentação para que todos os comparativos sejam ignorados:

O comando inteiro vai ficar assim:

./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

Distribuir o app com perfis de referência

Depois que o perfil de referência for gerado e copiado para o código-fonte do app, crie a versão de produção normalmente. Não é necessário fazer mais nada para distribuir os perfis de referência para os usuários. Eles são escolhidos pelo Plug-in do Android para Gradle durante o build e incluídos no AAB ou no APK. Em seguida, faça upload do build no Google Play.

Quando os usuários instalam ou atualizam o app, o perfil de referência também é instalado, resultando em uma melhora do desempenho já na primeira execução.

A próxima etapa mostra como verificar a melhora no desempenho do app com os perfis de referência.

8. (Opcional) Personalizar a geração de perfis de referência

O plug-in dos perfis de referência para Gradle inclui opções para personalizar a forma como os perfis são gerados para atender às suas necessidades específicas. Você pode mudar o comportamento com um bloco de configuração baselineProfile { } em scripts de build.

O bloco de configuração no módulo :baselineprofile afeta a execução dos geradores com a possibilidade de adicionar managedDevices e decidir entre useConnectedDevices ou dispositivos gerenciados pelo Gradle.

O bloco de configuração no módulo de destino :app decide onde os perfis serão salvos ou como eles serão gerados. É possível mudar os seguintes parâmetros:

  • automaticGenerationDuringBuild: se ativado, será possível gerar o perfil de referência ao criar o build de lançamento de produção. É útil ao programar em CI antes de enviar o app.
  • saveInSrc: especifica se os perfis de referência gerados serão armazenados na pasta src/. Como alternativa, você pode acessar o arquivo na pasta :baselineprofile do build.
  • baselineProfileOutputDir: define onde os perfis de referência gerados serão armazenados.
  • mergeIntoMain: por padrão, os perfis de referência são gerados por variante de build (variação de produto e tipo de build). Caso você queira juntar todos os perfis em src/main, ative essa flag.
  • filter: é possível filtrar quais classes ou métodos serão incluídos ou excluídos dos perfis de referência gerados. Isso pode ser útil para desenvolvedores de bibliotecas que querem apenas o código da biblioteca incluída.

9. Verificar melhorias no desempenho da inicialização

Depois de gerar e adicionar o perfil de referência ao app, verifique se ele tem o efeito desejado no desempenho.

O assistente de novo módulo cria uma classe de comparativo de mercado chamada StartupBenchmarks. Ela mede o tempo de inicialização do app e o compara ao uso de perfis de referência.

A classe tem esta aparência:

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        rule.measureRepeated(
            packageName = "com.example.baselineprofiles_codelab",
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()

                // TODO Add interactions to wait for when your app is fully drawn.
                // The app is fully drawn when Activity.reportFullyDrawn is called.
                // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
                // from the AndroidX Activity library.

                // Check the UiAutomator documentation for more information on how to
                // interact with the app.
                // https://d.android.com/training/testing/other-components/ui-automator
            }
        )
    }
}

Ela usa MacrobenchmarkRule, que é capaz de executar comparativos de mercado para o app e coletar métricas de desempenho. O ponto de entrada para programar uma comparação é a função measureRepeated da regra.

São necessários vários parâmetros:

  • packageName:: qual aplicativo avaliar.
  • metrics: o tipo de informação que você quer avaliar durante a comparação.
  • iterations: quantas vezes a comparação se repete.
  • startupMode: como você quer que o aplicativo seja iniciado após o início da comparação.
  • setupBlock: quais interações com seu app precisam acontecer antes da medição.
  • measureBlock: quais interações com seu app você quer medir durante a comparação.

A classe de teste também contém dois testes: startupCompilationeNone() e startupCompilationBaselineProfiles(), que chamam a função benchmark() com um compilationMode diferente.

CompilationMode

O parâmetro CompilationMode define como o aplicativo é pré-compilado no código de máquina. Ele inclui as opções abaixo:

  • DEFAULT: pré-compila o app parcialmente usando os perfis de referência, se disponíveis. Esta opção será usada se nenhum parâmetro compilationMode for aplicado.
  • None(): redefine o estado de compilação do app sem fazer uma pré-compilação. A compilação Just-In-Time (JIT) fica ativada durante a execução do app.
  • Partial(): pré-compila o app com perfis de referência, execuções de aquecimento ou ambos.
  • Full(): pré-compila todo o código do aplicativo. Esta é a única opção disponível no Android 6 (nível 23 da API) e anteriores.

Para começar a otimizar o desempenho do aplicativo, use o modo de compilação DEFAULT, que tem um desempenho parecido com o do app disponível no Google Play. Caso queira comparar os benefícios de desempenho proporcionados pelos perfis de referência, compare os resultados dos modos de compilação None e Partial.

Modificar o comparativo de mercado para aguardar conteúdo

Os comparativos de mercado são programados de forma parecida com os geradores de perfis de referência ao criar interações com o app. Por padrão, as comparações criadas aguardam apenas a renderização do primeiro frame, como ocorreu com o BaselineProfileGenerator. Portanto, recomendamos a melhoria para aguardar o conteúdo assíncrono.

Para isso, reutilize as funções de extensão que criou para o gerador. Como esse comparativo de mercado captura tempos de inicialização (usando StartupTimingMetric()), recomendamos incluir apenas a espera pelo conteúdo assíncrono aqui e, em seguida, programar um comparativo de mercado separado para as outras jornadas do usuário definidas no gerador.

// ...
measureBlock = {
   startActivityAndWait()

   // The app is fully drawn when Activity.reportFullyDrawn is called.
   // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
   // from the AndroidX Activity library.
   waitForAsyncContent() // <------- Added to wait for async content.

   // Check the UiAutomator documentation for more information on how to
   // interact with the app.
   // https://d.android.com/training/testing/other-components/ui-automator
}

Executar as comparações

É possível executar as comparações da mesma forma que os testes instrumentados. Você pode executar a função de teste ou toda a classe que tem o ícone de gutter ao lado.

587b04d1a76d1e9d.png

Selecione um dispositivo físico. A execução de comparativos de mercado em um emulador do Android falha no momento da execução com um aviso de que a comparação pode gerar resultados incorretos. Embora seja tecnicamente possível fazer a execução em um emulador, o que seria avaliado é o desempenho da máquina host. Se ela estiver sobrecarregada, as comparações terão um desempenho mais lento e inverso.

94e0da86b6f399d5.png

Após a comparação, o app é recriado e, em seguida, executa as comparações de novo. Os comparativos de mercado iniciam, interrompem e até reinstalam seu app várias vezes com base nas iterations definidas.

Quando as comparações forem concluídas, você poderá consultar os resultados na saída do Android Studio, conforme mostrado na captura de tela abaixo:

282f90d5f6ff5196.png

Na imagem, podemos notar que o tempo de inicialização do app é diferente para cada CompilationMode. Os valores médios são mostrados na tabela abaixo:

timeToInitialDisplay [ms]

timeToFullDisplay [ms]

Nenhum

202.2

818.8

BaselineProfiles

193.7

637.9

Melhoria

4%

28%

A diferença entre os modos de compilação de timeToFullDisplay é de 180ms, que é uma melhoria de aproximadamente 28% apenas por ter um perfil de referência. O CompilationNone tem um desempenho pior porque o dispositivo precisa fazer a maior parte da compilação JIT durante a inicialização do app. O CompilationBaselineProfiles tem um desempenho melhor porque a compilação parcial com perfis de referência AOT compila o código que o usuário provavelmente vai usar, mas deixa o código não essencial sem pré-compilação para que ele não precise ser carregado imediatamente.

10. (Opcional) Verificar a melhoria de desempenho da rolagem

Assim como na etapa anterior, você pode medir e avaliar o desempenho de rolagem. Primeiro, crie uma classe de teste ScrollBenchmarks com a regra de comparação e dois métodos de teste que usam modos de compilação diferentes:

@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {

   @get:Rule
   val rule = MacrobenchmarkRule()

   @Test
   fun scrollCompilationNone() = scroll(CompilationMode.None())

   @Test
   fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())

   private fun scroll(compilationMode: CompilationMode) {
       // TODO implement
   }
}

No método scroll, use a função measureRepeated com os parâmetros obrigatórios. Para o parâmetro metrics, use FrameTimingMetric, que mede quanto tempo leva para produzir frames de interface:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           // TODO implement
       },
       measureBlock = {
           // TODO implement
       }
   )
}

Desta vez, você precisa dividir mais as interações entre setupBlock e measureBlock para medir apenas a duração do frame no primeiro layout e rolagem do conteúdo. Portanto, coloque as funções que iniciam a tela padrão no setupBlock e as funções de extensão waitForAsyncContent() e scrollSnackListJourney() já criadas no measureBlock:

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           pressHome()
           startActivityAndWait()
       },
       measureBlock = {
           waitForAsyncContent()
           scrollSnackListJourney()
       }
   )
}

Quando a comparação estiver pronta, ela poderá ser executada como antes para que você receba os resultados como mostrado na captura de tela a seguir:

84aa99247226fc3a.png

A FrameTimingMetric gera resultados de duração de frames em milissegundos (frameDurationCpuMs) nos percentis 50, 90, 95 e 99. No Android 12 (nível 31 da API) e versões mais recentes, ela também retorna um valor correspondente ao tempo excedido pelos frames (frameOverrunMs). Esse valor pode ser negativo, o que significa que há tempo sobrando para produzir o frame.

Com esses resultados é possível observar que os CompilationBaselineProfiles têm, em média, uma duração de frame 2ms menor, o que talvez não seja perceptível para os usuários. Nos outros percentis, entretanto, os resultados são mais óbvios. Para a versão P99, a diferença é de 43,5ms, o que é mais de três frames pulados em um dispositivo operando a 90 QPS. Por exemplo, para o Pixel 6, a diferença é de 1000ms/90 QPS = aproximadamente 11ms de tempo máximo para renderizar um frame.

11. Parabéns

Parabéns! Você concluiu este codelab e melhorou o desempenho do app com os perfis de referência.

Outros recursos

Confira outros recursos a seguir:

Documentos de referência