Commit c440654
[Xamarin.Android.Build.Tasks] mostly R2R the main assembly (#12248)
crossgen2's `--partial` only precompiles methods named in the profile data passed via `--mibc`. There is no profile for a new app, so today the main app assembly is effectively not ReadyToRun compiled at all.
This adds a `GenerateMibcProfile` task that writes a `.mibc` naming the compilable methods of the main app assembly and adds it to `@(PublishReadyToRunPgoFiles)`, so crossgen2 compiles that assembly while everything else stays partial. Android version of dotnet/maui#34837.
The target runs `AfterTargets="ILLink" BeforeTargets="CreateReadyToRunImages"`, so the profile only contains methods that survived trimming, and only when `$(PublishReadyToRunCrossgen2ExtraArgs)` contains `--partial`.
Two things worth knowing:
- Methods in a generic context are left to the JIT. crossgen2 compiles those as shared code over `System.__Canon`, which a profile can only name via that implementation detail. So the assembly ends up mostly, not fully, R2R compiled.
- Output is deterministic, so an unchanged app produces a byte identical `.mibc` and does not invalidate incremental builds.
## Bring your own profile
If you record a real startup profile, for example with the `maui profile startup --format mibc` skill in [dotnet/maui-labs](https://github.com/dotnet/maui-labs), you have already said exactly what you want compiled, and the whole point of `--partial` is to compile only that. Naming every method of the app assembly on top of it would swamp it.
So this turns itself off when `@(PublishReadyToRunPgoFiles)` is already non-empty. Just adding your `.mibc` to that item group is enough, no extra property needed. MAUI's own framework profiles go into the private `@(_ReadyToRunPgoFiles)`, so they do not suppress this.
`$(_AndroidReadyToRunMainAssembly)` overrides the decision: `true` forces the profile on, `false` turns it off, blank (the default) means "on when crossgen2 is partial and the app brought no profile of its own".
## Results
Measured on the two MAUI templates, Android-only TFM, CoreCLR, Release, composite, `--partial;--map`, `-p:RuntimeIdentifier=android-arm64` so the APK carries a single ABI and the size numbers are not diluted by a second copy of everything. Each pair is the same project built twice, differing only by `$(_AndroidReadyToRunMainAssembly)`, so the profile is the only variable.
Startup is from a physical arm64 device: 25 iterations per run, and the whole thing run twice in opposite order (before/after, then after/before) so any ordering or thermal drift would show up as a gap between the two passes of the same APK. It does not.
| | `dotnet new maui` | `dotnet new maui -sc` |
| --- | --- | --- |
| methods in profile | 87 | 1,291 |
| profile size | 1,829 bytes | 14,867 bytes |
| skipped as generic | 0 | 23 |
| app methods in R2R image | 0 -> 87 | 0 -> 1,289 |
| APK size | 20,013,784 -> 20,079,320 | 23,032,560 -> 23,298,800 |
| APK delta | +65,536 (+0.33%) | +266,240 (+1.16%) |
| startup before | 826.6 +/- 3.6, 825.2 +/- 2.9 ms | 1685.1 +/- 3.8, 1688.3 +/- 4.0 ms |
| startup after | 805.7 +/- 2.7, 806.6 +/- 2.7 ms | 1609.0 +/- 2.9, 1613.7 +/- 3.7 ms |
| startup delta | -19.8 ms (-2.4%) | -75.4 ms (-4.5%) |
Both passes of each APK agree to within a couple of ms, and the before/after gap is 5-15x the combined standard error, so the deltas are well clear of the noise.
Essentially every method named in the profile lands in the R2R image, and the trade scales with how much code the app actually has: the blank template gains 87 methods for 64 KB, `-sc` gains 1,289 for 260 KB.
`apkdiff` shows all the growth is the composite R2R image; dex and every other entry are byte identical in both templates.
```
$ apkdiff full-arm64-before.apk full-arm64-after.apk # dotnet new maui
+ 66,488 lib/arm64-v8a/libassembly-store.so
Summary:
+ 0 Other entries 0.00% (of 3,930,902)
+ 0 Dalvik executables 0.00% (of 16,997,756)
+ 66,488 Shared libraries 0.39% (of 17,016,032)
+ 65,536 Package size difference 0.33% (of 20,013,784)
$ apkdiff sc-arm64-before.apk sc-arm64-after.apk # dotnet new maui -sc
+ 267,632 lib/arm64-v8a/libassembly-store.so
Summary:
+ 0 Other entries 0.00% (of 7,041,375)
+ 0 Dalvik executables 0.00% (of 17,026,184)
+ 267,632 Shared libraries 1.34% (of 20,013,592)
+ 266,240 Package size difference 1.16% (of 23,032,560)
```
## Build time cost
This never runs in a Debug build. `$(PublishReadyToRun)` only defaults to `true` for `$(Configuration)` of `Release`, and the target additionally requires crossgen2 to be in partial mode, so the inner-loop build is untouched. It is also CoreCLR only, since `Microsoft.Android.Sdk.CoreCLR.targets` is imported only for that runtime.
For Release builds, `GenerateMibcProfile` runs once per RID. Durations pulled from the binlogs:
| build | methods | 1st RID | 2nd RID |
| --- | --- | --- | --- |
| `dotnet new maui` | 87 | 33.6 ms | 1.0 ms |
| `dotnet new maui -sc` | 1,291 | 34.5 ms | 1.9 ms |
| `-sc`, `PublishTrimmed=false` | 1,291 | 40.4 ms | 1.9 ms |
So 35-45 ms per build, against roughly 50 s of wall clock and 128-142 s of total task time. Whichever RID runs first pays about 34 ms of task assembly load and JIT; the second pays 1-2 ms, and that marginal cost is the same whether the profile names 87 methods or 1,291. Writing the profile is effectively free, you are just paying to load the task once.
For scale, `RunReadyToRunCompiler` is 4.0-5.0 s in these same builds, so this is well under 1% of crossgen2's own time.
## Tests
`GenerateMibcProfileTests` for the writer, and `InstallAndRunTests.PublishReadyToRunPartial ([Values] bool isComposite)` builds, verifies the app assembly is R2R compiled in the APK, then installs and launches it. Both variants pass on an x86_64 emulator.
All four decision paths were verified end to end on a real MAUI app: partial with no profile generates it, a user supplied `@(PublishReadyToRunPgoFiles)` suppresses it, and each explicit value of `$(_AndroidReadyToRunMainAssembly)` overrides both. Both trimming paths were checked too: the default build profiles `obj/.../linked/App.dll`, and `-p:PublishTrimmed=false` profiles `obj/.../App.dll`.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>1 parent fb30bbf commit c440654
7 files changed
Lines changed: 1063 additions & 13 deletions
File tree
- .github/instructions
- src/Xamarin.Android.Build.Tasks
- Microsoft.Android.Sdk/targets
- Tasks
- Tests/Xamarin.Android.Build.Tests/Tasks
- Utilities
- tests/MSBuildDeviceIntegration/Tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
Lines changed: 67 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
| |||
49 | 51 | | |
50 | 52 | | |
51 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
52 | 119 | | |
Lines changed: 42 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
0 commit comments