Skip to content

Measured SIMD UTF and Unicode performance ​

Sparkles leads this field on long ASCII validation and terminal width. The October 2 non-ASCII pass also exceeds xutf on matched two-byte, CJK, supplementary and mixed-script width fixtures, and simdutf on mixed UTF-8→UTF-16 conversion. It is not universally fastest: simdutf8 leads multilingual validation, simdutf leads most conversion rows, and xutf leads complex-grapheme width and boundary enumeration.

Measured: September 30–October 2, 2026. The first three operation sections retain the initial SIMD pass; the October 2 follow-up records the subsequent optimization. See UTF algorithms, Unicode algorithms, and the survey inventory for papers, locally inspected checkouts, versions and source licensing.

Environment and contracts ​

PropertyMeasured configuration
HostAMD Ryzen 9 7940HX, Linux x86-64; AVX2 and AVX-512 available
Native DLDC 1.42.0, D frontend 2.112; -O3 -mcpu=native, optimization/inlining, assertions retained
C++GCC 15.3.0; -std=c++17 -O3 -DNDEBUG -fPIC; simdutf runtime-selected icelake implementation
Rustnightly 1.101.0 (5c543b0b8, September 29, 2026); release opt-level 3, thin LTO, one codegen unit, -C target-cpu=native
Runnersparkles:test-runner; 20 ms minimum sample time for the UTF matrix; the implementation library's opt-in benchmark configuration
Cache/workloadRepeated deterministic inputs, preallocated caller buffers, verification outside timing; hot/reused-input results
Small inputsUnder 256 bytes: 64 operations per timed batch; B/s includes all batch input bytes
Provenancesimdutf cf8715fad4d55c87aad3006a9a82531f740605b8; simdutf8 641d57f313df57354246d2b68d4778c092e076c3; xutf 9bb347af041369a68a4effd1ca85c6d2f9b4e17b

The initial scalar-to-SIMD baseline and result both contain 4,419 rows, zero errors, including malformed-input rows and an independent scalar D implementation. Only a representative subset is tabulated below; the follow-up uses its own focused baseline. Compiler flags are deliberately disclosed, not asserted equivalent across languages. Native D can inline; separately compiled foreign calls cross a C ABI and retain their library's dispatch overhead. No overhead is subtracted.

These are separate contracts:

  • Offset: first invalid UTF-8 sequence's lead byte, or the input length. Sparkles indexOfInvalidUtf8, simdutf diagnostic validation and simdutf8 compat agree on the tested fixtures.
  • Boolean: validity only. Sparkles uses its offset primitive; simdutf and simdutf8 basic have separate Boolean implementations. Whole-input versus early-exit behavior matters on malformed inputs.
  • Conversion: validating UTF-8 ↔ UTF-16, preserving embedded NUL. Sparkles accepts bounded destinations and guarantees fail-before-write on invalid input or insufficient capacity. The selected simdutf APIs require ample destination space and may write a prefix on failure. Their speed is not evidence that the stronger transaction contract costs nothing.
  • Display: matched printable fixtures only. Sparkles also handles ANSI, controls, replacement and terminal policy, and computes cluster width metadata during segmentation. xutf's Unicode version and full policy differ. The fixtures do not establish equivalence across all Unicode or ANSI inputs.

The source, exact revisions, foreign build commands, batch semantics and correctness checks are in libs/base/bench/utf/README.md. analysis.d normalization, folding, word boundaries and provenance are not disguised as width or segmentation benchmarks. Neither the normalization API nor generated Unicode tables changed.

UTF-8 validation ​

Throughput is decimal GB/s of input, using valid 65,536-byte fixtures. Speedup compares Sparkles before/after; competitor columns are from the final run.

Exact-offset corpusSparkles beforeSparkles afterSpeedupsimdutfsimdutf8 compat
ASCII19.999148.6087.43×99.147103.696
Two-byte1.66323.27314.00×19.12329.336
CJK3.10923.1997.46×19.46428.947
Supplementary2.21323.28110.52×19.12329.062
Mixed scripts1.50423.19915.43×19.34929.062
Embedded NUL1.60423.43914.61×19.07329.336
Boolean corpusSparkles beforeSparkles afterSpeedupsimdutfsimdutf8 basic
ASCII20.005152.0567.60×103.696103.861
CJK3.30823.2817.04×19.69831.001
Mixed scripts1.66223.27314.00×19.82331.001
Supplementary2.21523.28110.51×19.58631.148

