Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
5c38e84
Import the benchmark project
terrakok Nov 9, 2026
755c5a3
Extract the multiplatform benchmark runtime
terrakok Nov 9, 2026
e79d5ed
Add startup and real-frame measurements
terrakok Nov 9, 2026
073df6b
Add startup reports and HTTP reporting
terrakok Nov 9, 2026
da5fbd6
Add real-frame reports and HTTP reporting
terrakok Nov 9, 2026
e6bd9f7
Add configurable real-frame warmup
terrakok Nov 9, 2026
bc32a21
Add average-frame and FPS reporting
terrakok Nov 9, 2026
5ccdede
Add a typed JSON report collector
terrakok Nov 10, 2026
a56a1f6
Move web GC into a JavaScript interop function
terrakok Nov 10, 2026
53c720c
Add benchmark apps for desktop, web, and iOS
terrakok Nov 10, 2026
68095ac
Add benchmark cases and resources to the app
terrakok Nov 10, 2026
c7e3254
Separate offscreen runs from UI benchmarks
terrakok Nov 10, 2026
bfe8d44
Add startup timing across platforms
terrakok Nov 10, 2026
560eca2
Add run IDs and atomic report saving
terrakok Nov 10, 2026
af98b3f
Adopt the iOS scene lifecycle
terrakok Nov 10, 2026
1412dfd
Setup proguard for the desktop bench app
terrakok Nov 10, 2026
6313273
Add launch script
terrakok Nov 10, 2026
3d105ea
Disable CORS limitations for collector
terrakok Nov 10, 2026
8d6dded
Improve browser selection
terrakok Nov 10, 2026
60c6be5
Delete original benchmarks
terrakok Nov 11, 2026
d4c7c12
Setup included build on compose-core for benchmarks project
terrakok Nov 11, 2026
b0729c6
Implement benchmark launcher
terrakok Nov 11, 2026
599b456
Add readme
terrakok Nov 11, 2026
625fc50
Add kotlin/cmp versions params
terrakok Nov 11, 2026
d5928f9
Add html report generation
terrakok Nov 11, 2026
7029e63
Increase default test timeout
terrakok Nov 11, 2026
7e58ad8
Add FPS distributions reports
terrakok Nov 11, 2026
e739ddc
Add run-benchmarks.sh launch script
terrakok Nov 11, 2026
bfd42fa
New params: list of browsers and targets
terrakok Nov 11, 2026
64edc0e
Improve percentile graphs
terrakok Nov 11, 2026
23d2428
Fix infinity FPS measurement
terrakok Nov 11, 2026
638d9d4
Add target environment collection
terrakok Nov 12, 2026
9f6032d
Add environment metadata to benchmark reports
terrakok Nov 12, 2026
62a35a4
Add total time to benchmark reports
terrakok Nov 12, 2026
0909b5f
Add launcher environment metadata
terrakok Nov 12, 2026
511d0ab
Add test parameters to benchmark reports
terrakok Nov 12, 2026
605b593
Add links to raw benchmark reports
terrakok Nov 12, 2026
7a22dfd
Rename benchmark reports module to data
terrakok Nov 12, 2026
c2b3282
Remove redundant gradle properties
terrakok Nov 12, 2026
a0c3787
Extract HTML benchmark report generator
terrakok Nov 12, 2026
ebeecc8
Add validation and help to benchmarks launcher
terrakok Nov 12, 2026
af3247c
Document how to add benchmark cases
terrakok Nov 12, 2026
f2452f4
Add benchmark report environment info toggle
terrakok Nov 12, 2026
33818bb
Remove core checkout path from benchmark report metadata
terrakok Nov 12, 2026
9c57914
Add cross-platform benchmark workflows
terrakok Nov 13, 2026
4cbad61
Add JVM graphics backends to benchmark runtime
terrakok Nov 13, 2026
762f3b2
Add benchmark workflows for pull requests and pushes
terrakok Nov 13, 2026
13f59ce
Add unified cross-platform benchmark workflow
terrakok Nov 13, 2026
eacb197
Fix JVM benchmarks compilation
terrakok Nov 13, 2026
3c3cac0
Separate benchmark Skiko initialization by Compose version
terrakok Nov 13, 2026
0844270
Add JVM GPU backends to legacy Skiko benchmarks
terrakok Nov 13, 2026
61b9505
Add Selenium WebDriver support for web benchmarks
terrakok Nov 13, 2026
5955564
Replace colons in benchmark report filenames
terrakok Nov 13, 2026
3f87568
Enable software Mesa rendering for JVM benchmarks
terrakok Nov 13, 2026
b1e10e1
Run web benchmarks separately for JS and Wasm
terrakok Nov 13, 2026
243328a
Enable software WebGL for CI benchmarks
terrakok Nov 13, 2026
de521c3
Run JVM benchmarks on macOS
terrakok Nov 13, 2026
2f976c6
Limit CI benchmarks to selected tests
terrakok Nov 13, 2026
338a2ca
Run web benchmarks on macOS GPUs
terrakok Nov 13, 2026
c56a66f
Add dev Compose to benchmark versions
terrakok Nov 13, 2026
d2c78a6
Reduce benchmark warmup and frame counts
terrakok Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/actions/resolve-benchmark-versions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Resolve benchmark versions
description: Resolve stable and prerelease Compose and Kotlin versions from Maven Central, plus the latest dev Compose from JetBrains.
runs:
using: composite
steps:
- name: Resolve versions
shell: bash
env:
ACTION_PATH: ${{ github.action_path }}
run: python3 "$ACTION_PATH/resolve_versions.py"
68 changes: 68 additions & 0 deletions .github/actions/resolve-benchmark-versions/resolve_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import json
import os
import re
import urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path

