Measured on an Apple M1 running macOS 26.3, OTP 28, and Elixir 1.19.5. Each result is the median of 12 production-build iterations after 3 warmups. Rates are based on input bytes; results vary by machine, runtime, and data mix.

Run with:

MIX_ENV=prod mix run bench/benchmark.exs
ConversionBaseline MiB/sOptimized MiB/sSpeedup
UTF-8 -> UTF-823.69134.705.69x
CP1252 -> UTF-83.819.482.49x
UTF-8 -> CP12525.027.441.48x
SHIFT_JIS -> UTF-83.895.721.47x
UTF-8 -> SHIFT_JIS17.9120.511.15x
GB18030 -> UTF-82.185.252.41x
UTF-8 -> GB180307.8714.961.90x

The exhaustive packed-table conformance test also fell from 36.9 seconds to 1.8 seconds after replacing a quadratic table encoder with a linear specialized loop.

Optimizations retained

  • Valid UTF-8 identity conversion returns the original reference after validation.
  • Single-byte table decoding writes UTF-8 directly without an intermediate list.
  • Single-codepoint table encoding uses a linear recursive loop.
  • GB18030 dispatches by byte shape instead of performing repeated longest-map probes.
  • Mapping terms are compressed on disk, loaded lazily, and cached in persistent_term.

Strict conversion uses the fast paths. Discard conversion uses linear native loops for each codec family, preserving table longest matches and stateful designation/shift state without restarting at every rejected code point.

The exhaustive differential run exposed a quadratic length/1 call in the ISO-2022-JP-3 JIS X 0213 selector. The all-scalar case exceeded 90 seconds before interruption and takes about 1.2–2.3 seconds after the constant-time head-pair probe. The complete 198-codec forward/reverse/cross-decode run took 171.36 seconds on the benchmark machine; per-codec timings are recorded in EXHAUSTIVE_UNICODE_DIFFERENTIAL.md.

Final verification after upstream-suite port

The same production benchmark was rerun after adding the complete test corpus, substitution policies, and EBCDIC surface handling. A final warm run measured:

ConversionMiB/sMedian
UTF-8 -> UTF-8158.197.38 ms
CP1252 -> UTF-811.6982.84 ms
UTF-8 -> CP12526.90169.34 ms
SHIFT_JIS -> UTF-85.70128.46 ms
UTF-8 -> SHIFT_JIS20.3354.03 ms
GB18030 -> UTF-84.71162.11 ms
UTF-8 -> GB1803011.8870.25 ms

The new policy handling does not change the strict fast-path dispatch. Throughput varies between runs because these are scheduler/GC-sensitive pure-BEAM workloads; the benchmark script records exact inputs, warmups, iterations, medians, and reductions for reproducibility.

External codec dispatch

External codecs are registered through a serialized process, but conversions read their immutable metadata directly from protected ETS with concurrent-read optimization. Built-in names resolve from compiled maps first and never touch ETS. Registered codec modules may provide direct UTF-8 callbacks to avoid an intermediate code-point list.

Run the dedicated benchmark with:

MIX_ENV=prod mix run bench/external_codec_benchmark.exs

Apple M1, OTP 28, Elixir 1.19.5; median of 12 iterations after 3 warmups:

External ASCII conversionThroughputMedian
Generic external -> UTF-830.82 MiB/s33.42 ms
Zero-copy external -> UTF-8591.26 MiB/s1.74 ms
UTF-8 -> generic external19.93 MiB/s51.68 ms
UTF-8 -> zero-copy external586.21 MiB/s1.76 ms
Name lookupCost
Built-in string1267.6 ns/op
External string1713.8 ns/op
External module247.1 ns/op

The same run of the original bundled-codec benchmark measured 160.17 MiB/s for UTF-8 identity, 17.65 MiB/s for CP1252 decoding, 20.68 MiB/s for Shift-JIS encoding, and 15.01 MiB/s for GB18030 encoding. External support therefore did not introduce a material built-in throughput regression on this run.

Post-split release verification