Sparkles leads these long ASCII rows and exceeds this simdutf backend on the tabulated multilingual validation rows. simdutf8 remains faster on multilingual input. A 64-byte ASCII offset call instead measured 4.86 ns for Sparkles, 5.02 ns for simdutf and 3.59 ns for simdutf8 compat; Boolean calls were 7.03, 4.38 and 2.03 ns respectively. Those values are per operation after dividing the 64-operation batch. Long-input leadership does not imply short-input leadership.

UTF conversion ​

The corpus suffix names the original UTF-8 fixture size. to16 throughput counts UTF-8 input bytes; to8 counts UTF-16 input bytes, not the original UTF-8 byte count. All buffers are sized before timing.

DirectionCorpusSparkles before GB/sSparkles after GB/sSpeedupsimdutf GB/s
UTF-8 → UTF-16ASCII0.45418.90341.61×44.796
UTF-8 → UTF-16Two-byte0.5840.9571.64×8.663
UTF-8 → UTF-16CJK0.7361.3051.77×7.615
UTF-8 → UTF-16Supplementary0.7981.4721.85×5.031
UTF-8 → UTF-16Mixed scripts0.5731.0101.76×4.234
UTF-16 → UTF-8ASCII0.95529.39530.77×85.500
UTF-16 → UTF-8Two-byte0.8401.2931.54×19.989
UTF-16 → UTF-8CJK0.7461.3781.85×9.588
UTF-16 → UTF-8Supplementary1.0931.7371.59×5.758
UTF-16 → UTF-8Mixed scripts0.9441.5921.69×7.240

In this initial pass, ASCII widening/narrowing and much of sizing were vectorized, but non-ASCII emission remained scalar. simdutf led every conversion row in this table. The October 2 pass below implements the compaction algorithm family rather than merely widening classification.

Terminal width and cluster boundaries ​

The fixtures are 65,536 UTF-8 bytes. Width scans can aggregate ASCII without materializing a cluster per character; boundary enumeration cannot skip those outputs.

Visible-width corpusSparkles before GB/sSparkles after GB/sSpeedupxutf GB/s
ASCII0.016148.9459,306.62×102.240
Two-byte0.0260.0532.05×0.920
CJK0.0350.0671.92×1.198
Supplementary0.0450.0861.92×0.867
Mixed scripts0.0340.0772.30×1.004
Combining/ZWJ/flags0.0520.1082.06×0.893

The ASCII width median fell from 4,094,913 ns to 440 ns, versus xutf's 641 ns. The large multiplier reflects removal of per-character cluster work from an operation whose ASCII answer is its byte length; it is not a claim of that multiplier on general Unicode. At 64 ASCII bytes, Sparkles measured 5.33 ns and xutf 2.97 ns.

Cluster-boundary corpusSparkles before GB/sSparkles after GB/sSpeedupxutf GB/s
ASCII0.0150.0875.88×0.843
CJK0.0330.0481.47×0.698
Supplementary0.0410.0621.51×0.677
Mixed scripts0.0310.0511.65×0.683
Combining/ZWJ/flags0.0500.0851.69×0.751

In this initial pass, xutf led non-ASCII width and every tabulated boundary-enumeration row. Sparkles retains Phobos segmentation and the 32-codepoint cluster cap rather than changing Unicode behavior to win the benchmark.

October 2 non-ASCII follow-up ​

This section retains the pre-CI-repair measurements. The post-CI workload summary below measures the corrected kernels anew.

Same host, toolchains, foreign revisions and assertion-enabled native flags; 100 ms minimum sample time per row. The focused baseline contains 60 rows, zero errors; the final expanded subset adds sparse Unicode and ASCII checks for 166 rows, zero errors. The baseline already includes the initial SIMD validator, ASCII conversion and fixed-window segmentation; these speedups measure the subsequent non-ASCII work, not the scalar-to-SIMD cutover. Inputs are repeated 65,536-byte UTF-8 fixtures; conversion byte accounting is unchanged from the earlier section.

