Skip to content

Scan _isAscii through a byteArrayViewVarHandle instead of Unsafe.getLong - #15

Merged
merlimat merged 1 commit into
streamnative:masterfrom
merlimat:isascii-varhandle
Jul 30, 2026
Merged

Scan _isAscii through a byteArrayViewVarHandle instead of Unsafe.getLong#15
merlimat merged 1 commit into
streamnative:masterfrom
merlimat:isascii-varhandle

Conversation

@merlimat

Copy link
Copy Markdown
Collaborator

Motivation

String materialization (readString) decides between the zero-copy LATIN1 fast path and full UTF-8 decoding by scanning the copied bytes with _isAscii, 8 bytes at a time via sun.misc.Unsafe.getLong. Since JDK 24 (JEP 498) every sun.misc.Unsafe memory-access call executes beforeMemoryAccess() with an acquire-load, so the scan pays a fence per 8 bytes on every materialized string of 8+ chars — and those calls are also on the eventual-removal track.

Changes

_isAscii now scans through a MethodHandles.byteArrayViewVarHandle(long[].class, nativeOrder()) view: plain unfenced 8-byte loads on a supported API. The 0x8080808080808080 mask test is byte-order independent. The now-unused MH_GET_LONG plumbing is removed.

Results

No effect on the existing benchmarks — the only string they materialize is 3 chars, which never enters the 8-byte loop. On a temporary benchmark materializing a 57-char topic-style name (persistent://my-tenant/my-namespace/my-topic-partition-42, parse + getName(), 3 forks per side, JDK 26):

ops/µs
Unsafe.getLong scan 35.69 ± 1.67
VarHandle scan 37.28 ± 0.70 (+4.5%)

Longer strings benefit proportionally more. This is also one step of the planned migration off sun.misc.Unsafe memory-access methods before their removal.

286 tests green.

Since JDK 24 (JEP 498) every sun.misc.Unsafe memory-access call performs
an acquire-load, taxing the 8-byte scan that string materialization runs
on every >=8 char string. The VarHandle view compiles to plain unfenced
loads, and it is one less use of an API on the removal track.

No effect on the existing benchmarks - their only materialized string is
3 chars, which never enters the 8-byte loop.
Copilot AI review requested due to automatic review settings July 30, 2026 21:36
@merlimat
merlimat merged commit 738ad3d into streamnative:master Jul 30, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown

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 LightProto’s string materialization fast path to avoid sun.misc.Unsafe.getLong fences on JDK 24+ by switching ASCII scanning in _isAscii to use a MethodHandles.byteArrayViewVarHandle(long[].class, nativeOrder()) for plain (unfenced) 8-byte loads, and removes the now-unused MH_GET_LONG MethodHandle plumbing.

Changes:

  • Replace the _isAscii 8-byte scan implementation from Unsafe.getLong (via MH_GET_LONG) to a VarHandle byte[]→long view using plain get.
  • Remove MH_GET_LONG declarations/initialization now that it’s unused.
  • Simplify _isAscii by removing the MethodHandle invokeExact try/catch wrapper that was only needed for Throwable.

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

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