hyperuuid-wasm 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -15
- data/ext/hyperuuid_native/3.4/hyperuuid_native.a.gz +0 -0
- data/ext/hyperuuid_native/4.0/hyperuuid_native.a.gz +0 -0
- data/lib/hyperuuid/runtime.rb +4 -0
- data/lib/hyperuuid/uuid.rb +4 -1
- data/lib/hyperuuid.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4acc2b5b222f7604e7b21efe1c5f958e7279a470f546bed3ad00089f033f13f
|
|
4
|
+
data.tar.gz: 0c9349c620020edf44cf37afbfff39480d49497ade4e63c94e9549e028ce2abb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c9f72f6b99806013e5c6557189c15f542ab1a4328528b07a2992770c01d4008111e1591b36cc00f567cdd6a919d89e6e81e7fd4bad8886bcfdcef10f7b1328c9
|
|
7
|
+
data.tar.gz: 6d6d7e0f7eecd6d4e62d6af9803090be5b76f90f3ee1d59534d409c92f5ee95b14d4c829af820485d3668108fbe2b500f0dc7385865de00f3a1315e225e22417
|
data/README.md
CHANGED
|
@@ -123,7 +123,7 @@ bytes = HyperUuid.new_v7_batch_bytes(1000)
|
|
|
123
123
|
first = bytes[0, 16] # ready for a BINARY(16) bind parameter
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
**About
|
|
126
|
+
**About 40x faster than `new_v7_batch`** for a 1000-UUID batch (4.9 µs versus 202 µs). The native call is identical — the difference is that `new_v7_batch` then allocates a `Uuid` object and its byte Strings for every item on top of it. This hands back the bytes the native core already produced, untouched.
|
|
127
127
|
|
|
128
128
|
The catch, and it inverts the advice: **if you need `Uuid` objects, keep using `new_v7_batch`.** Slicing these bytes into objects yourself just relocates the identical allocations into your own code, and measures no better — sometimes worse. Reach for the byte form only when bytes are the destination: a bind parameter, a wire format, a bulk load.
|
|
129
129
|
|
|
@@ -139,30 +139,30 @@ Real numbers, `benchmark-ips` on Ruby 4.0.7, linux-x64 on an Intel Core i9-11900
|
|
|
139
139
|
|
|
140
140
|
| Call | i/s | vs `SecureRandom.uuid` |
|
|
141
141
|
|---|---:|---:|
|
|
142
|
-
| `SecureRandom.uuid` |
|
|
143
|
-
| `HyperUuid.new_v4` |
|
|
144
|
-
| `HyperUuid.
|
|
145
|
-
| `HyperUuid.
|
|
146
|
-
| `HyperUuid.new_v6` (current time) | 3,
|
|
147
|
-
| `HyperUuid.new_v7` (current time) | 3,
|
|
148
|
-
| `HyperUuid.new_v5` | 1,
|
|
142
|
+
| `SecureRandom.uuid` | 943,917 | baseline |
|
|
143
|
+
| `HyperUuid.new_v4` | 5,236,000 | **5.5x faster** |
|
|
144
|
+
| `HyperUuid.new_v6` (explicit ms) | 3,811,000 | **4.0x faster** |
|
|
145
|
+
| `HyperUuid.new_v7` (explicit ms) | 3,785,000 | **4.0x faster** |
|
|
146
|
+
| `HyperUuid.new_v6` (current time) | 3,677,000 | **3.9x faster** |
|
|
147
|
+
| `HyperUuid.new_v7` (current time) | 3,574,000 | **3.8x faster** |
|
|
148
|
+
| `HyperUuid.new_v5` | 1,942,000 | 2.1x faster |
|
|
149
149
|
|
|
150
|
-
A `HyperUuid.new_v4` — real entropy, correct version/variant bits, minted by the shared Rust core — costs a fifth of what `SecureRandom.uuid` does, because the Magnus extension is an ordinary native method call with nothing marshalled around it.
|
|
150
|
+
A `HyperUuid.new_v4` — real entropy, correct version/variant bits, minted by the shared Rust core — costs less than a fifth of what `SecureRandom.uuid` does, because the Magnus extension is an ordinary native method call with nothing marshalled around it.
|
|
151
151
|
|
|
152
152
|
The "current time" rows deserve a footnote, because they will not look like this everywhere. Here they land beside the explicit-ms rows: the only difference between the two is one `Process.clock_gettime(CLOCK_REALTIME)` wall-clock read, and on this machine that read is too cheap to see. On a machine with a slow clock it is the whole story — where a virtualized clock defeats the vDSO fast path the same read costs ~1µs, which puts both current-time rows at parity with `SecureRandom.uuid` while the explicit-ms rows stay well ahead of it. `SecureRandom.uuid` never reads a clock — random v4 is the only thing it does. If your clock is slow and you are minting many, read it once and pass the timestamp, or use the batch doors.
|
|
153
153
|
|
|
154
|
-
The Fiddle fallback (`HYPERUUID_PURE=1`, and any platform without a prebuilt extension) keeps its own diet — a reused thread-local scratch buffer instead of two GC-finalizer-registering mallocs per call, zero-copy `String` passes for read-only inputs, an unsynchronized fast path past the load mutex — landing at 1.
|
|
154
|
+
The Fiddle fallback (`HYPERUUID_PURE=1`, and any platform without a prebuilt extension) keeps its own diet — a reused thread-local scratch buffer instead of two GC-finalizer-registering mallocs per call, zero-copy `String` passes for read-only inputs, an unsynchronized fast path past the load mutex — landing at 1.16x slower than `SecureRandom.uuid` for v4 (1.24 µs against 1.07 µs in its own run) and about 1.3x slower for v6/v7, with the same structural story as before: `Fiddle`'s interpreted marshalling is the floor, and the batch doors are how you amortize it.
|
|
155
155
|
|
|
156
156
|
Batch generation still amortizes per-call cost on both backends — one native call for the whole batch:
|
|
157
157
|
|
|
158
158
|
| Call | i/s (Magnus backend) |
|
|
159
159
|
|---|---:|
|
|
160
|
-
| `new_v6` × 1000 (individual) | 3,
|
|
161
|
-
| `new_v6_batch(1000)` | 4,
|
|
162
|
-
| `new_v7` × 1000 (individual) | 3,
|
|
163
|
-
| `new_v7_batch(1000)` | 4,
|
|
160
|
+
| `new_v6` × 1000 (individual) | 3,761 |
|
|
161
|
+
| `new_v6_batch(1000)` | 4,857 (**1.3x**) |
|
|
162
|
+
| `new_v7` × 1000 (individual) | 3,742 |
|
|
163
|
+
| `new_v7_batch(1000)` | 4,850 (**1.3x**) |
|
|
164
164
|
|
|
165
|
-
The multiplier is small on this backend for the best reason available: the individual calls are cheap, so there is little waste left to amortize, and what `new_v7_batch` spends its
|
|
165
|
+
The multiplier is small on this backend for the best reason available: the individual calls are cheap, so there is little waste left to amortize, and what `new_v7_batch` spends its 206 µs on is building a thousand `Uuid` objects — the byte form above does the same native work in 5 µs. On the Fiddle backend, where each call costs 1.4 µs, the same batch is 6.7x the loop. If you need v5/v6/v7, need many at once, or need this Ruby service's IDs to agree byte-for-byte with a Go or Python service's, that's what this gem is for — and now it's the fast option too, not just the capable one.
|
|
166
166
|
|
|
167
167
|
## Backends
|
|
168
168
|
|
|
Binary file
|
|
Binary file
|
data/lib/hyperuuid/runtime.rb
CHANGED
|
@@ -38,6 +38,7 @@ module HyperUuid
|
|
|
38
38
|
out = scratch
|
|
39
39
|
rc = functions[:new_v4].call(out)
|
|
40
40
|
raise random_source_failure("uuid_new_v4") unless rc.zero?
|
|
41
|
+
|
|
41
42
|
out[0, 16]
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -49,6 +50,7 @@ module HyperUuid
|
|
|
49
50
|
# contract.
|
|
50
51
|
rc = functions[:new_v5].call(namespace_bytes, name_bytes, name_bytes.bytesize, out)
|
|
51
52
|
raise random_source_failure("uuid_new_v5") unless rc.zero?
|
|
53
|
+
|
|
52
54
|
out[0, 16]
|
|
53
55
|
end
|
|
54
56
|
|
|
@@ -68,6 +70,7 @@ module HyperUuid
|
|
|
68
70
|
|
|
69
71
|
def new_v6_batch(count, unix_millis)
|
|
70
72
|
return "" if count.zero?
|
|
73
|
+
|
|
71
74
|
out = buffer(count * 16)
|
|
72
75
|
rc = functions[:new_v6_batch].call(unix_millis, count, out)
|
|
73
76
|
case rc
|
|
@@ -93,6 +96,7 @@ module HyperUuid
|
|
|
93
96
|
|
|
94
97
|
def new_v7_batch(count, unix_millis)
|
|
95
98
|
return "" if count.zero?
|
|
99
|
+
|
|
96
100
|
out = buffer(count * 16)
|
|
97
101
|
rc = functions[:new_v7_batch].call(unix_millis, count, out)
|
|
98
102
|
case rc
|
data/lib/hyperuuid/uuid.rb
CHANGED
|
@@ -82,7 +82,9 @@ module HyperUuid
|
|
|
82
82
|
when 6 then Runtime.v6_unix_millis(bytes)
|
|
83
83
|
when 7 then Runtime.v7_unix_millis(bytes)
|
|
84
84
|
else
|
|
85
|
-
raise ArgumentError,
|
|
85
|
+
raise ArgumentError,
|
|
86
|
+
"timestamp is only defined for version 6 or 7 UUIDs, got version #{version}" if raise_on_mismatch
|
|
87
|
+
|
|
86
88
|
return nil
|
|
87
89
|
end
|
|
88
90
|
Time.at(millis / 1000, millis % 1000, :millisecond).utc
|
|
@@ -168,6 +170,7 @@ module HyperUuid
|
|
|
168
170
|
# Byte-order comparison against +other+, or +nil+ if +other+ isn't a Uuid.
|
|
169
171
|
def <=>(other)
|
|
170
172
|
return nil unless other.is_a?(Uuid)
|
|
173
|
+
|
|
171
174
|
bytes <=> other.bytes
|
|
172
175
|
end
|
|
173
176
|
|
data/lib/hyperuuid.rb
CHANGED
|
@@ -18,7 +18,7 @@ require_relative "hyperuuid/runtime"
|
|
|
18
18
|
module HyperUuid
|
|
19
19
|
# This gem's own version — distinct from the RFC 9562 UUID *versions* (v4/v5/v6/v7) the
|
|
20
20
|
# rest of this module generates.
|
|
21
|
-
VERSION = "0.
|
|
21
|
+
VERSION = "0.7.0"
|
|
22
22
|
|
|
23
23
|
# The widest batch count, and the longest v5 name in bytes, the native ABI carries (a u32).
|
|
24
24
|
# (The millisecond count is a u64; unix_millis_from refuses anything wider.)
|