Visible-width corpusSparkles before GB/sSparkles after GB/sSpeedupxutf GB/s
Two-byte0.0501.59431.65×0.917
CJK0.0661.90529.01×1.193
Supplementary0.0852.12124.82×0.866
Mixed scripts0.0761.77123.29×1.004
Combining/ZWJ/flags0.1050.2362.25×0.895

Ordinary singleton-run aggregation and bounded escape search exceed xutf by 60–145% on the four matched simple-Unicode fixtures. CJK width retires 1.060 M instructions instead of 20.397 M. Complex clusters still use Phobos and remain about four times slower than xutf; retaining the original segmentation policy is not evidence that this gap has disappeared.

DirectionCorpusSparkles before GB/sSparkles after GB/sSpeedupsimdutf GB/s
UTF-8 → UTF-16Two-byte0.9435.1305.44×8.721
UTF-8 → UTF-16CJK1.2794.7853.74×7.535
UTF-8 → UTF-16Supplementary1.2314.6923.81×5.162
UTF-8 → UTF-16Mixed scripts1.0164.8774.80×4.349
UTF-16 → UTF-8Two-byte1.2723.7452.94×20.151
UTF-16 → UTF-8CJK1.3517.6015.63×9.525
UTF-16 → UTF-8Supplementary1.7103.5612.08×5.963
UTF-16 → UTF-8Mixed scripts1.5303.8312.50×7.240

Register compaction, wide preflight and homogeneous-width emission improve conversion 2.1–5.6×. Mixed UTF-8→UTF-16 exceeds simdutf by 12% while retaining bounded destinations and fail-before-write. simdutf still leads the other seven rows; the transactional contracts remain different.

Grouped AVX-512 validation reduces CJK offset-validation instructions from 75.164 k to 30.906 k, but throughput rises only from 23.027 to 25.255 GB/s, versus simdutf8 compat's 29.336 GB/s. Wider vectors and fewer retired instructions do not alone establish higher throughput on this Zen 4 host. Other multilingual offset rows measure 25.158–25.352 GB/s; Boolean rows measure 25.148–25.255 GB/s, below simdutf8 basic's 30.710–31.432 GB/s.

Cluster-boundary corpusSparkles before GB/sSparkles after GB/sSpeedupxutf GB/s
Two-byte0.0330.1243.76×0.519
CJK0.0480.1683.49×0.698
Supplementary0.0620.2173.48×0.670
Mixed scripts0.0530.1613.02×0.669
Combining/ZWJ/flags0.0780.1892.43×0.748

Decoded-window reuse and fixed-width decoding improve boundary enumeration, but xutf still leads every row. Sparkles' range computes full cluster metadata; the Rust shim emits boundary offsets without that additional terminal policy.

Sparse Unicode and ASCII resumption ​

A separate 14-row, zero-error pre-correction field starts with é漢😀 and follows it with ASCII to total 65,536 bytes. Validation and conversion must resume their ASCII paths; width must not discover escape-free runs with a scalar byte loop. The final expanded subset exercises the same fixtures.

OperationBefore correction GB/sFinal GB/sSpeedupBest peerPeer GB/s
Exact-offset validation25.451105.3634.14×simdutf8102.240
Boolean validation24.871103.8614.18×simdutf8107.260
UTF-8 → UTF-165.16219.7643.83×simdutf44.191
UTF-16 → UTF-83.75310.8472.89×simdutf89.037
Visible width4.86360.56912.45×xutf99.147
Cluster boundaries0.0890.0901.01×xutf0.840

The sparse exact-offset row narrowly leads this measurement; Boolean validation, conversion, width and boundaries still have competitor leads.

The larger scanner also exposed a small-ASCII width initialization regression. Returning printable ASCII before constructing the scanner removes it:

