Skip to content

Stop the O(1) page reclaim from freeing slots the per-slot walk keeps - #5474

Merged
shai-almog merged 3 commits into
masterfrom
gc-aging-asymmetry
Jul 26, 2026
Merged

Stop the O(1) page reclaim from freeing slots the per-slot walk keeps#5474
shai-almog merged 3 commits into
masterfrom
gc-aging-asymmetry

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Summary

Closes the finding #5471 left open: kept objects referencing reclaimed memory on the issue-5425 workload. Chasing it gave a different answer than the one that PR guessed at, so the numbers below matter more than the hypothesis did.

What was actually wrong

The O(1) whole-page reclaim tests gcLastMarkedEpoch != V. The per-slot walk it claims to reproduce -- its comment says "Byte-identical outcome WITHOUT touching a single slot" -- frees on m < V-1. A slot marked at V-1 therefore survives the walk, while the shortcut drops the entire page holding it.

Measured on LargeArrayLoad, the final issue-5425 shape: 26,924 slots freed a full cycle early in one run. Every other driver reports zero, which fits -- it needs a large retained survivor set sharing pages with garbage, which is exactly what that workload builds.

That is the source of the dangling pairs. The legacy sweep ages on the same m < V-1 rule, so a matured Hashtable.Entry kept at V-1 was left pointing at a page-resident byte[] payload the shortcut had already reclaimed -- the reporter's dictionary shape precisely.

Fix: test gcLastMarkedEpoch < V - 1. Both bounds are then exact, and neither implies the other: gcLastMarkedEpoch covers slots marked since the last full walk, gcGraceEpoch covers slots the sweep itself promoted out of grace.

before after
early-freed slots (LargeArrayLoad) 26,924 0
early-freed slots (other 8 drivers) 0 0

Free, measured against master: LoadLoop 0.16s both, StormAB 0.59 vs 0.58s, LargeArrayLoad 5 collection cycles both, peak RSS 93.1 vs 92.6 MB.

On the resurrection hypothesis

#5471 speculated that the conservative native-stack scan resurrecting unreachable objects would turn those dangling pairs into corruption. Measured directly with a new pre-sweep audit -- record every object marked live again after ageing past the keep threshold, then check its fields before the sweep acts:

Across all nine drivers: 1 resurrection total, 0 holding a dangling reference.

The hazard is real but essentially never fires, for a structural reason: for the scan to revive an object a stale stack word must point at it, and such a word generally keeps marking it every cycle. That is retention, not resurrection -- the object never ages out to be revived. The audit stays in as a gate so this stops being an assumption.

Keeping it honest

  • earlyFreed, resurrected and resurrectedDangling are reported in the exit summary; run-gc-verify.sh fails on a nonzero earlyFreed or resurrectedDangling.
  • CN1_GC_FAULT=earlyfree restores the old bound, and a second self-test requires the gate to reject it (26,924 slots). Neither new check can go inert the way an unexercised assertion does.
  • CN1_GC_DEBUG_EARLY=1 dumps the offending pages' ageing bounds, since which bound went stale is the whole diagnosis.

Correction to #5471

That PR reported ~30k-60k dangling references per cycle from CN1_GC_VERIFY_AGING=1. That mode perturbs the collector: it roughly doubles the post-sweep walk, and the extra GC-thread time changes page ageing by orders of magnitude -- early-freed slots read 0 or 205,958 from the same binary depending only on that flag. Those figures overstated the problem. The README now says so, and every number in this PR comes from the default configuration.

Validation

check result
run-gauntlet.sh, both stop modes GREEN
run-gc-verify.sh, 9 drivers + both self-tests GREEN
GcHeapIntegrityIntegrationTest passes
perf A/B vs master (LoadLoop, StormAB, LargeArrayLoad) unchanged

🤖 Generated with Claude Code

Follow-up to #5471, which flagged kept objects referencing reclaimed memory
on the issue-5425 workload and left the cause open.

The O(1) whole-page reclaim tests gcLastMarkedEpoch != V, but the per-slot
walk it claims to reproduce ("byte-identical outcome") frees on m < V-1 -- so
a slot marked at V-1 SURVIVES the walk while the shortcut drops the entire
page holding it. Measured on LargeArrayLoad, the final issue-5425 shape:
26,924 slots freed a full cycle early in one run. The other drivers report
zero, which fits: it needs a large retained survivor set sharing pages with
garbage, which is exactly what that workload builds.

That is what produced the dangling pairs. The legacy sweep ages on the same
m < V-1 rule, so a matured Hashtable.Entry kept at V-1 was left pointing at a
page-resident byte[] payload the shortcut had already reclaimed -- the
reporter's dictionary shape precisely.