def latest_versions(group, artifact):
url = (
"https://repo.maven.apache.org/maven2/"
f"{group.replace('.', '/')}/{artifact}/maven-metadata.xml"
)
with urllib.request.urlopen(url, timeout=60) as response:
metadata = ET.parse(response)
stable, unstable = [], []
for node in metadata.findall("./versioning/versions/version"):
version = node.text
match = re.fullmatch(
r"(\d+)\.(\d+)\.(\d+)(?:-(alpha|beta|rc)(\d*)?)?",
version, re.IGNORECASE,
)
if not match:
continue
major, minor, patch, stage, number = match.groups()
key = (int(major), int(minor), int(patch))
if stage is None:
stable.append((key, version))
else:
rank = {"alpha": 0, "beta": 1, "rc": 2}[stage.lower()]
unstable.append((key + (rank, int(number or 0)), version))
if not stable or not unstable:
raise RuntimeError(f"Missing stable or prerelease version for {artifact}")
return max(stable)[1], max(unstable)[1]

def latest_dev_compose_version():
url = (
"https://packages.jetbrains.team/maven/p/cmp/dev/"
"org/jetbrains/compose/compose-gradle-plugin/maven-metadata.xml"
)
with urllib.request.urlopen(url, timeout=60) as response:
metadata = ET.parse(response)
version = metadata.findtext("./versioning/latest")
available = {node.text for node in metadata.findall("./versioning/versions/version")}
if not version or version not in available:
raise RuntimeError("Missing latest Compose version in the dev repository metadata")
return version


compose = latest_versions("org.jetbrains.compose", "compose-gradle-plugin")
dev_compose = latest_dev_compose_version()
selected_compose = tuple(dict.fromkeys((*compose, dev_compose, "local")))
kotlin = latest_versions("org.jetbrains.kotlin", "kotlin-gradle-plugin")
versions = {
"compose": {"stable": compose[0], "unstable": compose[1], "dev": dev_compose, "local": "local"},
"kotlin": {"stable": kotlin[0], "unstable": kotlin[1]},
}
results = Path(os.environ["RUNNER_TEMP"]) / "benchmark-results"
results.mkdir(parents=True, exist_ok=True)
(results / "versions.json").write_text(json.dumps(versions, indent=2) + "\n")
with open(os.environ["GITHUB_ENV"], "a") as env:
env.write(f"COMPOSE_VERSIONS={','.join(selected_compose)}\n")
env.write(f"KOTLIN_VERSIONS={','.join(kotlin)}\n")
with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as summary:
summary.write("## Benchmark versions\n\n")
summary.write(f"- Compose: {', '.join(selected_compose)}\n")
summary.write(f"- Kotlin: {', '.join(kotlin)}\n")
summary.write("- Local Compose uses the core checkout's own Kotlin version.\n")
print(json.dumps(versions, indent=2))
177 changes: 177 additions & 0 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
name: Benchmarks

on:
workflow_dispatch:
push:
branches:
- benchmarks

permissions:
contents: read

jobs:
jvm:
name: JVM benchmarks
runs-on: macos-26-xlarge
timeout-minutes: 360

defaults:
run:
shell: bash
working-directory: compose/mpp/benchmarks

