CARVIEW |
Select Language
HTTP/2 200
date: Sun, 27 Jul 2025 12:23:53 GMT
content-type: text/html; charset=utf-8
vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, X-Requested-With,Accept-Encoding, Accept, X-Requested-With
x-repository-download: git clone https://github.com/dotnet/android.git
etag: W/"03ee6cc263c201fd3b1aefbbd929110c"
cache-control: max-age=0, private, must-revalidate
strict-transport-security: max-age=31536000; includeSubdomains; preload
x-frame-options: deny
x-content-type-options: nosniff
x-xss-protection: 0
referrer-policy: no-referrer-when-downgrade
content-security-policy: default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com release-assets.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com copilotprodattachments.blob.core.windows.net/github-production-copilot-attachments/ github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/
server: github.com
content-encoding: gzip
accept-ranges: bytes
set-cookie: _gh_sess=PziMhAMAhj%2BIhZ%2Bfaq%2FEOnOoCLj2ybhNgyP3l7awEKI%2BSnpdk%2F463%2FyN0yJygEAMVfVijN2W19VM207SAvU755MCN%2FntU48E1gFbpU1PexzDZZ%2FCEtAt25n04iP7RMbHEK0%2Fhv%2BSvHayCv4fWiq2K6wk%2FtL8e%2BWW8HtohuD52ImPVX0NUuEJZtLX84C12AoUgrb7FuVb%2B6zoL8Wi9JStY06ijAGug%2Bws%2FsG2Co9wpOJwrd0WKLkZcdVFm6M6IxxA3SnDDJQCRFlaU3W3pbvFmg%3D%3D--6bLQuTvOexg%2B865Y--68AKeHYzr%2FYGG9sy5uzY9w%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax
set-cookie: _octo=GH1.1.1761874091.1753619032; Path=/; Domain=github.com; Expires=Mon, 27 Jul 2026 12:23:52 GMT; Secure; SameSite=Lax
set-cookie: logged_in=no; Path=/; Domain=github.com; Expires=Mon, 27 Jul 2026 12:23:52 GMT; HttpOnly; Secure; SameSite=Lax
x-github-request-id: A900:3467EF:E1B56B:1296709:68861A58
[monodroid] Embedded assemblies store (#6311) · dotnet/android@c927026 · GitHub
Dismiss banner
Copy file name to clipboard
Copy file name to clipboardExpand all lines: build-tools/automation/azure-pipelines.yaml
Copy file name to clipboardExpand all lines: build-tools/installers/create-installers.targets
Skip to content
Navigation Menu
{{ message }}
-
Notifications
You must be signed in to change notification settings - Fork 555
Commit c927026
authored
[monodroid] Embedded assemblies store (#6311)
What do we want? Faster (Release) App Startup!
How do we get that? Assembly Stores!
"In the beginning", assemblies were stored in the `assemblies`
directory within the `.apk`. App startup would open the `.apk`,
traverse all entries within the `.apk` looking for `assemblies/*.dll`,
`assemblies/*.dll.config`, and `assemblies/*.pdb` files. When a
"supported" `assemblies/*` entry was encountered, the entry would be
**mmap**(2)'d so that it could be used; see also commit c195683.
Of particular note is:
1. The need to enumerate *all* entries within the `.apk`, as there
is no guarantee of entry ordering, and
2. The need for *N* `mmap()` invocations, one per assembly included
in the app, *plus* additional `mmap()` invocations for the `.pdb`
and `.dll.config` files, if present.
Useful contextual note: a "modern" AndroidX-using app could pull
in dozens to over 200 assemblies without really trying.
There will be *lots* of `mmap()` invocations.
Instead of adding (compressed! d236af5) data for each assembly
separately, instead add a small set of "Assembly Store" files which
contain the assembly & related data to use within the app:
* `assemblies/assemblies.blob`
* `assemblies/assemblies.[ARCHITECTURE].blob`
`assemblies.[ARCHITECTURE].blob` contains architecture-specific
assemblies, e.g. `System.Private.CoreLib.dll` built for x86 would be
placed within `assemblies.x86.blob`. `ARCHITECTURE` is one of `x86`,
`x86_64`, `armeabi_v7a`, or `arm64_v8a`; note use of `_` instead of
`-`, which is different from the `lib/ARCHITECTURE` convention within
`.apk` files. This is done because this is apparently what Android
and `bundletool` do, e.g. creating `split_config.armeabi_v7a.apk`.
Once the architecture-neutral `assemblies.blob` and appropriate
(singular!) `assemblies.[ARCHITECTURE].blob` for the current
architecture is found and `mmap()`'d, `.apk` entry traversal can end.
There is no longer a need to parse the entire `.apk` during startup.
The reduction in the number of `mmap()` system calls required can
have a noticeable impact on process startup, particularly with
.NET SDK for Android & MAUI; see below for timing details.
The assembly store format uses the followings structures:
struct AssemblyStoreHeader {
uint32_t magic, version;
uint32_t local_entry_count; // Number of AssemblyStoreAssemblyDescriptor entries
uint32_t global_entry_count; // Number of AssemblyStoreAssemblyDescriptor entries in entire app, across all *.blob files
uint32_t store_id;
};
struct AssemblyStoreAssemblyDescriptor {
uint32_t data_offset, data_size; // Offset from beginning of file for .dll data
uint32_t debug_data_offset, debug_data_size; // Offset from beginning of file for .pdb data
uint32_t config_data_offset, config_data_size; // Offset from beginning of file for .dll.config data
};
struct AssemblyStoreHashEntry {
union {
uint64_t hash64; // 64-bit xxhash of assembly filename
uint32_t hash64; // 32-bit xxhash of assembly filename
};
uint32_t mapping_index, local_store_index, store_id;
};
The assembly store format is roughly as follows:
AssemblyStoreHeader header {…};
AssemblyStoreAssemblyDescriptor assemblies [header.local_entry_count];
// The following two entries exist only when header.store_id == 0
AssemblyStoreHashEntry hashes32[header.global_entry_count];
AssemblyStoreHashEntry hashes64[header.global_entry_count];
uint8_t data[];
Note that `AssemblyStoreFileFormat::hashes32` and
`AssemblyStoreFileFormat::hashes64` are *sorted by their hash*.
Further note that assembly *filenames* are not present.
`EmbeddedAssemblies::blob_assemblies_open_from_bundles()` will hash
the filename, then binary search the appropriate `hashes*` array to
get the appropriate assembly information.
As the assembly store format doesn't include assembly names, `.apk`
and `.aab` files will also contain an `assemblies.manifest` file,
which contains the assembly names and other information in a human-
readable format; it is also used by `assembly-store-reader`:
Hash 32 Hash 64 Blob ID Blob idx Name
0xa2e0939b 0x4288cfb749e4c631 000 0000 Xamarin.AndroidX.Activity
…
0xad6f1e8a 0x6b0ff375198b9c17 001 0000 System.Private.CoreLib
Add a new `tools/assembly-store-reader` utility which can read the
new `assemblies*.blob` files:
% tools/scripts/read-assembly-store path/to/app.apk
Store set 'base_assemblies':
Is complete set? yes
Number of stores in the set: 5
Assemblies:
0:
Name: Xamarin.AndroidX.Activity
Store ID: 0 (shared)
Hashes: 32-bit == 0xa2e0939b; 64-bit == 0x4288cfb749e4c631
Assembly image: offset == 1084; size == 14493
Debug data: absent
Config file: absent
…
16:
Name: System.Private.CoreLib
Store ID: 1 (x86)
Hashes: 32-bit == 0xad6f1e8a; 64-bit == 0x6b0ff375198b9c17
Assembly image: offset == 44; size == 530029
Debug data: absent
Config file: absent
…
On a Pixel 3 XL (arm64-v8a) running Android 12 with MAUI
6.0.101-preview.10.1952, we observe:
~~ MAUI: Displayed Time ~~
| Before ms | After ms | Δ | Notes |
| ---------:| --------: | -----------: | ------------------------------------- |
| 1016.800 | 892.600 | -12.21% ✓ | defaults; profiled AOT; 32-bit build |
| 1016.100 | 894.700 | -11.95% ✓ | defaults; profiled AOT; 64-bit build |
| 1104.200 | 922.000 | -16.50% ✓ | defaults; full AOT+LLVM; 64-bit build |
| 1102.700 | 926.100 | -16.02% ✓ | defaults; full AOT; 32-bit build |
| 1108.400 | 932.600 | -15.86% ✓ | defaults; full AOT; 64-bit build |
| 1106.300 | 932.600 | -15.70% ✓ | defaults; full AOT+LLVM; 32-bit build |
| 1292.000 | 1271.800 | -1.56% ✓ | defaults; 64-bit build |
| 1307.000 | 1275.400 | -2.42% ✓ | defaults; 32-bit build |
Displayed time reduces by ~12% when Profiled AOT is used.
It is interesting to note that **Displayed time** is nearly identical
for the default (JIT) settings case. It's most probably caused by the
amount of JIT-ed code between `OnCreate()` and the time when the
application screen is presented, most likely the time is spent JIT-ing
MAUI rendering code.
~~ MAUI: Total native init time (before `OnCreate()`) ~~
| Before ms | After ms | Δ | Notes |
| --------: | --------: | -----------: | ------------------------------------- |
| 96.727 | 88.921 | -8.07% ✓ | defaults; 32-bit build |
| 97.236 | 89.693 | -7.76% ✓ | defaults; 64-bit build |
| 169.315 | 108.845 | -35.71% ✓ | defaults; profiled AOT; 32-bit build |
| 170.061 | 109.071 | -35.86% ✓ | defaults; profiled AOT; 64-bit build |
| 363.864 | 208.949 | -42.57% ✓ | defaults; full AOT; 64-bit build |
| 363.629 | 209.092 | -42.50% ✓ | defaults; full AOT; 32-bit build |
| 373.203 | 218.289 | -41.51% ✓ | defaults; full AOT+LLVM; 64-bit build |
| 372.783 | 219.003 | -41.25% ✓ | defaults; full AOT+LLVM; 32-bit build |
Note that "native init time" includes running `JNIEnv.Initialize()`,
which requires loading `Mono.Android.dll` + dependencies such as
`System.Private.CoreLib.dll`, which in turn means that the AOT DSOs
such as `libaot-System.Private.CoreLib.dll.so` must *also* be loaded.
The loading of the AOT DSOs is why JIT is fastest here (no AOT DSOs),
and why Profiled AOT is faster than Full AOT (smaller DSOs).
~~ Plain Xamarin.Android: Displayed Time ~~
| Before ms | After ms | Δ | Notes |
| --------: | --------: | -----------: | ------------------------------------- |
| 289.300 | 251.000 | -13.24% ✓ | defaults; full AOT+LLVM; 64-bit build |
| 286.300 | 252.900 | -11.67% ✓ | defaults; full AOT; 64-bit build |
| 285.700 | 255.300 | -10.64% ✓ | defaults; profiled AOT; 32-bit build |
| 282.900 | 255.800 | -9.58% ✓ | defaults; full AOT+LLVM; 32-bit build |
| 286.100 | 256.500 | -10.35% ✓ | defaults; full AOT; 32-bit build |
| 286.100 | 258.000 | -9.82% ✓ | defaults; profiled AOT; 64-bit build |
| 328.900 | 310.600 | -5.56% ✓ | defaults; 32-bit build |
| 319.300 | 313.000 | -1.97% ✓ | defaults; 64-bit build |
~~ Plain Xamarin.Android: Total native init time (before `OnCreate()`) ~~
| Before ms | After ms | Δ | Notes |
| --------: | --------: | -----------: | ------------------------------------- |
| 59.768 | 42.694 | -28.57% ✓ | defaults; profiled AOT; 64-bit build |
| 60.056 | 42.990 | -28.42% ✓ | defaults; profiled AOT; 32-bit build |
| 65.829 | 48.684 | -26.05% ✓ | defaults; full AOT; 64-bit build |
| 65.688 | 48.713 | -25.84% ✓ | defaults; full AOT; 32-bit build |
| 67.159 | 49.938 | -25.64% ✓ | defaults; full AOT+LLVM; 64-bit build |
| 67.514 | 50.465 | -25.25% ✓ | defaults; full AOT+LLVM; 32-bit build |
| 66.758 | 62.531 | -6.33% ✓ | defaults; 32-bit build |
| 67.252 | 62.829 | -6.58% ✓ | defaults; 64-bit build |1 parent e0f3683 commit c927026Copy full SHA for c927026
File tree
Expand file treeCollapse file tree
72 files changed
+3847
-533
lines changedFilter options
- Documentation
- project-docs
- build-tools
- automation
- installers
- src
- Xamarin.Android.Build.Tasks
- Tasks
- Tests
- Xamarin.Android.Build.Tests
- Tasks
- Utilities
- Xamarin.ProjectTools
- Common
- Resources/Base
- Utilities
- java-runtime
- java/mono/android
- monodroid/jni
- tests
- MSBuildDeviceIntegration
- Tests
- Xamarin.Forms-Performance-Integration/Droid
- apk-sizes-reference
- tools
- assembly-store-reader
- decompress-assemblies
- scripts
- tmt
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Expand file treeCollapse file tree
72 files changed
+3847
-533
lines changed+1Lines changed: 1 addition & 0 deletions
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
25 | 25 |
| |
26 | 26 |
| |
27 | 27 |
| |
| 28 | + | |
28 | 29 |
| |
29 | 30 |
| |
30 | 31 |
| |
|
+1Lines changed: 1 addition & 0 deletions
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
11 | 11 |
| |
12 | 12 |
| |
13 | 13 |
| |
| 14 | + | |
14 | 15 |
| |
15 | 16 |
| |
16 | 17 |
| |
|
Documentation/project-docs/AssemblyStores.md
Copy file name to clipboard+224Lines changed: 224 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 | + | |
| 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 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + |
+7Lines changed: 7 additions & 0 deletions
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
148 | 148 |
| |
149 | 149 |
| |
150 | 150 |
| |
| 151 | + | |
| 152 | + | |
151 | 153 |
| |
152 | 154 |
| |
153 | 155 |
| |
| |||
408 | 410 |
| |
409 | 411 |
| |
410 | 412 |
| |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
411 | 417 |
| |
412 | 418 |
| |
413 | 419 |
| |
| |||
474 | 480 |
| |
475 | 481 |
| |
476 | 482 |
| |
| 483 | + | |
477 | 484 |
| |
478 | 485 |
| |
479 | 486 |
| |
|
build-tools/automation/azure-pipelines.yaml
Copy file name to clipboardExpand all lines: build-tools/automation/azure-pipelines.yaml+3Lines changed: 3 additions & 0 deletions
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
397 | 397 |
| |
398 | 398 |
| |
399 | 399 |
| |
| 400 | + | |
| 401 | + | |
| 402 | + | |
400 | 403 |
| |
401 | 404 |
| |
402 | 405 |
| |
|
build-tools/installers/create-installers.targets
Copy file name to clipboardExpand all lines: build-tools/installers/create-installers.targets+1Lines changed: 1 addition & 0 deletions
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
290 | 290 |
| |
291 | 291 |
| |
292 | 292 |
| |
| 293 | + | |
293 | 294 |
| |
294 | 295 |
| |
295 | 296 |
| |
|
You can’t perform that action at this time.
0 commit comments