Test gcLastMarkedEpoch < V-1 instead. Both bounds are then exact and neither
implies the other: gcLastMarkedEpoch covers slots marked since the last full
walk, gcGraceEpoch covers slots the sweep itself promoted out of grace.
earlyFreed goes 26,924 -> 0 across the suite, and it is free: LoadLoop and
StormAB wall time and RSS unchanged, LargeArrayLoad still 5 cycles, peak RSS
93.1 vs 92.6 MB.

On the resurrection half of that report, the measurement says the hazard is
real but essentially never fires: across all nine drivers, 1 resurrection
total and 0 of them holding a dangling reference. For the conservative scan
to revive an object a stale stack word must point at it, and such a word
generally keeps marking it EVERY cycle -- retention, not resurrection. The
audit stays in as a gate so that stops being an assumption.

Three things keep this honest:
- resurrected / resurrectedDangling / earlyFreed are reported at exit, and
  run-gc-verify.sh fails on a nonzero earlyFreed or resurrectedDangling.
- CN1_GC_FAULT=earlyfree restores the old bound, and a second self-test
  requires the gate to reject it (26,924 slots), so the new check cannot go
  inert the way an unexercised assertion does.
- CN1_GC_VERIFY_AGING is documented as PERTURBING: it doubles the post-sweep
  walk and the extra GC-thread time moves page ageing by orders of magnitude
  (early-freed 0 vs 205,958 from one binary). The 30k-60k figures quoted in
  #5471 came from that mode and overstated the problem; every number here is
  from the default configuration.

Validation: gauntlet GREEN in both stop modes, run-gc-verify GREEN over nine
drivers with both self-tests firing, GcHeapIntegrityIntegrationTest green.

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

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

Fixes a ParparVM GC correctness issue where the O(1) whole-page reclaim could free BiBOP page slots that the per-slot sweep would have kept, which could leave surviving objects holding references into reclaimed memory. The PR also strengthens the GC-verify harness with counters/audits and fault-injection to keep the new invariant check exercised.

Changes:

  • Adjust BiBOP fast-sweep eligibility to match the per-slot keep/free rule (gcLastMarkedEpoch < V-1), and add an earlyfree fault-injection mode for A/B validation.
  • Add earlyFreed and resurrection auditing/counters to the GC-verify summary output to detect (and gate on) the “freed a cycle early” condition and resurrected-dangling hazards.
  • Update run-gc-verify.sh and vm/benchmarks/README.md to parse/report the new counters and enforce a second self-test for the early-free regression.

Reviewed changes

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

File Description
vm/ByteCodeTranslator/src/cn1_globals.m Fixes the O(1) page reclaim bound, adds early-free tracking, resurrection auditing, and summary reporting for GC verification.
vm/benchmarks/run-gc-verify.sh Gates driver runs on earlyFreed/resurrectedDangling, and adds a second self-test for the early-free fault injection.
vm/benchmarks/README.md Documents new diagnostics and clarifies that CN1_GC_VERIFY_AGING perturbs collector behavior/counters.
Comments suppressed due to low confidence (1)

vm/ByteCodeTranslator/src/cn1_globals.m:3078

  • This comment says the issue-5425 workload freed "232,882 slots" early, but the repo documentation and self-test mention 26,924 early-freed slots for LargeArrayLoad. Aligning the number (or avoiding a hard-coded count) will prevent confusion when comparing runs.
        // cycle earlier than the rule it claims to reproduce. Measured on the
        // issue-5425 workload: 232,882 slots freed early in one run.

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

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m
The bullet list above the O(1) page decision still documented
gcLastMarkedEpoch != V -- the exact bound this PR replaces -- so the file
stated both the old rule and the new one a few lines apart, with the wrong
one first. The bullet now carries the real criterion and says why != V is
insufficient; the paragraph below no longer restates it.

getenv("CN1_GC_DEBUG_EARLY") also ran once per early-freed slot, and the
earlyfree self-test drives 26,924 of them through that loop. Cached in a
static, like the other QA env reads in this file.

Gate re-run: clean, with both self-tests firing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 26, 2026 10:28
@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

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 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m
The resurrection audit runs at the end of the mark but drives the same
per-field reporter as the post-sweep gate, so anything it found announced
itself as happening "after sweep" -- pointing a reader at the wrong phase of
the collector while they try to reconstruct what freed the memory.

Carry the calling context in the report instead of suppressing the per-field
detail, which is the part worth having: the holder and victim classes and the
mark site are what map a resurrected object's dangling field back to source.
The audit also now says which resolver snapshot it classifies against (the
mark's, correct there because the memory it looks for was reclaimed by
earlier cycles).