ASCII bytesInitial SIMD pass GB/sIntermediate GB/sFinal GB/s
10.2910.0690.400
6412.0124.17115.694
65,536148.608145.313148.608

This is not a win for every small operation: one-byte boundary enumeration measures 0.045 GB/s versus 0.068 GB/s in the initial pass. The larger metadata queue still has per-iterator overhead; the width shortcut does not remove it.

These are hot/reused-input measurements, not DRAM bandwidth or cold-cache results. The earlier ~149 GB/s ASCII row repeatedly scans a 64 KiB input; no equivalent streaming-memory throughput is claimed. CPU affinity and governor isolation are not claimed, and absent LLC counters are not zero misses.

Post-CI workload summary ​

After the compile-memory and unoptimized vector-ABI corrections, a fresh 555-row field passed with zero errors, including the independent scalar D reference. Same host, foreign revisions, assertion-enabled native flags and 100 ms minimum sample time. Units below are input GB/s, including both bytes of each UTF-16 unit. — means no measured adapter row, not an unsupported library API. Ranges span workload variants, not confidence intervals.

Operation / workloadSparklesScalar Dsimdutfsimdutf8xutf
Exact offset / ASCII 1 byte0.4540.9140.2130.305—
Exact offset / ASCII 64 bytes10.7511.24612.37519.505—
Exact offset / ASCII 64 KiB139.1421.25396.235100.670—
Exact offset / four dense Unicode scripts23.448–24.3181.369–1.88418.687–19.24128.188–28.807—
Exact offset / sparse Unicode + ASCII99.1471.24196.23599.147—
Exact offset / malformed UTF-8 at end, 17 variants130.816–136.2641.228–1.25988.447–92.05596.239–100.525—
Boolean / ASCII 1 byte0.2660.9140.2670.427—
Boolean / ASCII 64 bytes8.1761.63515.17031.267—
Boolean / ASCII 64 KiB136.2491.654100.670100.670—
Boolean / four dense Unicode scripts23.532–24.0501.551–1.78619.073–19.46429.721–30.710—
Boolean / sparse Unicode + ASCII97.5241.63099.147105.363—
Boolean / malformed UTF-8 at end, 17 variants128.252–136.2641.196–1.42597.525–100.68096.094–100.525—
UTF-8 → UTF-16 / ASCII 64 KiB17.5370.27242.473——
UTF-8 → UTF-16 / four dense Unicode scripts4.440–4.8160.392–0.5713.839–8.249——
UTF-8 → UTF-16 / mixed scripts4.6590.4213.839——
UTF-8 → UTF-16 / sparse Unicode + ASCII17.5840.28641.930——
UTF-8 → UTF-16 / embedded NUL4.5930.4274.245——
UTF-8 → UTF-16 / malformed UTF-8 at end37.796–39.4120.586–0.60140.134–42.751——
UTF-16 → UTF-8 / ASCII 64 KiB25.0141.26783.859——
UTF-16 → UTF-8 / four dense Unicode scripts3.437–7.1170.598–0.9265.634–19.166——
UTF-16 → UTF-8 / sparse Unicode + ASCII10.0081.25584.940——
UTF-16 → UTF-8 / embedded NUL3.5240.8427.172——
UTF-16 → UTF-8 / lone surrogate at end55.918–56.3752.606–2.62080.215–83.326——
Visible width / ASCII 1 byte0.376———0.376
Visible width / ASCII 64 bytes13.170———20.480
Visible width / ASCII 64 KiB139.142———100.670
Visible width / two-byte1.567———0.899
Visible width / CJK1.877———1.158
Visible width / supplementary2.094———0.844
Visible width / mixed scripts1.717———0.969
Visible width / sparse Unicode + ASCII59.470———94.842
Visible width / combining, ZWJ, flags0.193———0.863
Boundaries / ASCII 64 KiB0.086———0.819
Boundaries / four dense Unicode scripts0.118–0.208———0.509–0.677
Boundaries / sparse Unicode + ASCII0.086———0.823
Boundaries / combining, ZWJ, flags0.165———0.735

