From 17685ff73b6fcb05afd8b1780276c9b3394c0b6a Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Tue, 7 Jul 2026 08:07:42 +0200 Subject: [PATCH] build(android): debug APKs ship release-profile rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cargo's debug profile is not "slower" for this library — it is unusable, and it invalidated every on-device performance test to date: RustCrypto's AES-GCM compiles to generic-array iterator closures with per-byte precondition checks instead of ARMv8 hardware AES. Profiled live on a phone (simpleperf, 62k samples): ~800 µs of user CPU per 1.4 KB packet — the receive pump pinned above a full core yet only draining ~1,400 pkt/s of a 1,775 pkt/s (20 Mbps) stream, 2.3 MB standing in the kernel socket buffer, the latency-bound flush firing every 2 s forever. With release rust in the same debug APK: pump at ~12 % of a core, socket queue zero, no flushes, 2800x1260@120 streaming clean. preDebugBuild now depends on cargoNdkRelease; `-PrustDebug` opts back into a debug-profile native build for sessions that actually step through Rust. Kotlin debuggability is unchanged. Co-Authored-By: Claude Fable 5 --- clients/android/kit/build.gradle.kts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/clients/android/kit/build.gradle.kts b/clients/android/kit/build.gradle.kts index 61d06187..4220df88 100644 --- a/clients/android/kit/build.gradle.kts +++ b/clients/android/kit/build.gradle.kts @@ -110,8 +110,18 @@ afterEvaluate { // screenshot unit tests render Compose on the JVM and never load libpunktfunk_android.so), so // CI/local screenshot runs don't need the Rust toolchain or NDK. The native build stays wired // for every normal APK/AAR build. + // + // DEBUG APKs SHIP RELEASE RUST. Cargo's debug profile is not "a bit slower" for this library — + // it is unusable: the AES-GCM data-plane decrypt runs through generic-array iterator closures + // with per-byte UB checks instead of ARMv8 hardware AES. Profiled live on a phone (simpleperf): + // ~800 µs of user CPU per 1.4 KB packet, the receive pump pinned over a full core yet unable to + // drain a 20 Mbps stream — every debug-APK on-device test was silently benchmarking unoptimized + // crypto, not the streaming pipeline. Kotlin debuggability is untouched (the APK is still a + // debug build); only the cargo profile changes. `-PrustDebug` restores a debug-profile native + // build for the rare session that actually steps through Rust. if (!project.hasProperty("skipRustBuild")) { - tasks.named("preDebugBuild").configure { dependsOn(cargoNdkDebug) } + val debugRust = if (project.hasProperty("rustDebug")) cargoNdkDebug else cargoNdkRelease + tasks.named("preDebugBuild").configure { dependsOn(debugRust) } tasks.named("preReleaseBuild").configure { dependsOn(cargoNdkRelease) } } }