Skip to content

Pace the low-memory allocation throttle (issue #5482) - #5493

Merged
shai-almog merged 2 commits into
masterfrom
fix/5482-low-memory-throttle
Jul 30, 2026
Merged

Pace the low-memory allocation throttle (issue #5482)#5493
shai-almog merged 2 commits into
masterfrom
fix/5482-low-memory-throttle

Conversation

@shai-almog

@shai-almog shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

The problem

After the OS reports memory pressure the VM raises lowMemoryMode and slows allocators so the collector can catch up. The throttle parked every legacy-path allocation for a fixed millisecond:

if(lowMemoryMode && !threadStateData->nativeAllocationMode) {
    CN1_GC_PARK_CAPTURE(threadStateData);
    threadStateData->threadActive = JAVA_FALSE;
    usleep((JAVA_INT)(1000));          // every allocation, unconditionally
    while(threadStateData->threadBlockedByGC) { usleep((JAVA_INT)(1000)); }
    threadStateData->threadActive = JAVA_TRUE;
}

That is not backpressure, it is a hard ceiling of about 1000 allocations/second per thread — three orders of magnitude under the un-throttled rate. A loader that allocates a few hundred thousand buffers goes from seconds to hours. There is no crash and no log line; the app simply stops making progress.

lowMemoryMode is raised in didReceiveMemoryWarning (CodenameOne_GLViewController.m:4000) and cleared only when a collection cycle completes (nativeMethods.m:1807), so under sustained pressure — which is what an allocation-heavy load on a memory-constrained device produces — it is effectively pinned on.

Why it looked impossible to reproduce. Nothing but iOS raises lowMemoryMode. The simulator on a large-RAM host essentially never delivers a memory warning, and Android has no equivalent path. The same code reads as "works on the simulator and Android, hangs on the device", which is exactly how #5482 was reported. Everything we ran against the reporter's own reproducer — Release build, real data, low-free-memory (CN1_FAKE_FREE_MB) and forced-signal-GC variants — passed in about 4 seconds, because none of them could reach this state.

Reproduction

The reporter's reproducer, iPhone 17 Pro simulator, Release build, with memory-warning delivery emulated:

condition result
no warnings completes, 4s
warning every 200ms completes, 42s
warning every 20ms never completes — 300s in, 133k/250k words, degrading 1.1s -> 3.8s per 1000 words
warning every 20ms, with this PR completes, 4s

Peak RSS 410MB -> 450MB: the working set the per-allocation park was buying back.

The fix

Parking is capped at one per thread per CN1_LOW_MEMORY_PARK_INTERVAL_MS (10ms), so the worst case a thread can lose is that duty cycle no matter how fast it allocates. Waiting out a collector that has actually stopped the world is a safepoint rather than a throttle, so threadBlockedByGC is honored every time, unchanged.

The pacing stamp is per thread (ThreadLocalData.lowMemoryParkStampMs) and monotonic, and is initialized explicitly in getThreadLocalDataThreadLocalData is malloc'd, not zeroed.

Tests

LowMemoryThrottleIntegrationTest translates an allocation-dense load, builds it through the clean target and runs it twice: once with no pressure (asserting the throttle never engages) and once under sustained warnings. It asserts the pacing invariant — parks <= elapsed / interval + slack — rather than wall-clock time, so the budget scales with the runner instead of assuming a machine speed. RESULT= is compared against the same program on the host JVM so the throttle cannot be "fixed" by dropping work.

Verified to fail on the pre-fix VM:

Low-memory throttle parked 15756 times over 19851ms (budget 4070, one park per 10ms plus slack)
across 15756 throttled allocations.

and to pass on this branch: throttledAllocations=12312 parks=1 elapsedMs=4.

Two supporting hooks, both no-ops unless their environment variable is set:

  • CN1_SIMULATE_MEMORY_WARNING_MS=<ms> raises lowMemoryMode at a cadence, standing in for sustained didReceiveMemoryWarning delivery. Nothing off iOS can otherwise reach this state, so without it the path is untestable on CI.
  • CN1_LOG_LOWMEM_PARKS reports [LOWMEM] parks=P throttledAllocations=T at exit, and gates the counters so a shipping build pays nothing for them (they sit on the legacy allocation path, which is hot during exactly the pressure this responds to).

Verification

  • vm/tests: 404 tests, 0 failures.
  • Gauntlet: all tortures byte-identical to the host JVM; GcStress + MtStress green in cooperative, forced-signal and sustained-memory-warning modes (the last one also exercises the new hook against the collector).
  • Pre-existing, not from this PR: TaggedSync does not link on macOS/arm64 on master either (java_lang_Thread_sleep___long undefined in the clean target), which aborts run-gauntlet.sh before its stress rounds. Confirmed identical on a pristine checkout; the stress rounds above were run directly.

CI: the Alpine/musl failure is not from this PR

build + run suite (musl, Alpine x64) fails with a gcc internal compiler error -- SSA corruption in coalesce_ssa_name, on the abnormal setjmp edges ParparVM's exception handling emits -- while compiling a generated file this PR does not touch:

com_codename1_surfaces_SurfaceDiagnostics.c:574:14: internal compiler error: SSA corruption
  in function 'com_codename1_surfaces_SurfaceDiagnostics_isEdt___R_boolean'
0xd294de coalesce_ssa_name(_var_map*)
0xcca38e rewrite_out_of_ssa(ssaexpand*)

The identical ICE -- same file, same line, same function -- reproduces on the unrelated first-class-health branch (run 30506992421). It is a master-wide break: this workflow last ran green on master at f9810b020, which is before ad2c1952cc, the base of both branches. Needs its own fix; it is not a signal on this change.

Note on #5482

This is a separate defect from the bounds-check fix in #5485, and explains why that PR changed nothing for the reporter: his symptom is a stall, not a bad read.

🤖 Generated with Claude Code

After the OS reports memory pressure the VM raises lowMemoryMode and slows
allocators so the collector can catch up. The throttle parked EVERY legacy-path
allocation for one millisecond, which is not backpressure but a hard ceiling of
about 1000 allocations/second per thread. An allocation-heavy loader dropped
from seconds to hours: no crash, no log line, just an app that stops making
progress.

It could only ever bite iOS. Nothing else raises lowMemoryMode -- the simulator
on a large-RAM host essentially never delivers a memory warning and Android has
no equivalent path -- so the same code read as "works everywhere but the
device", which is exactly how it was reported.

Parking is now capped at one per thread per CN1_LOW_MEMORY_PARK_INTERVAL_MS
(10ms), bounding the cost at that duty cycle however fast a thread allocates.
Waiting out a collector that has actually stopped the world is a safepoint
rather than a throttle, so threadBlockedByGC is still honored every time.

Measured on the reporter's workload, iPhone 17 Pro simulator, Release build,
with memory warnings emulated at 20ms: never completes (300s in, 53% done and
degrading 1.1s -> 3.8s per 1000 words) before, 4s after -- the same 4s the run
takes with no memory pressure at all. Peak RSS 410MB -> 450MB, the working set
the throttle was buying back.

Tests: LowMemoryThrottleIntegrationTest translates and runs an allocation-dense
load twice, with and without simulated warnings, and asserts the pacing
invariant (parks <= elapsed/interval + slack) rather than wall time, so the
budget scales with the runner. Verified to FAIL on the pre-fix VM: 15756 parks
over 19851ms against a budget of 4070. Memory pressure comes from a new
CN1_SIMULATE_MEMORY_WARNING_MS test hook, since nothing off iOS can otherwise
reach this state; CN1_LOG_LOWMEM_PARKS reports the counters and gates them so a
shipping build pays nothing.

vm/tests: 404 tests green. Gauntlet: all tortures byte-identical to the host
JVM, GcStress + MtStress green in cooperative, forced-signal and sustained
memory-warning modes. (TaggedSync does not link on macOS/arm64 on master
either -- java_lang_Thread_sleep___long is undefined in the clean target --
which aborts run-gauntlet.sh before the stress rounds; those were run directly.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 01:41

Copilot AI 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.

Pull request overview

This PR updates ParparVM’s low-memory allocation throttle to provide paced backpressure (instead of a fixed 1ms sleep on every legacy allocation), adds per-thread pacing state, and introduces an integration test plus runtime hooks to reproduce/assert the behavior off iOS.

Changes:

  • Pace low-memory throttling to at most one 1ms park per thread per CN1_LOW_MEMORY_PARK_INTERVAL_MS, while still honoring threadBlockedByGC waits as safepoints.
  • Add low-memory throttle diagnostics (CN1_LOG_LOWMEM_PARKS) and a CI test hook to simulate sustained memory warnings (CN1_SIMULATE_MEMORY_WARNING_MS).
  • Add a clean-target integration test that translates/builds/runs an allocation-dense workload and asserts a pacing invariant rather than wall-clock time.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vm/tests/src/test/resources/com/codename1/tools/translator/LowMemoryThrottleApp.java Allocation-dense workload used by the clean-target integration test to exercise legacy-path allocations.
vm/tests/src/test/java/com/codename1/tools/translator/LowMemoryThrottleIntegrationTest.java Translates/builds/runs the workload and asserts that low-memory parks are paced under sustained warnings.
vm/ByteCodeTranslator/src/nativeMethods.m Initializes the new per-thread pacing stamp (lowMemoryParkStampMs) since ThreadLocalData is malloc’d.
vm/ByteCodeTranslator/src/cn1_globals.m Implements paced throttling, adds monotonic time helper, adds diagnostics counters, and adds the memory-warning simulation hook.
vm/ByteCodeTranslator/src/cn1_globals.h Adds lowMemoryParkStampMs to ThreadLocalData and defines CN1_LOW_MEMORY_PARK_INTERVAL_MS.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 412 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 19406 ms

  • Hotspots (Top 20 sampled methods):

    • 14.09% com.codename1.tools.translator.Parser.addToConstantPool (232 samples)
    • 10.08% java.util.ArrayList.indexOf (166 samples)
    • 3.16% java.lang.StringBuilder.append (52 samples)
    • 3.10% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (51 samples)
    • 3.04% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (50 samples)
    • 2.98% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (49 samples)
    • 2.25% com.codename1.tools.translator.BytecodeMethod.optimize (37 samples)
    • 2.19% com.codename1.tools.translator.BytecodeMethod.equals (36 samples)
    • 2.06% org.objectweb.asm.tree.analysis.Analyzer.analyze (34 samples)
    • 1.94% java.lang.System.identityHashCode (32 samples)
    • 1.94% com.codename1.tools.translator.ByteCodeClass.fillVirtualMethodTable (32 samples)
    • 1.82% com.codename1.tools.translator.Parser.classIndex (30 samples)
    • 1.82% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (30 samples)
    • 1.58% org.objectweb.asm.ClassReader.readCode (26 samples)
    • 1.52% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (25 samples)
    • 1.40% com.codename1.tools.translator.bytecodes.Invoke.findMethodUp (23 samples)
    • 1.28% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (21 samples)
    • 1.21% java.lang.Object.hashCode (20 samples)
    • 1.21% sun.nio.fs.UnixNativeDispatcher.open0 (20 samples)
    • 1.09% java.util.HashMap.hash (18 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog shai-almog linked an issue Jul 30, 2026 that may be closed by this pull request
3 tasks
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [HTML preview] [Download]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 1 findings (Normal: 1)
      • Top findings
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 79ms / native 5ms = 15.8x speedup
SIMD float-mul (64K x300) java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 197.000 ms
Base64 CN1 decode 129.000 ms
Base64 SIMD encode 99.000 ms
Base64 encode ratio (SIMD/CN1) 0.503x (49.7% faster)
Base64 SIMD decode 98.000 ms
Base64 decode ratio (SIMD/CN1) 0.760x (24.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 23.000 ms
Image createMask (SIMD on) 18.000 ms
Image createMask ratio (SIMD on/off) 0.783x (21.7% faster)
Image applyMask (SIMD off) 46.000 ms
Image applyMask (SIMD on) 41.000 ms
Image applyMask ratio (SIMD on/off) 0.891x (10.9% faster)
Image modifyAlpha (SIMD off) 168.000 ms
Image modifyAlpha (SIMD on) 39.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.232x (76.8% faster)
Image modifyAlpha removeColor (SIMD off) 42.000 ms
Image modifyAlpha removeColor (SIMD on) 31.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.738x (26.2% faster)

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 4ms = 17.5x speedup
SIMD float-mul (64K x300) java 62ms / native 5ms = 12.4x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 183.000 ms
Base64 CN1 decode 117.000 ms
Base64 SIMD encode 93.000 ms
Base64 encode ratio (SIMD/CN1) 0.508x (49.2% faster)
Base64 SIMD decode 91.000 ms
Base64 decode ratio (SIMD/CN1) 0.778x (22.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 37.000 ms
Image createMask (SIMD on) 25.000 ms
Image createMask ratio (SIMD on/off) 0.676x (32.4% faster)
Image applyMask (SIMD off) 49.000 ms
Image applyMask (SIMD on) 43.000 ms
Image applyMask ratio (SIMD on/off) 0.878x (12.2% faster)
Image modifyAlpha (SIMD off) 48.000 ms
Image modifyAlpha (SIMD on) 41.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.854x (14.6% faster)
Image modifyAlpha removeColor (SIMD off) 61.000 ms
Image modifyAlpha removeColor (SIMD on) 52.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.852x (14.8% faster)

Copilot AI review requested due to automatic review settings July 30, 2026 02:11

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

vm/ByteCodeTranslator/src/cn1_globals.m:294

  • lowMemoryMode is now an _Atomic JAVA_BOOLEAN, but this test-hook loop writes it using plain assignment, which defaults to a seq_cst atomic store. Since the allocator side explicitly uses memory_order_relaxed, it’s better to make this store relaxed as well to avoid an unnecessary global ordering barrier (especially when simulating warnings at 1ms cadence).
        lowMemoryMode = JAVA_TRUE;

…struct field

Review follow-ups.

lowMemoryMode is now _Atomic. It was a plain JAVA_BOOLEAN written from the UI
thread in didReceiveMemoryWarning, cleared by the collector and read by every
allocating thread -- a formal data race that predates this PR and that the
CN1_SIMULATE_MEMORY_WARNING_MS hook would have widened. The allocation-path read
is an explicit relaxed load: the flag carries no ordering relationship, seeing a
raise one allocation late costs nothing, and seq_cst would put an acquire
barrier on the legacy allocation path.

cn1MonotonicMillis on Windows now uses cn1_win_compat's cn1_monotonic_micros
(QueryPerformanceCounter) instead of gettimeofday. gettimeofday is the wall
clock, so an NTP step would either suppress the throttle for the length of the
jump or park on every allocation until the clock caught up -- exactly the
invariant the pacing depends on.

The park stamp moves from a ThreadLocalData field to a __thread long, matching
the other per-thread GC state in this file: zero-initialized per thread, so the
malloc'd-not-zeroed init in getThreadLocalData is no longer needed, and the
header contributes only a #define rather than changing a struct layout that
every generated translation unit compiles against.

vm/tests: 407 green. GcStress + MtStress green in cooperative, forced-signal and
sustained memory-warning modes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog force-pushed the fix/5482-low-memory-throttle branch from fa54239 to 77d6c28 Compare July 30, 2026 02:28
@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 56ms / native 3ms = 18.6x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 247.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 64.000 ms
Base64 encode ratio (SIMD/CN1) 0.259x (74.1% faster)
Base64 SIMD decode 62.000 ms
Base64 decode ratio (SIMD/CN1) 0.484x (51.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 7.000 ms
Image createMask ratio (SIMD on/off) 0.583x (41.7% faster)
Image applyMask (SIMD off) 24.000 ms
Image applyMask (SIMD on) 18.000 ms
Image applyMask ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha (SIMD off) 16.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha removeColor (SIMD off) 19.000 ms
Image modifyAlpha removeColor (SIMD on) 11.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.579x (42.1% faster)

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog
shai-almog merged commit 9c7affa into master Jul 30, 2026
50 of 51 checks passed
@shai-almog
shai-almog deleted the fix/5482-low-memory-throttle branch July 30, 2026 05:16
@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 305 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 82ms / native 3ms = 27.3x speedup
SIMD float-mul (64K x300) java 85ms / native 5ms = 17.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 213.000 ms
Base64 CN1 decode 153.000 ms
Base64 native encode 824.000 ms
Base64 encode ratio (CN1/native) 0.258x (74.2% faster)
Base64 native decode 534.000 ms
Base64 decode ratio (CN1/native) 0.287x (71.3% faster)
Base64 SIMD encode 73.000 ms
Base64 encode ratio (SIMD/CN1) 0.343x (65.7% faster)
Base64 SIMD decode 58.000 ms
Base64 decode ratio (SIMD/CN1) 0.379x (62.1% faster)
Base64 encode ratio (SIMD/native) 0.089x (91.1% faster)
Base64 decode ratio (SIMD/native) 0.109x (89.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 78.000 ms
Image applyMask (SIMD on) 61.000 ms
Image applyMask ratio (SIMD on/off) 0.782x (21.8% faster)
Image modifyAlpha (SIMD off) 69.000 ms
Image modifyAlpha (SIMD on) 49.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.710x (29.0% faster)
Image modifyAlpha removeColor (SIMD off) 66.000 ms
Image modifyAlpha removeColor (SIMD on) 69.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.045x (4.5% slower)

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 363 seconds

Build and Run Timing

Metric Duration
Simulator Boot 61000 ms
Simulator Boot (Run) 1000 ms
App Install 11000 ms
App Launch 3000 ms
Test Execution 389000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 263.000 ms
Base64 CN1 decode 95.000 ms
Base64 native encode 711.000 ms
Base64 encode ratio (CN1/native) 0.370x (63.0% faster)
Base64 native decode 558.000 ms
Base64 decode ratio (CN1/native) 0.170x (83.0% faster)
Base64 SIMD encode 49.000 ms
Base64 encode ratio (SIMD/CN1) 0.186x (81.4% faster)
Base64 SIMD decode 45.000 ms
Base64 decode ratio (SIMD/CN1) 0.474x (52.6% faster)
Base64 encode ratio (SIMD/native) 0.069x (93.1% faster)
Base64 decode ratio (SIMD/native) 0.081x (91.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.286x (71.4% faster)
Image applyMask (SIMD off) 50.000 ms
Image applyMask (SIMD on) 35.000 ms
Image applyMask ratio (SIMD on/off) 0.700x (30.0% faster)
Image modifyAlpha (SIMD off) 68.000 ms
Image modifyAlpha (SIMD on) 85.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.250x (25.0% slower)
Image modifyAlpha removeColor (SIMD off) 132.000 ms
Image modifyAlpha removeColor (SIMD on) 165.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.250x (25.0% slower)

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 421 seconds

Build and Run Timing

Metric Duration
Simulator Boot 99000 ms
Simulator Boot (Run) 1000 ms
App Install 18000 ms
App Launch 5000 ms
Test Execution 519000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 140ms / native 3ms = 46.6x speedup
SIMD float-mul (64K x300) java 74ms / native 2ms = 37.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 277.000 ms
Base64 CN1 decode 138.000 ms
Base64 native encode 946.000 ms
Base64 encode ratio (CN1/native) 0.293x (70.7% faster)
Base64 native decode 292.000 ms
Base64 decode ratio (CN1/native) 0.473x (52.7% faster)
Base64 SIMD encode 110.000 ms
Base64 encode ratio (SIMD/CN1) 0.397x (60.3% faster)
Base64 SIMD decode 66.000 ms
Base64 decode ratio (SIMD/CN1) 0.478x (52.2% faster)
Base64 encode ratio (SIMD/native) 0.116x (88.4% faster)
Base64 decode ratio (SIMD/native) 0.226x (77.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 68.000 ms
Image createMask ratio (SIMD on/off) 5.667x (466.7% slower)
Image applyMask (SIMD off) 661.000 ms
Image applyMask (SIMD on) 273.000 ms
Image applyMask ratio (SIMD on/off) 0.413x (58.7% faster)
Image modifyAlpha (SIMD off) 324.000 ms
Image modifyAlpha (SIMD on) 282.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.870x (13.0% faster)
Image modifyAlpha removeColor (SIMD off) 237.000 ms
Image modifyAlpha removeColor (SIMD on) 208.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.878x (12.2% faster)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] IOS crash with "runtime exception"

3 participants