Sparkles leads long ASCII validation/width, late malformed UTF-8 validation, simple Unicode width (62–148% above xutf), and mixed/embedded-NUL UTF-8→UTF-16. simdutf8 leads dense Unicode validation; simdutf leads most conversion cases; xutf leads complex width and every boundary-enumeration case. Small inputs have mixed winners, including the independent scalar reference.

The conversion failure rows are still different contracts: Sparkles validates before any write; simdutf may have written a prefix. Boundary iteration retains Sparkles' width metadata and 32-codepoint cap. Error rates above use late errors; they must not be extrapolated to early-error whole-input throughput.

Only simdutf, simdutf8 and xutf have measured foreign adapters. The other eleven libraries in the fourteen-library inventory were surveyed, not timed: is_utf8, utf8_range, encoding_rs, simd-normalizer, fastvalidate-utf-8, utf8lut, unicode-segmentation, unicode-normalization, unicode-width, utf8proc and uni-algo. Normalization, folding, provenance-bearing analysis, ANSI-policy edge cases and cold-cache performance have no competitive measurement in this field. Capability coverage is not a performance ranking.

Wired: measured consumer, not an inferred speedup ​

The same wired-native runtime field was measured before and after: 15 rows, seven datasets, zero row errors. These runs preserve the existing wired bench build's assertion-disabled configuration in both measurements; the new UTF matrix uses the assertion-enabled optimized configuration described above. The two benchmark fields should not be conflated.

Dataset / operationBefore µsAfter µsBefore GB/sAfter GB/s
canada / parse1411.8371357.3911.5941.658
canada / validate1477.1141454.6511.5241.547
citm_catalog / parse406.491406.6664.2494.247
citm_catalog / validate939.3721007.0851.8391.715
github_events / parse15.63115.4104.1674.227
github_events / validate42.23341.6011.5421.566
mesh / parse420.698420.7191.7201.720
mesh / validate560.731563.7371.2901.284
mesh_pretty / parse809.390786.1901.9492.006
mesh_pretty / validate960.018939.6341.6431.679
osm / parse1943.6621897.1521.5351.573
osm / validate2443.7352355.6501.2211.267
twitter / decode197.414193.3463.1993.266
twitter / parse168.989164.6403.7373.836
twitter / validate422.231413.8951.4961.526

The final non-ASCII kernels were measured against the original consumer baseline. Twitter parse time improved 2.6% and decode 2.1%. Retired instructions fell from 3.774 M to 3.625 M for parse and 4.098 M to 3.950 M for decode. Validation instructions were effectively unchanged at 11.258 M. Other datasets show changes in both directions, including slower citm_catalog validation. There is no broad wired speedup in this measurement. Small timing changes without corresponding work reduction should not be presented as a statistically established win.

Counters covered kernel+user. The runner dropped LLC events because they would multiplex with the available counters; the absent cache-miss values are not zero cache misses. No CPU affinity/governor isolation is claimed.