Post-sweep reports still read "after sweep"; verified against the nograce
self-test, and the gate stays green with both self-tests firing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 26, 2026 10: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

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 411 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 20242 ms

  • Hotspots (Top 20 sampled methods):

    • 21.18% java.util.ArrayList.indexOf (377 samples)
    • 6.18% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (110 samples)
    • 3.76% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (67 samples)
    • 3.60% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (64 samples)
    • 3.26% java.lang.StringBuilder.append (58 samples)
    • 2.36% org.objectweb.asm.tree.analysis.Analyzer.analyze (42 samples)
    • 2.36% com.codename1.tools.translator.BytecodeMethod.optimize (42 samples)
    • 1.85% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (33 samples)
    • 1.57% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (28 samples)
    • 1.52% java.lang.System.identityHashCode (27 samples)
    • 1.52% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (27 samples)
    • 1.46% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (26 samples)
    • 1.35% java.util.HashMap.hash (24 samples)
    • 1.12% java.io.UnixFileSystem.getBooleanAttributes0 (20 samples)
    • 1.07% java.lang.Object.hashCode (19 samples)
    • 1.07% com.codename1.tools.translator.Parser.classIndex (19 samples)
    • 1.07% java.lang.StringCoding.encode (19 samples)
    • 1.07% org.objectweb.asm.ClassReader.readCode (19 samples)
    • 1.01% java.io.FileOutputStream.open0 (18 samples)
    • 1.01% java.lang.String.equals (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.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

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

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Jul 26, 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 66ms / native 5ms = 13.2x speedup
SIMD float-mul (64K x300) java 66ms / native 4ms = 16.5x 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 196.000 ms
Base64 CN1 decode 138.000 ms
Base64 SIMD encode 105.000 ms
Base64 encode ratio (SIMD/CN1) 0.536x (46.4% faster)
Base64 SIMD decode 84.000 ms
Base64 decode ratio (SIMD/CN1) 0.609x (39.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 23.000 ms
Image createMask (SIMD on) 19.000 ms
Image createMask ratio (SIMD on/off) 0.826x (17.4% faster)
Image applyMask (SIMD off) 55.000 ms
Image applyMask (SIMD on) 48.000 ms
Image applyMask ratio (SIMD on/off) 0.873x (12.7% faster)
Image modifyAlpha (SIMD off) 52.000 ms
Image modifyAlpha (SIMD on) 122.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.346x (134.6% slower)
Image modifyAlpha removeColor (SIMD off) 60.000 ms
Image modifyAlpha removeColor (SIMD on) 61.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.017x (1.7% slower)

@shai-almog

shai-almog commented Jul 26, 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 26, 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 26, 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 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 57ms / native 3ms = 19.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 246.000 ms
Base64 CN1 decode 127.000 ms
Base64 SIMD encode 65.000 ms
Base64 encode ratio (SIMD/CN1) 0.264x (73.6% faster)
Base64 SIMD decode 58.000 ms
Base64 decode ratio (SIMD/CN1) 0.457x (54.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 11.000 ms
Image createMask (SIMD on) 7.000 ms
Image createMask ratio (SIMD on/off) 0.636x (36.4% faster)
Image applyMask (SIMD off) 24.000 ms
Image applyMask (SIMD on) 17.000 ms
Image applyMask ratio (SIMD on/off) 0.708x (29.2% faster)
Image modifyAlpha (SIMD off) 15.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.800x (20.0% faster)
Image modifyAlpha removeColor (SIMD off) 16.000 ms
Image modifyAlpha removeColor (SIMD on) 12.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.750x (25.0% faster)

@shai-almog

shai-almog commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author

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

@shai-almog

shai-almog commented Jul 26, 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: 201 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 51ms / native 2ms = 25.5x speedup
SIMD float-mul (64K x300) java 53ms / native 3ms = 17.6x 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 157.000 ms
Base64 CN1 decode 118.000 ms
Base64 native encode 472.000 ms
Base64 encode ratio (CN1/native) 0.333x (66.7% faster)
Base64 native decode 192.000 ms
Base64 decode ratio (CN1/native) 0.615x (38.5% faster)
Base64 SIMD encode 46.000 ms
Base64 encode ratio (SIMD/CN1) 0.293x (70.7% faster)
Base64 SIMD decode 43.000 ms
Base64 decode ratio (SIMD/CN1) 0.364x (63.6% faster)
Base64 encode ratio (SIMD/native) 0.097x (90.3% faster)
Base64 decode ratio (SIMD/native) 0.224x (77.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.167x (83.3% faster)
Image applyMask (SIMD off) 38.000 ms
Image applyMask (SIMD on) 30.000 ms
Image applyMask ratio (SIMD on/off) 0.789x (21.1% faster)
Image modifyAlpha (SIMD off) 34.000 ms
Image modifyAlpha (SIMD on) 33.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.971x (2.9% faster)
Image modifyAlpha removeColor (SIMD off) 40.000 ms
Image modifyAlpha removeColor (SIMD on) 33.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.825x (17.5% faster)

@shai-almog

shai-almog commented Jul 26, 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: 480 seconds

Build and Run Timing

Metric Duration
Simulator Boot 68000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 3000 ms
Test Execution 815000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 63ms / native 3ms = 21.0x speedup
SIMD float-mul (64K x300) java 72ms / native 4ms = 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 284.000 ms
Base64 CN1 decode 182.000 ms
Base64 native encode 555.000 ms
Base64 encode ratio (CN1/native) 0.512x (48.8% faster)
Base64 native decode 623.000 ms
Base64 decode ratio (CN1/native) 0.292x (70.8% faster)
Base64 SIMD encode 78.000 ms
Base64 encode ratio (SIMD/CN1) 0.275x (72.5% faster)
Base64 SIMD decode 105.000 ms
Base64 decode ratio (SIMD/CN1) 0.577x (42.3% faster)
Base64 encode ratio (SIMD/native) 0.141x (85.9% faster)
Base64 decode ratio (SIMD/native) 0.169x (83.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 86.000 ms
Image applyMask (SIMD on) 65.000 ms
Image applyMask ratio (SIMD on/off) 0.756x (24.4% faster)
Image modifyAlpha (SIMD off) 54.000 ms
Image modifyAlpha (SIMD on) 45.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.833x (16.7% faster)
Image modifyAlpha removeColor (SIMD off) 82.000 ms
Image modifyAlpha removeColor (SIMD on) 45.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.549x (45.1% faster)

@shai-almog

shai-almog commented Jul 26, 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 26, 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: 279 seconds

Build and Run Timing

Metric Duration
Simulator Boot 67000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 1000 ms
Test Execution 843000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 58ms / native 3ms = 19.3x speedup
SIMD float-mul (64K x300) java 56ms / native 3ms = 18.6x 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 191.000 ms
Base64 CN1 decode 122.000 ms
Base64 native encode 482.000 ms
Base64 encode ratio (CN1/native) 0.396x (60.4% faster)
Base64 native decode 455.000 ms
Base64 decode ratio (CN1/native) 0.268x (73.2% faster)
Base64 SIMD encode 52.000 ms
Base64 encode ratio (SIMD/CN1) 0.272x (72.8% faster)
Base64 SIMD decode 45.000 ms
Base64 decode ratio (SIMD/CN1) 0.369x (63.1% faster)
Base64 encode ratio (SIMD/native) 0.108x (89.2% faster)
Base64 decode ratio (SIMD/native) 0.099x (90.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 9.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.222x (77.8% faster)
Image applyMask (SIMD off) 49.000 ms
Image applyMask (SIMD on) 47.000 ms
Image applyMask ratio (SIMD on/off) 0.959x (4.1% faster)
Image modifyAlpha (SIMD off) 83.000 ms
Image modifyAlpha (SIMD on) 167.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.012x (101.2% slower)
Image modifyAlpha removeColor (SIMD off) 116.000 ms
Image modifyAlpha removeColor (SIMD on) 256.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 2.207x (120.7% slower)

@shai-almog

shai-almog commented Jul 26, 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 26, 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 62ms / native 4ms = 15.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 195.000 ms
Base64 CN1 decode 127.000 ms
Base64 SIMD encode 103.000 ms
Base64 encode ratio (SIMD/CN1) 0.528x (47.2% faster)
Base64 SIMD decode 83.000 ms
Base64 decode ratio (SIMD/CN1) 0.654x (34.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 30.000 ms
Image createMask (SIMD on) 24.000 ms
Image createMask ratio (SIMD on/off) 0.800x (20.0% faster)
Image applyMask (SIMD off) 220.000 ms
Image applyMask (SIMD on) 59.000 ms
Image applyMask ratio (SIMD on/off) 0.268x (73.2% faster)
Image modifyAlpha (SIMD off) 58.000 ms
Image modifyAlpha (SIMD on) 60.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.034x (3.4% slower)
Image modifyAlpha removeColor (SIMD off) 65.000 ms
Image modifyAlpha removeColor (SIMD on) 35.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.538x (46.2% faster)

@shai-almog
shai-almog merged commit 0e845c8 into master Jul 26, 2026
48 of 49 checks passed
@shai-almog
shai-almog deleted the gc-aging-asymmetry branch July 26, 2026 13:21
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.

2 participants