feat: Implement baseline profile generation and startup benchmarking
- Added baseline profile generator for Rosetta Messenger to optimize startup performance. - Created startup benchmark tests to measure cold start times under different compilation modes. - Introduced a new Gradle module for baseline profile and benchmark tests. - Updated ChatsListViewModel to show loading skeleton while data is being fetched. - Improved keyboard handling in MessageInputBar by using SHOW_IMPLICIT instead of SHOW_FORCED. - Minor code cleanups and optimizations across various components.
This commit is contained in:
33
baselineprofile/build.gradle.kts
Normal file
33
baselineprofile/build.gradle.kts
Normal file
@@ -0,0 +1,33 @@
|
||||
plugins {
|
||||
id("com.android.test")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.rosetta.messenger.baselineprofile"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 28 // Baseline Profiles require API 28+
|
||||
targetSdk = 34
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
|
||||
targetProjectPath = ":app"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.test.ext:junit:1.1.5")
|
||||
implementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
implementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||
implementation("androidx.benchmark:benchmark-macro-junit4:1.2.2")
|
||||
}
|
||||
3
baselineprofile/src/main/AndroidManifest.xml
Normal file
3
baselineprofile/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
</manifest>
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.rosetta.messenger.baselineprofile
|
||||
|
||||
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
|
||||
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Baseline Profile Generator для Rosetta Messenger
|
||||
*
|
||||
* Запуск:
|
||||
* ./gradlew :baselineprofile:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.rosetta.messenger.baselineprofile.BaselineProfileGenerator
|
||||
*
|
||||
* Результат:
|
||||
* Профиль будет сгенерирован и сохранён — скопируйте его в app/src/main/baseline-prof.txt
|
||||
*
|
||||
* Требования:
|
||||
* - Эмулятор или устройство API 28+ (лучше userdebug/rooted)
|
||||
* - Приложение должно быть установлено
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class BaselineProfileGenerator {
|
||||
|
||||
@get:Rule
|
||||
val baselineProfileRule = BaselineProfileRule()
|
||||
|
||||
@Test
|
||||
fun generateBaselineProfile() = baselineProfileRule.collectBaselineProfile(
|
||||
packageName = "com.rosetta.messenger"
|
||||
) {
|
||||
// 1. Cold start — запуск приложения
|
||||
pressHome()
|
||||
startActivityAndWait()
|
||||
|
||||
// 2. Ждём загрузки (Splash → Auth/Main)
|
||||
device.waitForIdle()
|
||||
Thread.sleep(3000) // Ждём splash + auth flow
|
||||
|
||||
// 3. Если попали на экран логина — пропускаем (профиль соберёт auth UI)
|
||||
// Если попали на список чатов — скроллим
|
||||
val chatList = device.wait(Until.hasObject(By.scrollable(true)), 5000)
|
||||
if (chatList) {
|
||||
// Скролл списка чатов вниз и обратно
|
||||
val scrollable = device.findObject(By.scrollable(true))
|
||||
scrollable?.let {
|
||||
it.scroll(androidx.test.uiautomator.Direction.DOWN, 2.0f)
|
||||
device.waitForIdle()
|
||||
Thread.sleep(500)
|
||||
|
||||
it.scroll(androidx.test.uiautomator.Direction.UP, 2.0f)
|
||||
device.waitForIdle()
|
||||
Thread.sleep(500)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Ждём перед завершением
|
||||
device.waitForIdle()
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.rosetta.messenger.baselineprofile
|
||||
|
||||
import androidx.benchmark.macro.CompilationMode
|
||||
import androidx.benchmark.macro.FrameTimingMetric
|
||||
import androidx.benchmark.macro.StartupMode
|
||||
import androidx.benchmark.macro.StartupTimingMetric
|
||||
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Startup Benchmark для замера холодного старта Rosetta
|
||||
*
|
||||
* Запуск:
|
||||
* ./gradlew :baselineprofile:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.rosetta.messenger.baselineprofile.StartupBenchmark
|
||||
*
|
||||
* Сравнивает 3 режима компиляции:
|
||||
* - None (интерпретатор) — worst case
|
||||
* - BaselineProfile — с нашими профилями
|
||||
* - Full (полная AOT) — best case
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class StartupBenchmark {
|
||||
|
||||
@get:Rule
|
||||
val benchmarkRule = MacrobenchmarkRule()
|
||||
|
||||
@Test
|
||||
fun startupNoCompilation() = startup(CompilationMode.None())
|
||||
|
||||
@Test
|
||||
fun startupBaselineProfile() = startup(CompilationMode.Partial())
|
||||
|
||||
@Test
|
||||
fun startupFullCompilation() = startup(CompilationMode.Full())
|
||||
|
||||
private fun startup(compilationMode: CompilationMode) {
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = "com.rosetta.messenger",
|
||||
metrics = listOf(StartupTimingMetric(), FrameTimingMetric()),
|
||||
compilationMode = compilationMode,
|
||||
iterations = 5,
|
||||
startupMode = StartupMode.COLD,
|
||||
setupBlock = {
|
||||
pressHome()
|
||||
}
|
||||
) {
|
||||
startActivityAndWait()
|
||||
|
||||
// Ждём появления контента (чат-лист или auth)
|
||||
device.waitForIdle()
|
||||
Thread.sleep(2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user