static_embeddings 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a6d1a316162408c80a3de7afce3e1bc1f6e7cce0707c58df8737c86e0748183
4
- data.tar.gz: c6e117117e09740437da333ec7203b62695b53233d5f64caa8cd15cc16f82d7c
3
+ metadata.gz: 250b847231ef37114e5c1c6b12076565eaa42d6193c305b0b107b424b12b3397
4
+ data.tar.gz: e59f63626b715759a286c25949b3c03bed78bc58744085716b96cd96a8fa8f98
5
5
  SHA512:
6
- metadata.gz: 274b82bff6ffbe3f650687b9e30a611c919ffeedea859d98864857d095100f0142f3ed56954b9420eddf17ab9aac1d879dcf7648539af9a852c4d4a08df150c3
7
- data.tar.gz: e9f11243e8e73ea99ab215620fa024b7cf78ccae8c5e9b2bb1d06dd88e60862fadaa37b8d2fb56b2251740a474c5d556f262e827b35ec898b1dfeadac938fc3d
6
+ metadata.gz: 71f7a2e68300934fe1362c5b8f546312ced1424dc0616a752400d8d48c9d80a1b82e053592bcf882e24f3a0ba7a10044b7b17a23a2bb5ea6bbe6f8dfcb896e97
7
+ data.tar.gz: 957e006602a759274873d894cbaf40a50018af531a0a9cd39806ef5eea8f6159b59b4ebba3e4bc2f8daaf3fe6b89f3ed4eeb9e10d28a62eef1057a6e363226c1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,126 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.2 (unreleased)
4
+
5
+ A tokenizer correctness fix, the ASCII fast path it made safe to write, and an
6
+ optional build that accounts for the runtime's own C heap.
7
+
8
+ External parity against `model2vec.StaticModel` has **not** been re-run for this
9
+ version. The DEL fix below changes token ids for any input containing `U+007F`,
10
+ so the 0.1.1 audit record no longer covers the shipping runtime. See
11
+ `docs/MODEL_AUDIT.md`; that re-run is the release blocker.
12
+
13
+ ### Fixed
14
+
15
+ - **`U+007F DEL` was not treated as a control character.** `is_control()`
16
+ matched `cp < 0x20` only, so DEL survived `clean_text` and stayed a word byte
17
+ while the reference normalizer drops it. The effect was not one token: the
18
+ word carrying DEL fell out of the vocabulary and collapsed to `[UNK]`.
19
+ A 20 000-case differential fuzz against `StaticEmbeddings::Reference` produced
20
+ 4 084 mismatches before the fix and 0 after; an exhaustive sweep of
21
+ `0x00..0x7F` shows DEL was the only divergent ASCII byte.
22
+
23
+ - **`embed_batch(format: :f16)` allocated a second output buffer.** The f32
24
+ batch buffer was encoded into a freshly allocated half buffer, so an f16 call
25
+ peaked at 1.5x the f32 output and cost more transient C heap than f32 for the
26
+ same work. It now encodes in place. Output is byte-identical; on 5000 x 512
27
+ the transient peak drops from 16.4 MB to 11.3 MB, the same peak the f32 call
28
+ has. Throughput is unchanged: the run-to-run spread on that sample is wider
29
+ than any difference the change makes.
30
+
31
+ ### Added
32
+
33
+ - **ASCII fast path in the tokenizer.** For `cp < 0x80` the normalizer chain
34
+ resolves the CJK, NFD, combining-mark and case-folding branches identically
35
+ every time, so ASCII runs now go through a 128-entry classification table and
36
+ reserve their codepoint buffer once per word instead of once per character.
37
+ Output is unchanged: the token-stream digest over 20 000 fuzz cases is
38
+ identical with and without it. Roughly 1.7x on `tokenize` and 1.2x on `embed`
39
+ for short English text, 2x on `tokenize` with `max_tokens: false`. Non-ASCII
40
+ input is unaffected.
41
+
42
+ - **`test/ascii_parity_test.rb`.** Exhaustive `0x00..0x7F` sweep in six
43
+ contexts, boundary codepoints across the ASCII/Unicode edge, deterministic
44
+ differential fuzz against `Reference` (`FUZZ_N`, `FUZZ_SEED`), truncation
45
+ boundaries, and DEL walked across the prefix window so the `embed` path is
46
+ covered and not just `tokenize`. Four of its cases fail on 0.1.1.
47
+
48
+ - **`test/validate_encoding_test.rb`** and verify coverage in
49
+ `test/format_test.rb`: mode agreement on valid input, invalid bytes inside
50
+ and past the window, cached-broken coderange, non-UTF-8 encodings, batch
51
+ index reporting, tamper detection, truncated and foreign files, and an
52
+ allocation bound proving `verify` does not hold the file in memory.
53
+
54
+ - **`test/cancellation_timing_test.rb`.** Measures how far past a `Timeout`
55
+ deadline `embed` keeps running. It is excluded from `rake test` because it is
56
+ timing-sensitive; run it with `rake cancellation_timing`.
57
+
58
+ - **`validate_encoding: :full | :prefix`** on `embed`, `embed_batch`,
59
+ `embed_with_stats` and `tokenize`. `embed` has to establish that its `String`
60
+ is valid UTF-8, and when Ruby has not computed the coderange yet that scan is
61
+ O(total bytes) and runs before the prefix window is chosen — so the
62
+ large-input path was not actually bounded on a freshly read document.
63
+ `:prefix` skips the up-front scan and lets the tokenizer validate the bytes it
64
+ reads: 442 µs to 78 µs on a 3 MB string, against 71 µs for the same string
65
+ with its coderange cached. `:full` stays the default, because `:prefix`
66
+ changes behaviour — invalid bytes past the truncation window are no longer
67
+ seen. A coderange Ruby has already computed is honoured in both modes.
68
+
69
+ - **Optional C allocation accounting.** Building with
70
+ `STATIC_EMBEDDINGS_ALLOC_STATS=1` wraps the runtime's own allocations and
71
+ exposes per-category bytes and counts, which the sample harness prints as
72
+ `c_alloc.<category>.<metric>`. Default builds do not define the internal
73
+ methods and do not pay for the counters. See `docs/PERFORMANCE.md`.
74
+
75
+ - **CI jobs `parity_fuzz`, `timing`, `alloc_stats` and `windows_loader`,** plus
76
+ a sanitizer build of the instrumented allocator inside the existing `memory`
77
+ job. The Windows job cross-compiles with mingw-w64 and runs the result under
78
+ wine, because that branch of `se_model_open` is the one path no other job
79
+ builds. `alloc_stats` is separate because it has to `rake clobber` first, and
80
+ clobber removes `tmp/`.
81
+
82
+ ### Changed
83
+
84
+ - **The ASCII fast path checks the cancellation flag** on the same cadence as
85
+ the main loop. A run of ASCII bytes is consumed inside one C call, so without
86
+ the check a `max_tokens: false` call on a large document ignored its deadline
87
+ until the whole document was tokenized: 0.33 s against a 0.05 s `Timeout` on a
88
+ 16 MB input, versus 0.05 s now.
89
+
90
+ - **`ext/static_embeddings/static_embeddings.c` was split.** The f16 codec and
91
+ kernels moved to `se_f16.c`, the top-k kernels to `se_topk.c`, and the
92
+ allocation counters live in `se_alloc_stats.c`. Shared overflow-checked size
93
+ helpers, `static_assert`s on the mmapped struct layouts, and an explicit
94
+ rejection of big-endian hosts moved into `se_internal.h`.
95
+
96
+ - **`Format.verify` streams the file.** It read the whole model and then
97
+ duplicated it to zero the checksum field, so verifying a 135 MB artifact
98
+ needed roughly 270 MB of transient `String` before hashing started. Peak is
99
+ now one 1 MiB chunk: measured on a 62 MB model, 433 ms and 280 MB of peak RSS
100
+ down to 249 ms and 30 MB.
101
+
102
+ - **Windows maps the model instead of reading it into the heap.** The POSIX
103
+ loader has always used `mmap`; Windows did `fopen` plus `fread` into a
104
+ private allocation, so every process paid a full read before the first query
105
+ and a private copy of the whole model. It now uses
106
+ `CreateFileMapping`/`MapViewOfFile`, with the old read path kept as a
107
+ fallback for filesystems that refuse to map. Verified by cross-compiling with
108
+ mingw-w64 and running under wine against a real `.semb`: same `map_size`,
109
+ same vectors as the POSIX build, and `mapped=1`.
110
+
111
+ - **Throughput figures separate logical from processed bytes.**
112
+ `tools/benchmark.rb` reports how many texts were truncated and refuses a
113
+ ns/byte figure when truncation makes the two differ; the large-input samples
114
+ print a measured `processed_input_mb_per_sec` alongside the logical one.
115
+
116
+ - **`samples/run_all.sh` captures `rake test`** into `00_test.log`, so a run
117
+ directory can back both "samples green" and "tests green". `TEST=0` skips it.
118
+
119
+ - **Documentation.** The README no longer implies `embed` is constant-time in
120
+ input size (the prefix window bounds the tokenizer, not the UTF-8 validity
121
+ scan) and no longer claims `f16` is faster, since this repository's own
122
+ samples show it winning on x86-64 F16C and losing on M1 Pro.
123
+
3
124
  ## 0.1.1 (unreleased)
4
125
 
5
126
  Safety and correctness release. Everything here was found by re-reviewing 0.1.0