steps:
- name: Checkout Repository
uses: actions/checkout@v5

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Resolve latest benchmark versions
uses: ./.github/actions/resolve-benchmark-versions

- name: Build launcher
run: ./gradlew :benchmarks-launcher:installDist --console=plain

- name: Run JVM benchmarks
run: |
mkdir -p "$RUNNER_TEMP/benchmark-results"
./benchmarks-launcher/build/install/benchmarks-launcher/bin/benchmarks-launcher \
--target jvm \
--compose-version "$COMPOSE_VERSIONS" \
--kotlin-version "$KOTLIN_VERSIONS" \
--compose-core-path "$GITHUB_WORKSPACE" \
--tests AnimatedVisibility LazyGrid MultipleComponents TextLayout CanvasDrawing \
--repeat 3 \
--output-dir "$RUNNER_TEMP/benchmark-results"

# GitHub packages the whole session, including index.html, as one ZIP artifact.
- name: Upload benchmark results
if: always()
uses: actions/upload-artifact@v4
with:
name: jvm-benchmarks-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/benchmark-results/
if-no-files-found: warn
retention-days: 30

web:
name: Web benchmarks (${{ matrix.target }})
runs-on: macos-26-xlarge
timeout-minutes: 360
strategy:
fail-fast: false
matrix:
target: [js, wasmJs]

defaults:
run:
shell: bash
working-directory: compose/mpp/benchmarks

steps:
- name: Checkout Repository
uses: actions/checkout@v5

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Check Chrome
run: '"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version'

- name: Resolve latest benchmark versions
uses: ./.github/actions/resolve-benchmark-versions

- name: Build launcher
run: ./gradlew :benchmarks-launcher:installDist --console=plain

- name: Run benchmarks in Chrome
env:
BENCHMARK_TARGET: ${{ matrix.target }}
# Use the runner's GPU through ANGLE's Metal backend instead of SwiftShader.
BENCHMARK_CHROME_ARGUMENTS: '["--use-gl=angle", "--use-angle=metal"]'
run: |
./benchmarks-launcher/build/install/benchmarks-launcher/bin/benchmarks-launcher \
--target "$BENCHMARK_TARGET" \
--compose-version "$COMPOSE_VERSIONS" \
--kotlin-version "$KOTLIN_VERSIONS" \
--compose-core-path "$GITHUB_WORKSPACE" \
--browser "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--tests AnimatedVisibility LazyGrid MultipleComponents TextLayout CanvasDrawing \
--repeat 3 \
--output-dir "$RUNNER_TEMP/benchmark-results"

# GitHub packages the whole session, including index.html, as one ZIP artifact.
- name: Upload benchmark results
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}-benchmarks-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/benchmark-results/
if-no-files-found: warn
retention-days: 30

ios:
name: iOS benchmarks
runs-on: macos-26-xlarge
timeout-minutes: 360

defaults:
run:
shell: bash
working-directory: compose/mpp/benchmarks

steps:
- name: Checkout Repository
uses: actions/checkout@v5

- name: Setup iOS benchmark environment
id: ios
uses: ./.github/actions/setup-ios-instrumented-test-environment
with:
xcode-path: /Applications/Xcode_26.5.app
xcode-cache-key-suffix: '26.5'
simulator-device: iPhone 17
simulator-os: '26.5'

- name: Resolve latest benchmark versions
uses: ./.github/actions/resolve-benchmark-versions

- name: Build launcher
run: ./gradlew :benchmarks-launcher:installDist --console=plain

- name: Run iOS benchmarks
env:
SIMULATOR_ID: ${{ steps.ios.outputs.simulator-id }}
run: |
./benchmarks-launcher/build/install/benchmarks-launcher/bin/benchmarks-launcher \
--target ios \
--simulator "$SIMULATOR_ID" \
--compose-version "$COMPOSE_VERSIONS" \
--kotlin-version "$KOTLIN_VERSIONS" \
--compose-core-path "$GITHUB_WORKSPACE" \
--tests AnimatedVisibility LazyGrid MultipleComponents TextLayout CanvasDrawing \
--repeat 3 \
--output-dir "$RUNNER_TEMP/benchmark-results"

# GitHub packages the whole session, including index.html, as one ZIP artifact.
- name: Upload benchmark results
if: always()
uses: actions/upload-artifact@v4
with:
name: ios-benchmarks-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/benchmark-results/
if-no-files-found: warn
retention-days: 30
2 changes: 2 additions & 0 deletions compose/mpp/benchmarks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/launcher/
yarn.lock
Loading
Loading