Skip to content

v0.4.0 - Alpine, a linked-in core, and every benchmark re-measured

Choose a tag to compare

@buvinghausen buvinghausen released this 02 Oct 03:49
· 52 commits to master since this release

0.4.0 widens where HyperUuid runs, removes the shared library from the builds that never needed one, and replaces the published benchmarks with a fresh set measured on x86-64. The full list is in CHANGELOG.md.

Highlights

  • Alpine works. linux-musl-x64 and linux-musl-arm64 are built, attested and shipped in every package (as musllinux_1_2 wheels for Python). Through 0.3.0 Alpine got the glibc library, which does not load under musl.
  • The core links in where it can.
    • Swift on Linux links a static library through SwiftPM, which adds the static Linux SDK (musl) and WebAssembly to that binding. Nothing has to be deployed beside the executable.
    • Go cgo builds on Linux and macOS link the core instead of embedding and dlopening it. The binary starts without touching the filesystem and runs from a read-only or scratch image.
    • C# Native AOT publishes link the core into the executable.
  • A load probe in every binding. The core reports its own version (hyperuuid_version), and each binding fronts it with a check that never throws: UuidGenerator.IsAvailable/NativeVersion in C#, and the same pair in each language's idiom.
  • Java in a GraalVM Native Image is fast. FFM calls were being interpreted there: 6.4 µs per newV7(long) against 36 ns on the JVM. They are now compiled: 78 ns. Nothing changes on the JVM, and a consumer's native-image build needs no configuration.
  • Python and Ruby calls cost about half.
    • Python: new_v4() 540 → 243 ns, new_v7(ms) 726 → 354 ns, v7_timestamp 466 → 252 ns. Wheels are unchanged (still one abi3 wheel per platform).
    • Ruby: new_v4 317 → 234 ns, new_v7 512 → 284 ns on the Magnus backend.
  • New in Python: v6_unix_millis and v7_unix_millis return the embedded timestamp as an int, with no datetime built (161 ns). The package is now typed (py.typed).

Benchmarks, re-measured

Every table was re-measured on linux-x64 (Intel Core i9-11900H) with each binding's own harness. The previous figures came from an arm64 WSL2 machine whose slow clock read inflated several ratios, so some headline numbers are smaller than in 0.3.0. Each README names the machine and runtime behind its tables.

Binding Generation vs. the platform's own call
Swift 9.5–13x faster than Foundation.UUID()
C# 5.7–8.1x faster than Guid.NewGuid()
Ruby 2.1–4.9x faster than SecureRandom.uuid
Python 3.0–4.2x faster than stdlib uuid
Java 2.4–4.5x faster than UUID.randomUUID()
Rust 2x faster than the uuid crate on v5/v7, level on v4/v6
PHP level to 1.2x faster than a naive inline v4
Go slower per call than google/uuid; use the batch doors

Upgrade notes

Runtime floors. Every floor that had reached end of life is raised. A consumer on an older runtime keeps resolving 0.3.0.

Floor
Python 3.11
Ruby 3.3
PHP 8.2
Java JDK 25
Swift 6.2

Behaviour changes.

  • Ruby, PHP: Uuid.parse accepts the 8-4-4-4-12 form only. Bare and misplaced-hyphen strings no longer parse.
  • Python: a datetime passed to new_v6/new_v7 is truncated to its millisecond. It used to be rounded and could stamp a UUID up to half a millisecond late.
  • Every binding: caller errors (out-of-range timestamps and counts, wrong types, null names) are now the same exception on every backend and name the mistake. Ruby's are HyperUuid::TimestampOutOfRangeError and RandomSourceError; the Runtime:: names remain as aliases.
  • Go: batch functions return ErrNegativeCount for a negative count. -tags hyperuuid_dynamic keeps the previous loading cgo backend.
  • C#: set HyperUuidStaticLink=false to keep the shared library under Native AOT. Blazor WebAssembly requires .NET 11; an older browser project gets warning HYPERUUID001 and no native link.
  • Java: the optional GraalWasm engine moves to 25.4.4.1.1. It must be the same release as the GraalVM JDK you run on.
  • Unknown architectures are no longer treated as x64. Go and PHP report an unsupported platform, Swift refuses to compile, and Java falls back to its wasm backend.
  • Intel macOS (osx-x64) still ships in every package. There is no precompiled x86_64-darwin gem; Ruby there runs on the Fiddle backend.

Fixes

  • An empty v5 name crossed the C ABI as a null pointer from C#, Go, Ruby (Fiddle) and Swift, which the core then built a slice from. Fixed in the core; every binding pins the empty-name vector.
  • Java: a batch count past Integer.MAX_VALUE / 16 wrote past a Java array. It is now IllegalArgumentException.
  • Python: new_v6_batch/new_v7_batch narrowed count to 32 bits, so 2**32 + 1 returned one UUID. Out of range is now ValueError.
  • Swift: newV6(_: Date)/newV7(_: Date) trapped on a date before 1970, and a missing resource bundle crashed the process. Both are thrown errors now.
  • Go: a core missing a symbol panicked on the purego backend.
  • Ruby: on Alpine, 0.3.0 loaded the glibc library and the first call raised Fiddle::DLError.
  • C#: a Blazor WebAssembly app could not use HyperUuid and HyperCast together (duplicate symbol at link time), failed in the browser on .NET 11, and got no native link when it reached the package through a class library.

Verifying

Every native library and static archive in this release is built by CI and carries a build-provenance attestation:

gh attestation verify go/native/linux-x64/libhyperuuid.so \
  --repo SkunkWerkx/HyperUuid --signer-repo SkunkWerkx/.github