Implementation and invariants ​

  • utf8_simd.d carries previous vector bytes across blocks. AVX2 and AVX-512BW use independently derived nibble classifications and continuation consistency from the researched lookup algorithm family; SSE2 uses byte-class masks. An initial four-vector ASCII sweep avoids speculative four-vector probes on every multilingual block. Four multilingual vectors share one error reduction. Scalar replay preserves first-invalid sequence-lead offsets, including a sequence crossing a group boundary.
  • simd_caps.d checks CPUID F/BW/VL and XCR0 before admitting wide paths; compaction also requires VBMI2. Function-local targets explicitly enable LLVM 18's evex512 backend feature for ZMM/64-bit-mask lowering, while VL admits narrowed opmask operations. An Android core2-baseline build exposed both missing lowering requirements; the corrected minimal cross-compile and both real APK builds pass. utf8.d dispatches eligible runtime inputs to the bounded validator and provides fixed-width decoding for already-validated prefixes. Malformed replacement still delegates to Phobos, preserving byte consumption. DMD, other architectures and CTFE retain scalar behavior; no foreign dependency was added to base.
  • utf16_simd.d counts UTF-8 units and validates/sizes UTF-16 blocks, including AVX-512BW paths, surrogate-pair masks and a deferred final high surrogate. utf16.d completes source/capacity preflight before any write. utf16_emit.d emits non-ASCII blocks using register compaction followed by exact masked stores; homogeneous two-byte and three-byte blocks avoid general conversion work. Memory-form compaction regressed on this Zen 4 host and was rejected.
  • grapheme.d reuses a bounded 64-codepoint decoded queue while retaining the 32-codepoint cluster cap. width.d packs width and singleton-boundary traits derived from public Phobos probes. gen_grapheme_tables.d runs those probes offline and emits compact runs in grapheme_tables.d, avoiding 262,144 grapheme probes in each consumer's CTFE heap. A runtime exhaustive parity test checks the supported frontend; other frontend versions retain the public engine. Width-only scans aggregate ordinary singleton runs, withholding their final starter until attachment is ruled out. Complex clusters retain Phobos; failed aggregation is retried after a promising queue refill rather than at every cluster. Boundary iteration still produces full metadata.
  • Compaction intrinsics live in explicitly targeted functions rather than trusted callsite lambdas: lambdas do not inherit the parent's target UDA. This also allows nonoptimized, baseline-target LDC builds; an isolated debug emitter compile failed before that correction and passed afterward.
  • simd_io.d keeps wide vector returns in target-matched functions, with trusted pointer loads assigning through a reference rather than returning a vector from a baseline lambda. The public unoptimized LDC suite exposed seven runtime failures before this ABI correction; all pass afterward. preceding also uses the matching target ABI.
  • scan.d uses the shared validator's string-body mode from its four UTF-8 run callsites. Quote, backslash and control bytes stop bulk scanning and re-enter the existing grammar path. Its padding contract remains unchanged; vector loads are bounded by the actual available pool.

All vector loads/stores are bounded; caller input/output does not acquire a padding requirement. Public signatures, @safe pure nothrow @nogc behavior, embedded-NUL policy, transactional conversion and Unicode tables remain unchanged.

Correctness evidence and limits ​

Observed across the initial and non-ASCII passes:

  • Independent scalar UTF-8 differential smoke: 6,157,573 inputs, covering every scalar value, truncations, corrupted bytes and 32 alignments; exact-offset parity.
  • Guard-page smoke: 2,556 cases, source/destination bounds through length 512, ordinary and NUL-terminated conversion, invalid tails and insufficient capacity; compile-time round-trip and transaction checks also passed.
  • Full unoptimized LDC base suite after CI corrections: 620 passed, including forced SSE2/AVX2/AVX-512 boundary checks, the permanent 1,026-case guard-page conversion test and exhaustive cached-boundary parity. The ownership negative-copy probe now checks an invoked body and keeps its source alive; the earlier -allinst test-build blocker is removed.
  • Full DMD base suite after CI corrections: 619 passed.
  • Final full LDC wired consumer suite: 201 passed.
  • Real competitor correctness sweep: passed after the CI corrections. The intermediate complete field contains 4,419 rows, zero errors; the pre-CI subset contains 166 rows, zero errors; the post-CI representative field contains 555 rows, zero errors, including scalar D and valid/invalid workload variants.
  • Offline conformance layers 1 and 2: 1,112,064 scalar widths and 3,655 RGI emoji cases passed.
  • Actual Nix ci-minimal, text-wasm and table-wasm builds passed. A checked native dub build :ci completed in 71.57 seconds; GNU time reported maximum RSS 2,721,536 KiB. Earlier CI consumer builds were killed while compiling base; no CI guards, timeouts or assertions were weakened.
  • Actual Nix terminal-apk and hue-apk builds passed for x86_64 and arm64-v8a after the explicit target-feature correction. The VitePress build passed with the CI Node heap settings after replacing nonexistent documentation-page URLs with pinned source URLs; the rendered benchmark link was checked in Chromium.

Layer 0 is not all green: on LDC 1.42, Unicode 15 GraphemeBreakTest passed 601/602 cases. U+2701 U+200D U+2701 split as [2, 1] instead of [3]. A direct Phobos probe and the original shared-buffer window produce the same first stride of two. This predates the optimized window; neither expected results nor the divergence allowlist were changed to hide it. The conformance documentation records the compiler-version observation.

These results do not cover cold-cache throughput, every processor, every Unicode policy, all normalization/search operations, or a complete independent segmentation replacement. No universal fastest-library claim follows from this matrix.

Reproduce ​

Build the real C++ and Rust shims using the benchmark README, with the pinned checkout revisions. Then run from the repository root:

sh
dub test --root=libs/base/bench/utf --compiler=ldc2 -bbench \
  -cunittest-foreign -- -i 'utf\.correctness$'
libs/base/bench/utf/build/base-utf-bench-test-unittest-foreign \
  --bench -i 'utf\.(offset|boolean|to16|to8|display)$' \
  --group-by=operation,corpus --bench-min-time=20 \
  --bench-json=/tmp/sparkles-base-utf-final.json --no-colors

For the pre-CI non-ASCII, sparse and ASCII subset, use the same built binary:

sh
UTF_BENCH_CORPORA='two-byte/65536,cjk/65536,supplementary/65536,mixed/65536,grapheme/65536,sparse/65536,ascii/1,ascii/64,ascii/65536' \
UTF_BENCH_ENGINES=sparkles,simdutf,simdutf8,xutf \
  libs/base/bench/utf/build/base-utf-bench-test-unittest-foreign \
  --bench --perf -i 'utf\.(offset|boolean|to16|to8|display)$' \
  --group-by=operation,corpus --bench-min-time=100 \
  --bench-json=/tmp/base-nonascii-final.json --no-colors

Corpus selectors are prefixes: ascii/1 also selects 15, 16, 127, 128 and 129.

For the post-CI summary, include the scalar reference, every ASCII size and the documented malformed families at the end of long ASCII prefixes:

sh
UTF_BENCH_CORPORA='ascii/,two-byte/65536,cjk/65536,supplementary/65536,mixed/65536,grapheme/65536,sparse/65536,nul/65536,invalid8/0/65535,invalid8/1/65535,invalid8/2/65535,invalid8/3/65535,invalid8/4/65535,invalid8/5/65535,invalid8/6/65535,invalid8/7/65535,invalid8/8/65535,invalid8/9/65535,corrupt/3/65535,corrupt/4/65535,invalid16/65535' \
UTF_BENCH_ENGINES=scalar-d,sparkles,simdutf,simdutf8,xutf \
  libs/base/bench/utf/build/base-utf-bench-test-unittest-foreign \
  --bench --perf -i 'utf\.(offset|boolean|to16|to8|display)$' \
  --group-by=operation,corpus --bench-min-time=100 \
  --bench-json=/tmp/base-ci-workloads.json --no-colors

The assertion-enabled warning is a runner heuristic, not evidence that this optimized bench build is a debug build. Do not disable assertions to silence it. See runner configuration.

Run wired from libs/wired/bench/runtime, preserving the same flags on both sides:

sh
DC=ldc2 WIRED_BENCH_ENGINES=wired-native \
  dub test -bbench -f --override-config=sparkles:test-runner-impl/benchmark -- \
  --bench --perf -i 'wired\.(parse|decode|validate)$' \
  --bench-min-time=250 --bench-json=/tmp/wired-utf-after.json \
  --group-by=dataset,operation --no-colors

WIRED_BENCH_DATA pointed to the flake's wired-bench-data store output for the measured runs; use the wired benchmark setup for the dataset environment.

Session raw JSON artifacts are retained at /tmp/sparkles-base-utf-before.json, /tmp/sparkles-base-utf-final.json, /tmp/base-nonascii-before.json, /tmp/base-utf-oct2-final.json (intermediate complete field), /tmp/base-sparse-before.json, /tmp/base-nonascii-final.json (pre-CI), /tmp/base-ci-workloads.json (post-CI), /tmp/wired-utf-before.json, /tmp/wired-utf-after.json (initial pass) and /tmp/wired-utf-oct2.json (non-ASCII pass). They are local measurement artifacts, not files shipped with this catalog. The tables above preserve the representative observations; do not treat an absent local artifact as reproducible baseline evidence on another host.