After moving all 86 non-default codecs into iconvex_extras, the core production benchmark was rerun on Apple M1, OTP 28, and Elixir 1.19.5:

ConversionMiB/sMedian
UTF-8 -> UTF-8244.204.78 ms
CP1252 -> UTF-824.3639.74 ms
UTF-8 -> CP125212.5193.42 ms
SHIFT_JIS -> UTF-88.4486.73 ms
UTF-8 -> SHIFT_JIS31.9334.41 ms
GB18030 -> UTF-87.6899.28 ms
UTF-8 -> GB1803022.0637.82 ms

The extras package's byte-identical CP932/CP943 paired benchmark measured only 1.6% decode and 0.6% encode overhead for external-package dispatch. Full values and reductions are in iconvex_extras/BENCHMARKS.md.

Fixed-width bit packing

Run MIX_ENV=prod mix run bench/packed_benchmark.exs. It executes strict packing and unpacking over 1 MiB of units, with two warmups and the median of seven production-build samples. An isolated Apple M1 / OTP 28 / Elixir 1.19.5 run measured:

Width/orderPack MiB/sUnpack MiB/s
5-bit MSB61.4114.06
5-bit LSB23.2719.25
6-bit MSB162.8740.14
6-bit LSB165.0443.37
7-bit MSB47.3114.55
7-bit LSB21.3015.90

The implementation validates and emits in one linear pass. Generic widths use bounded 4,096-unit binary chunks rather than allocating one bitstring or list node per unit. Six-bit MSB and LSB paths additionally consume four units as one 24-bit group. Exact bit length, invalid-unit offsets, truncated groups, and final-octet padding validation remain part of the timed API.

Post-review performance gate

After the deep-dive fixes, the combined all-scalar differential exposed repeated table searches in stateful encoders and per-codepoint string formatting in JAVA and C99. Versioned precedence maps now reduce ISO-2022/HZ encoding to one map lookup per scalar; escape codecs use direct nibble conversion; compatible Unicode pairs transcode through BEAM's Unicode BIF with exact native fallback.

Same Apple M1 / OTP 28 / Elixir 1.19.5 process and corpus:

CodecBeforeFinalImprovement
ISO-2022-JP-213,172 ms332 ms39.7x
ISO-2022-JP-MS10,350 ms286 ms36.2x
ISO-2022-JP-39,488 ms508 ms18.7x
ISO-2022-CN-EXT6,020 ms365 ms16.5x
JAVA7,167 ms1,677 ms4.3x
C995,543 ms1,283 ms4.3x
HZ2,220 ms275 ms8.1x

The original optimized 198-codec forward/reverse/cross-decode gate fell from 261,695 ms to 43,849 ms. It fails if any codec exceeds 30.00x its GNU 1.19 reference time. The latest source-bound rerun took 151,134 ms: 198/198 codecs were byte-exact, with zero mismatches and zero performance failures. The measurement ran on Dell/OTP 27, covers all 1,114,112 Unicode code points, and records the fastest of three isolated samples per direction after a three-sample calibration to a minimum 10 ms timing window. Its worst measured slowdown was ISO-2022-CN at 24.80x.

A reproducibility audit retained every outcome rather than discarding a failed sample. The first post-optimization complete run was byte-exact but recorded non-reproducing reverse outliers for IBM-1148 (103.89x) and ISO-8859-16 (42.75x). An immediate focused rerun passed both beneath 10x, and the second complete run passed all 198 codecs with C99 worst at 23.68x. The failed run remains recorded as scheduler/CPU-frequency noise rather than being hidden or used to relax the unchanged 30.00x gate.

Final production application benchmark:

ConversionMiB/sMedian
UTF-8 -> UTF-8233.145.01 ms
CP1252 -> UTF-828.0934.47 ms
UTF-8 -> CP125212.6292.60 ms
SHIFT_JIS -> UTF-88.9481.92 ms
UTF-8 -> SHIFT_JIS32.0634.26 ms
GB18030 -> UTF-88.3391.60 ms
UTF-8 -> GB1803023.7335.16 ms