Discuss disabling Dynamic PGO by default for CoreCLR Android apps while preserving developer opt-in
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 25/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- csharp
- Área
- mobile-dev
Línea de trabajo
No implementation file or test is identified; the issue is asking whether to change the Android CoreCLR default and where that policy belongs. First review the Android SDK targets and the common .NET SDK logic that emits System.Runtime.TieredPGO, then gather the additional workload and device measurements listed in the issue. Done means a policy decision supported by broader performance evidence, while preserving explicit TieredPGO settings.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Android framework version
net11.0-android (Preview)
Affected platform version
Measured with the installed .NET 11 RC2 stack:
- .NET SDK:
11.0.100-rc.2.26475.136 - CoreCLR runtime:
11.0.0-rc.2.26475.136 - Android workload:
37.2.0-rc.2.84 - Microsoft.Maui.Controls:
11.0.0-rc.2.26475.3 - Sample-content template:
Microsoft.Maui.Templates.net1111.0.0-rc.1.26451.6(the manually installed template selected bydotnet new) - Device: Samsung Galaxy A16 (
SM-A165F), Android 16, arm64
These measurements use the installed workload, not a locally built SDK from dotnet/android main.
Description
Should .NET for Android override the CoreCLR default and disable Dynamic PGO by default, while allowing developers to enable it explicitly with <TieredPGO>true</TieredPGO>?
Where the current default comes from
Dynamic PGO is inherited from .NET CoreCLR, rather than explicitly enabled by the Android SDK. Neither the checked-out Android product targets (revision 0205e8caae9daca2b587eef49592c8253485f751) nor the installed Android/MAUI SDK targets set TieredPGO or TieredCompilation. Both properties are empty when evaluating the sample without an override.
The common .NET SDK emits System.Runtime.TieredPGO into runtime configuration only when $(TieredPGO) is nonempty. With the entry omitted, CoreCLR chooses its own default. A separate on-device JIT diagnostic confirmed the exact shipped runtime produces an instrumented tier followed by code optimized using Dynamic PGO when the property is omitted. With TieredPGO=false, it produces uninstrumented Tier1 code with No PGO data.
Reference: Microsoft Learn: profile-guided optimization configuration. This discussion concerns CoreCLR Dynamic PGO, not Mono profiled AOT, NativeAOT, or static ReadyToRun/MIBC PGO.
Measured startup effect
A matched A/B experiment on the MAUI sample-content app found a small but consistent startup benefit from disabling Dynamic PGO:
| Mean startup metric | Dynamic PGO enabled | Dynamic PGO disabled | Improvement when disabled |
|---|---|---|---|
First display (am start -W TotalTime) |
1,582.97 ms | 1,548.83 ms | 34.13 ms / 2.16% |
Loaded dashboard drawn (ReportFullyDrawn) |
1,689.63 ms | 1,654.17 ms | 35.47 ms / 2.10% |
There were 30 measured cold-process launches per variant, in 10 matched installation pairs. Disabled was faster in all 10 pairs. The paired bootstrap 95% confidence interval for the dashboard improvement is 24.43–45.47 ms (20,000 resamples of installation pairs, seed 20261008; individual launches were not treated as independent resampling units).
Both variants used Release, trimming, CoreCLR, arm64, and the default MAUI partial ReadyToRun compilation. Tiered compilation remained at its enabled runtime default. APK verification showed byte-identical managed assembly stores, CoreCLR/runtime libraries, DEX, and resources; only the generated native runtime configuration and APK signatures differed. The runtime configurations differed only in System.Runtime.TieredPGO.
Discussion / possible policy
- Consider an Android-specific
TieredPGO=falsedefault for CoreCLR applications only when the developer has not specified the property. Preserve explicittrueandfalse; reuse the existing .NET property rather than introducing a new Android-specific switch. - Keep tiered compilation and static ReadyToRun PGO separate from this decision. Neither was disabled in the experiment.
- Decide whether the policy belongs in the Android SDK or a MAUI-specific layer, and whether it should be limited to particular build configurations.
- Before changing the default, measure sustained throughput, longer UI interactions, CPU/energy/memory, additional apps, and additional devices. Dynamic PGO may improve these workloads even though it slightly slowed this startup scenario.
The experiment supports investigating a startup-oriented default, not concluding that Dynamic PGO is universally detrimental. No product default has been changed.
Steps to Reproduce
-
Use the versions above and generate
dotnet new maui --sample-content -n PgoStartupSample --no-restore. Target Android only and use a dedicated package ID. PinMicrosoft.Extensions.Logging.Debugto11.0.0-rc.2.26475.136rather than leaving its template floating version. -
Add identical readiness instrumentation to both variants: after
MainPageModel.Appearingcompletes its initialInitData, sets_dataLoaded, and completes the subsequentRefresh, register a one-shotViewTreeObserver.IOnDrawListeneron the activity decor view. On the next draw, post a callback that removes the listener and callsActivity.ReportFullyDrawn(). This measures data-loaded dashboard draw rather than only splash/first-window display. The two startup refreshes are preserved. -
Publish and preserve the enabled APK, then publish and preserve the disabled APK:
dotnet publish PgoStartupSample.csproj -c Release -f net11.0-android -r android-arm64 -p:TieredPGO=true -p:AndroidPackageFormats=apk -bl:pgo-on.binlog dotnet publish PgoStartupSample.csproj -c Release -f net11.0-android -r android-arm64 -p:TieredPGO=false -p:AndroidPackageFormats=apk -bl:pgo-off.binlogBoth commands passed. Set
AndroidSdkDirectoryandJavaSdkDirectoryas appropriate for the host. Verify the generated runtime configuration values and compare uncompressed APK payloads; the measured managed/runtime payloads were identical. -
Seed the app database once, retaining identical preferences and database across replacement installs. Use
adb install -r --no-streamingto switch variants with the same package ID. After each install, runadb shell cmd package compile -m speed -f <package>to keep Java ART compilation mode fixed. -
Run 10 matched installation pairs, five enabled-first and five disabled-first, shuffled with seed
20261008. After each install, discard two launches and measure three. Before every launch, force-stop the package, confirm there is no live PID, wait one second, then runadb shell am start -W -n <component>. RequireLaunchState: COLD, successful launch, and aFully drawn/readiness report from the new process. Allow three seconds cooldown between launches. -
Capture first-display TotalTime and ActivityTaskManager
Fully drawnduration. All 60 measured launches passed, with unique processes and no crashes/timeouts. All 20 per-install thermal checks reported thermal status 0; battery temperature was 24.7–25.3 °C. The phone was USB-powered with a full battery and its existing animation scales were zero; no global device settings were changed. -
Analyze the paired block means. Dashboard
disabled - enableddifferences in milliseconds were:-35.67, -3.00, -47.67, -20.33, -39.67, -52.33, -37.33, -45.67, -13.67, -59.33
Scope/limitations: these are force-stopped process-cold launches with seeded app data and warm filesystem caches, not first-install database seeding or reboot/cache-cold measurements. Only one device and stack were measured. Separate JIT diagnostic builds and post-experiment visual checks were excluded from timing samples. No sustained-throughput, energy, or memory conclusions are available.
Did you find any workaround?
Developers can already disable Dynamic PGO using the existing .NET property:
<PropertyGroup>
<TieredPGO>false</TieredPGO>
</PropertyGroup>
If we adopt a disabled Android default, developers should retain the explicit opt-in:
<PropertyGroup>
<TieredPGO>true</TieredPGO>
</PropertyGroup>
Relevant log output
Separate diagnostics on the same shipped runtime, with no TieredPGO override:
Runtime=11.0.0; explicit_pgo=False; value=False
; Assembly listing for method PgoStartupSample.MainActivity:DynamicPgoProbe(int):int (Instrumented Tier1)
; Instrumented Tier1 code
; Assembly listing for method PgoStartupSample.MainActivity:DynamicPgoProbe(int):int (Tier1)
; Tier1 code
; optimized using Dynamic PGO
explicit_pgo=False means AppContext.TryGetSwitch found no explicit configuration entry; it does not mean the native runtime default is disabled. JIT output establishes the actual behavior.
With TieredPGO=false:
Runtime=11.0.0; explicit_pgo=True; value=False
; Assembly listing for method PgoStartupSample.MainActivity:DynamicPgoProbe(int):int (Tier1)
; Tier1 code
; No PGO data
Both diagnostic builds and the final default-versus-disabled JIT verification passed. The JIT output stream was flushed and only newly appended output from each process was inspected, avoiding stale disassembly from earlier launches.
- Lenguaje dominante
- C#
- Estrellas
- 2.1k
- Forks
- 579
- Merge medio
- 1 d 13 h
- PR fusionados (30 d)
- 238
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Sin guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de dotnet/android
-
Area: App+Library Build
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
EditText.getText() isn't boundAbiertoArea: Mono.Android
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
dotnet/android#9192 · 4 comentarios · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
agentic-workflows needs-triage
Dificultad 3/5 1-2 días Aptitud para principiantes 43/100
Los mantenedores suelen responder en 1 día
-
[NativeAOT] XAJVC0000 with Xamarin.GooglePlayServices.Ads.LitePosiblemente ocupada @simonrozsival la tomó hace 1 día. AbiertoArea: App+Library Build needs-triage
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
dotnet/android#13235 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
agentic-workflows needs-triage
Dificultad 3/5 1-2 días Aptitud para principiantes 38/100
dotnet/android#13207 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de dotnet/android
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Nimblesite/SharpLsp#468 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
elastic/ecs-dotnet#575 ·
-
V4 alert-rule reads have no scope gate, so any authenticated credential can list alert rulesAbiertoalerts bug effort:S P2
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
nightscout/nocturne#2511 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 2 días
-
Crash in v3.00Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
arkane-systems/mousejiggler#140 ·