Skip to content

[tests] Add Android interop benchmarks and reduce JNI overhead - #12644

Merged
jonathanpeppers merged 3 commits into
mainfrom
simonrozsival-android-benchmarks
Sep 3, 2026
Merged

[tests] Add Android interop benchmarks and reduce JNI overhead#12644
jonathanpeppers merged 3 commits into
mainfrom
simonrozsival-android-benchmarks

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

  • add focused BenchmarkDotNet coverage for trimmable typemap lookups, peer activation/cache behavior, JNI-to-Java-to-UCO roundtrips, Android APIs, object returns, and primitive/jagged arrays
  • decompose Uri.Parse() into Java execution, string creation, and managed peer activation
  • eliminate transient JNI array-signature allocations by using UTF-8 literals for built-in array classes
  • avoid redundant class-name discovery for known jagged-array element types and use IsInstanceOf() for Java-to-managed validation
  • resolve sealed framework peer proxies from the requested managed type instead of materializing the runtime Java class name
  • cache raw-JNI benchmark method metadata outside the measured operation

Samsung S23 results

BenchmarkBeforeAfterAllocation beforeAllocation after
byte[256] conversion956.9 ns515.2 ns336 B280 B
four-element byte[256][] conversion6.010 µs2.940 µs1,432 B1,176 B

The remaining allocations are exactly the required managed array representations: 280 B for byte[256], and 1,176 B for the outer array plus four inner arrays. The raw JNI URI controls now allocate 0 B.

For sealed framework peer activation, the existing runtime class discovery components cost 471.8 ns and 112 B (GetObjectClass plus GetJniTypeName). Validating the already-known target class directly costs 384.9 ns and 0 B, removing the class-name allocation and reducing this lookup stage by about 18%.

Validation

  • built and ran the targeted benchmark filters on a Samsung S23 (SM-S911B)
  • built the complete Android benchmark project in Release with CoreCLR and the trimmable typemap
  • built Mono.Android with the sealed-peer optimization
  • added compatible, incompatible, and sealed closed-generic peer activation coverage
  • existing enum, nullable primitive, byte, sbyte, and jagged-array coverage exercises the changed conversion paths

Add trimmable typemap, peer activation, JNI roundtrip, Android API, and array marshalling benchmarks. Avoid transient JNI array type-name allocations and redundant class lookups discovered by the new coverage.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI lite review requested due to automatic review settings September 2, 2026 15:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It modifies core JNIEnv interop array-class/type-compat logic in addition to adding benchmarks, making the blast radius broad enough to warrant final human review.

Review tier: Lite
Findings: 1 Medium severity

New issues introduced by this change (1)
SeverityFinding
Medium severitytests/​Android.Benchmarks/​AndroidArrayMarshallingBenchmarks.cs — 💡 suggestion Operational implications — This benchmark uses Android 12 / API 31 types (e.g.,…
What changed in this PR

This PR extends the tests/Android.Benchmarks project with targeted BenchmarkDotNet scenarios for common interop and framework usage patterns, and makes a small runtime-side optimization in JNIEnv to reduce transient allocations during array-class lookups (notably for built-in primitive/boxed array signatures).

Changes:

  • Add new BenchmarkDotNet benchmark types covering typemap lookups, peer activation/cache behavior, raw JNI roundtrips, object creation, and array marshalling.
  • Decompose URI parsing into separate “bound” vs “raw JNI” vs “activation from prepared JNI handles” benchmarks to isolate allocation sources.
  • Optimize JNIEnv array type compatibility checks and class lookup paths by using UTF-8 literals for built-in array class names and a fast-path for jagged array converter selection.
FileDescription
tests/​Android.Benchmarks/​UriParseAllocationBenchmarks.csAdds URI parse benchmarks isolating string creation, raw JNI call, and managed peer activation allocations.
tests/​Android.Benchmarks/​TypeMapLookupBenchmarks.csAdds benchmarks for typemap type ↔ signature resolution (framework/app/missing).
tests/​Android.Benchmarks/​PeerLookupBenchmarks.csAdds benchmarks for GetObject<T> peer cache hit/miss and global-ref scenarios.
tests/​Android.Benchmarks/​PeerActivationComponentBenchmarks.csAdds microbenchmarks for common activation components (class lookup/name/hash/global ref).
tests/​Android.Benchmarks/​ExportRoundtripBenchmarks.csAdds JNI→managed exported method roundtrip benchmarks (primitive + string).
tests/​Android.Benchmarks/​AndroidObjectCreationBenchmarks.csAdds benchmarks for common framework object creation APIs (Intent, Bitmap, etc.).
tests/​Android.Benchmarks/​AndroidArrayMarshallingBenchmarks.csAdds API 31+ array marshalling benchmarks (AppSearch, Parcel, Base64, digest, prepared JNI arrays).
tests/​Android.Benchmarks/​AndroidApiBenchmarks.csAdds benchmarks for frequently-used Android API calls (Bundle, resources, system services, view ops).
tests/​Android.Benchmarks/​Android.Benchmarks.csprojForces CoreCLR (UseMonoRuntime=false) and trimmable typemap for the benchmarks app.
src/​Mono.Android/​Android.Runtime/​JNIEnv.csReduces array-signature allocations via UTF-8 built-in array class names and adjusts array converter selection/compat checks.

Resolve sealed framework peer proxies from the requested managed type and validate them with IsInstanceOf, avoiding runtime Java class-name materialization. Add component benchmark and sealed/generic activation coverage.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival changed the title [tests] Add Android interop benchmarks[tests] Add Android interop benchmarks and reduce JNI overheadSep 2, 2026
@jonathanpeppers

Copy link
Copy Markdown
Member

/review

@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12644

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the runtime array-class/compatibility changes, the sealed framework-peer fast path, and the added tests and benchmarks. I found no additional actionable code issues beyond the existing API 31 benchmark-execution comment.

CI build #1579150 is still in progress and currently red: Package Tests macOS > Tests > APKs 1 has failed, with eight lanes still running. The Azure CLI could not initialize its configuration in this environment, so I could not verify the failure's root cause and am not attributing it to this change.

Generated by Android PR Reviewer for #12644 · gpt56 · 199.8 AIC · ⌖ 8.83 AIC · ⊞ 25.7K
Comment /review to run again

Pass the nested array's element type into GetArray so primitive array items are not mistaken for jagged object arrays.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Sep 3, 2026
@jonathanpeppers
jonathanpeppers merged commit f437900 into mainSep 3, 2026
44 checks passed
@jonathanpeppers
jonathanpeppers deleted the simonrozsival-android-benchmarks branch September 3, 2026 15:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers