Nucleus
Performance & native

Benchmarks

Measured cross-runtime benchmark results — one Geekbench-style suite run inside a real Compose desktop app across JVM JIT, GraalVM Native Image, SwiftUI, Tauri, and Flutter.

The benchmark-demo example runs one Geekbench-style suite — 13 CPU kernels, geometric render ramps, and a list-load bench — inside a real Compose desktop app, then re-implements the same kernels byte-for-byte in SwiftUI, Tauri, and Flutter. Every variant runs the identical suite on the same machine, so the numbers are directly comparable. This page presents the measured results.

Setup

  • Date: 2026-07-16
  • Machine: Apple M4, 10 cores, AC power, thermally stable
  • Toolchains: Oracle GraalVM 25.1.3+9.1 (Java 25.0.3), OpenJDK 26.0.1, Gradle daemon JBR 21
  • Protocol: each variant self-runs its suite (3 discarded warmups, then best-of-5 over the CPU kernels). Runs are strictly sequential, one app at a time; peak RSS is sampled during each run. Built-in self-checks pass everywhere.

Composite scores are only comparable within an identical suite. Every value below comes from the same suite and the same machine session. CPU and GFX: higher is better. list, peak RAM, and app size: lower is better.

Summary

Sorted by composite CPU score.

#VariantRuntimeCPUGFXlist (ms)Peak RAMApp size
1jvm-graalGraalVM 25.0.3 JIT + ProGuard146.71304121.0695 MB160 MB
2tauriRust + WebView (WKWebView)142.215013233.0794 MB †5.6 MB ‡
3jvm-c2-pgOpenJDK 26 C2 + ProGuard140.41304120.9616 MB119 MB
4swiftuiSwift/LLVM + SwiftUI138.71525414.4268 MB0.34 MB ‡
5jvm-c2OpenJDK 26 C2 (no ProGuard)138.51304121.6620 MB136 MB
6aot-o3-pgoGraalVM Native -O3 + PGO132.71385917.1269 MB129 MB
7aot-o3GraalVM Native -O3 (ML-inferred)100.81385917.1297 MB174 MB
8aot-o2GraalVM Native -O286.91123917.2278 MB150 MB
9aot-osGraalVM Native size-opt (-Os)80.11043317.3326 MB129 MB
10flutterDart AOT + Impeller57.31513317.2196 MB37 MB

Key takeaways

  • Raw CPU. GraalVM JIT leads (146.7). The LLVM baselines (Tauri 142, SwiftUI 139) and OpenJDK C2 (138–140) are bunched together. GraalVM Native only catches the JVM/LLVM pack with PGO (100.8 → 132.7); without a profile, matmul collapses (3877 vs 12225 MFLOP/s). -O2 and -Os trail further behind.
  • The PGO sweet spot. aot-o3-pgo reaches near-JVM CPU (132.7) with 2.5× lower RAM (269 MB vs 616–695 MB), a compact .app (129 MB), and fast startup. The cost is the PGO flow: instrumented build → GUI run → rebuild. PGO also shrinks the image (174 MB → 129 MB).
  • RAM. Flutter is the leanest (196 MB), ahead of SwiftUI (268) and GraalVM Native (269–326). The JVMs sit at 616–695 MB. Tauri is the heaviest (794 MB) once the WebKit process tree is counted (†).
  • Startup / list. Native ~17 ms, JVM ~21 ms, SwiftUI 14 ms (best), Tauri 233 ms (WebView DOM/canvas list rendering — an order of magnitude slower).
  • Lowest CPU is Flutter (57.3): raytracer at a quarter of the pack, weak matmul/sha/sieve — but it compensates with top-tier rendering (GFX 15133).

Tauri RAM — the multi-process WebView pitfall (†)

Tauri/WKWebView is multi-process on macOS. The canvas and JS run in separate WebKit processes (com.apple.WebKit.WebContent, .GPU, .Networking), not in the app binary. The main-process RSS alone is 177 MB, but the real footprint — summing the WebKit process tree — is 794 MB, making Tauri the heaviest variant in RAM, not the lightest. (Summing RSS slightly overcounts shared WebKit.framework pages, so 794 MB is an upper bound; the 177→794 gap is large and real.)

App size — self-contained vs system runtime (‡)

The self-contained variants ship their own runtime, so their weight is the full .app (runtime + Skiko/dylibs + jars/binary): JVM (jlink), GraalVM Native, Flutter. SwiftUI and Tauri rely on the OS runtime — SwiftUI on the system SwiftUI/AppKit, Tauri on the system WebKit — so their binaries (0.34 / 5.6 MB) exclude the engine macOS provides. The two size classes are not directly comparable.

Per-kernel CPU

Throughput, higher is better.

Kerneljvm-graaltaurijvm-c2-pgswiftuijvm-c2aot-o3-pgoaot-o3aot-o2aot-osflutter
mandelbrot4.094.134.333.923.991.983.321.973.273.61
nbody700.6693.2639.6659.4691.3694.7683.0654.8678.3608.7
raytracer17.219.818.919.819.017.515.814.07.04.2
matmul1421716224166901621516724122253877324431053054
blur93.444.559.780.749.291.549.546.636.530.0
sha256332.9442.1361.2360.3361.7326.9296.8294.9268.2192.4
sieve708.5642.3565.9603.6599.9725.9467.2463.2484.4268.6
fft407.8452.7441.5436.9441.1363.9369.6376.3356.8366.9
pi0.090.110.110.110.100.100.090.090.090.08
mandelbrot_mt15.516.515.616.116.114.514.17.512.210.6
raytracer_mt74.581.268.675.067.271.460.249.925.513.7
matmul_mt6208869442640383301363925601131998817255159569731
blur_mt345.0188.4244.1261.7238.3285.9203.5194.4155.140.0

The Graal compiler signature (JIT and AOT+PGO alike) is strong blur/sieve, dull fft/sha. The known PGO regression on mandelbrot reproduces (1.98 with the profile vs 3.32 without).

Render ramps

Maximum sustained load at ≥ 55 fps, higher is better.

VariantParticlesStarsTexts
swiftui125000181753906
aot-o3 / o3-pgo75000145406102
jvm (×3)50000181756102
flutter50000181759533
aot-o250000116326102
aot-os5000093066102
tauri250001817518618

Reproduce

terminal
cd examples/benchmark-demo

# JVM + native matrix (homogeneous RAM):
JAVA_HOME=<jbr-21> GRAALVM_HOME=<graalvm-25> JDK_C2_HOME=<jdk-26> \
  ONLY="jvm-c2 jvm-c2-pg jvm-graal aot-os aot-o2 aot-o3 aot-o3-pgo" ./run-all.sh

# ports:
ONLY="swiftui tauri flutter" ./run-all.sh

Native builds must run with JAVA_HOME=<graalvm> (the current JVM must be GraalVM). Otherwise a stale Gradle-provisioned Oracle JDK 25 without native-image can match the vendor=ORACLE, version=25 spec and get selected, failing the compile. Each variant writes a result JSON; the full per-variant appendix lives in the example's RESULTS.md.

What's next