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.
@@ -15,25 +15,6 @@
15
15
  #include <stdlib.h>
16
16
  #include <string.h>
17
17
 
18
- #if defined(__ARM_NEON) || defined(__ARM_NEON__)
19
- #include <arm_neon.h>
20
- #define SE_HAVE_NEON 1
21
- #if defined(__aarch64__)
22
- #define SE_HAVE_NEON_FP16 1
23
- #endif
24
- #elif defined(__SSE__)
25
- #include <xmmintrin.h>
26
- #define SE_HAVE_SSE 1
27
- #endif
28
-
29
- #if defined(__x86_64__) || defined(__i386__)
30
- #if defined(__GNUC__) || defined(__clang__)
31
- #include <cpuid.h>
32
- #include <immintrin.h>
33
- #define SE_HAVE_X86_F16C_TARGET 1
34
- #endif
35
- #endif
36
-
37
18
  #include "se_internal.h"
38
19
 
39
20
  #if defined(__GNUC__) || defined(__clang__)
@@ -50,29 +31,6 @@
50
31
  #define SE_PREFIX_MIN_BYTES 4096
51
32
  #define SE_PREFIX_MAX_BYTES 65536
52
33
  #define SE_PREFIX_BACKSCAN_BYTES 8192
53
- #define SE_SIZE_MAX ((size_t)-1)
54
-
55
- typedef enum { SE_VECTOR_FORMAT_F32 = 1, SE_VECTOR_FORMAT_F16 = 2 } se_vector_format_t;
56
-
57
- typedef enum {
58
- SE_F16_BACKEND_LUT = 0,
59
- SE_F16_BACKEND_NEON_FP16 = 1,
60
- SE_F16_BACKEND_F16C = 2
61
- } se_f16_backend_t;
62
-
63
- static se_f16_backend_t se_f16_backend = SE_F16_BACKEND_LUT;
64
-
65
- #if !defined(SE_HAVE_NEON_FP16)
66
- #define SE_NEED_F16_LUT 1
67
- /* 256 KB of BSS, and only for builds that can fall back to it. It is populated
68
- * lazily by select_f16_backend, so an x86 machine with F16C never touches these
69
- * pages either. AArch64 does not compile the table at all. */
70
- static float se_f16_lut[65536];
71
- #endif
72
-
73
- static size_t vector_format_element_bytes(se_vector_format_t format) {
74
- return format == SE_VECTOR_FORMAT_F16 ? 2u : sizeof(float);
75
- }
76
34
 
77
35
  static int string_equals_literal(VALUE str, const char *lit) {
78
36
  size_t n = strlen(lit);
@@ -101,147 +59,6 @@ static se_vector_format_t resolve_vector_format(VALUE opt) {
101
59
  opt);
102
60
  }
103
61
 
104
- static void write_u16le(uint8_t *dst, uint16_t v) {
105
- dst[0] = (uint8_t)(v & 0xffu);
106
- dst[1] = (uint8_t)(v >> 8);
107
- }
108
-
109
- static uint16_t read_u16le(const uint8_t *src) {
110
- return (uint16_t)src[0] | ((uint16_t)src[1] << 8);
111
- }
112
-
113
- static uint16_t float_to_f16_bits(float value) {
114
- uint32_t bits;
115
- memcpy(&bits, &value, sizeof(bits));
116
-
117
- uint32_t sign = (bits >> 16) & 0x8000u;
118
- uint32_t exp = (bits >> 23) & 0xffu;
119
- uint32_t mant = bits & 0x7fffffu;
120
-
121
- if (exp == 0xffu) {
122
- if (mant == 0)
123
- return (uint16_t)(sign | 0x7c00u);
124
- mant >>= 13;
125
- return (uint16_t)(sign | 0x7c00u | mant | (mant == 0));
126
- }
127
-
128
- int32_t half_exp = (int32_t)exp - 127 + 15;
129
- if (half_exp >= 31)
130
- return (uint16_t)(sign | 0x7c00u);
131
-
132
- if (half_exp <= 0) {
133
- if (half_exp < -10)
134
- return (uint16_t)sign;
135
- mant |= 0x800000u;
136
- uint32_t shift = (uint32_t)(14 - half_exp);
137
- uint32_t rounded = (mant + (1u << (shift - 1))) >> shift;
138
- return (uint16_t)(sign | rounded);
139
- }
140
-
141
- mant += 0x1000u;
142
- if (mant & 0x800000u) {
143
- mant = 0;
144
- half_exp++;
145
- if (half_exp >= 31)
146
- return (uint16_t)(sign | 0x7c00u);
147
- }
148
-
149
- return (uint16_t)(sign | ((uint32_t)half_exp << 10) | (mant >> 13));
150
- }
151
-
152
- static float f16_bits_to_float(uint16_t half) {
153
- uint32_t sign = ((uint32_t)half & 0x8000u) << 16;
154
- uint32_t exp = ((uint32_t)half >> 10) & 0x1fu;
155
- uint32_t mant = (uint32_t)half & 0x03ffu;
156
- uint32_t bits;
157
-
158
- if (exp == 0) {
159
- if (mant == 0) {
160
- bits = sign;
161
- } else {
162
- exp = 1;
163
- while ((mant & 0x0400u) == 0) {
164
- mant <<= 1;
165
- exp--;
166
- }
167
- mant &= 0x03ffu;
168
- bits = sign | ((exp + (127 - 15)) << 23) | (mant << 13);
169
- }
170
- } else if (exp == 31) {
171
- bits = sign | 0x7f800000u | (mant << 13);
172
- } else {
173
- bits = sign | ((exp + (127 - 15)) << 23) | (mant << 13);
174
- }
175
-
176
- float value;
177
- memcpy(&value, &bits, sizeof(value));
178
- return value;
179
- }
180
-
181
- static void encode_f16_from_floats(uint8_t *dst, const float *src, size_t count) {
182
- for (size_t i = 0; i < count; i++)
183
- write_u16le(dst + i * 2, float_to_f16_bits(src[i]));
184
- }
185
-
186
- static void decode_f16_to_floats(float *dst, const uint8_t *src, size_t count) {
187
- for (size_t i = 0; i < count; i++)
188
- dst[i] = f16_bits_to_float(read_u16le(src + i * 2));
189
- }
190
-
191
- #if defined(SE_NEED_F16_LUT)
192
- static void init_f16_lut(void) {
193
- for (uint32_t i = 0; i <= 0xffffu; i++)
194
- se_f16_lut[i] = f16_bits_to_float((uint16_t)i);
195
- }
196
- #endif
197
-
198
- #if defined(SE_HAVE_X86_F16C_TARGET)
199
- #ifndef bit_OSXSAVE
200
- #define bit_OSXSAVE (1u << 27)
201
- #endif
202
- #ifndef bit_AVX
203
- #define bit_AVX (1u << 28)
204
- #endif
205
- #ifndef bit_F16C
206
- #define bit_F16C (1u << 29)
207
- #endif
208
-
209
- static int detect_x86_f16c(void) {
210
- unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
211
- if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx))
212
- return 0;
213
- if ((ecx & bit_OSXSAVE) == 0 || (ecx & bit_AVX) == 0 || (ecx & bit_F16C) == 0)
214
- return 0;
215
-
216
- uint32_t xcr0_lo = 0, xcr0_hi = 0;
217
- #if defined(_MSC_VER)
218
- return 0;
219
- #else
220
- __asm__ volatile("xgetbv" : "=a"(xcr0_lo), "=d"(xcr0_hi) : "c"(0));
221
- (void)xcr0_hi;
222
- return (xcr0_lo & 0x6u) == 0x6u;
223
- #endif
224
- }
225
- #endif
226
-
227
- static int checked_add_size(size_t a, size_t b, size_t *out) {
228
- if (a > SE_SIZE_MAX - b)
229
- return 0;
230
- *out = a + b;
231
- return 1;
232
- }
233
-
234
- static int checked_mul_size(size_t a, size_t b, size_t *out) {
235
- if (a != 0 && b > SE_SIZE_MAX / a)
236
- return 0;
237
- *out = a * b;
238
- return 1;
239
- }
240
-
241
- static int size_fits_long(size_t n) {
242
- return n <= (size_t)LONG_MAX;
243
- }
244
-
245
62
  static VALUE mStaticEmbeddings;
246
63
  static VALUE cModel;
247
64
  static VALUE cFiber;
@@ -266,6 +83,9 @@ static ID id_unk_count;
266
83
  static ID id_truncated;
267
84
  static ID id_dim;
268
85
  static ID id_allow_unfrozen;
86
+ static ID id_validate_encoding;
87
+ static ID id_full;
88
+ static ID id_prefix;
269
89
 
270
90
  RUBY_FUNC_EXPORTED void Init_static_embeddings(void);
271
91
 
@@ -327,7 +147,24 @@ static void raise_se(const se_error_t *err) {
327
147
  rb_raise(error_class_for(err->status), "%s", err->message);
328
148
  }
329
149
 
330
- static void check_text_encoding_at(VALUE str, long index) {
150
+ static VALUE se_simd_backend(VALUE self) {
151
+ (void)self;
152
+ switch (se_current_f16_backend()) {
153
+ case SE_F16_BACKEND_NEON_FP16:
154
+ return rb_str_new_cstr("neon-fp16");
155
+ case SE_F16_BACKEND_F16C:
156
+ return rb_str_new_cstr("f16c");
157
+ default:
158
+ return rb_str_new_cstr("lut");
159
+ }
160
+ }
161
+
162
+ typedef enum {
163
+ SE_VALIDATE_ENCODING_FULL = 0,
164
+ SE_VALIDATE_ENCODING_PREFIX = 1
165
+ } se_encoding_validation_t;
166
+
167
+ static void check_text_encoding_mode(VALUE str, long index, se_encoding_validation_t mode) {
331
168
  rb_encoding *enc = rb_enc_get(str);
332
169
  if (enc != utf8_encoding && enc != rb_usascii_encoding()) {
333
170
  if (index >= 0) {
@@ -338,7 +175,14 @@ static void check_text_encoding_at(VALUE str, long index) {
338
175
  rb_raise(eEncodingError, "expected UTF-8 or US-ASCII, got %s (transcode explicitly)",
339
176
  rb_enc_name(enc));
340
177
  }
341
- int cr = rb_enc_str_coderange(str);
178
+
179
+ int cr = ENC_CODERANGE(str);
180
+ if (cr == ENC_CODERANGE_UNKNOWN) {
181
+ if (mode == SE_VALIDATE_ENCODING_PREFIX)
182
+ return;
183
+ cr = rb_enc_str_coderange(str);
184
+ }
185
+
342
186
  if (cr != ENC_CODERANGE_VALID && cr != ENC_CODERANGE_7BIT) {
343
187
  if (index >= 0)
344
188
  rb_raise(eEncodingError, "input[%ld]: string is not valid %s", index, rb_enc_name(enc));
@@ -346,8 +190,19 @@ static void check_text_encoding_at(VALUE str, long index) {
346
190
  }
347
191
  }
348
192
 
349
- static void check_text_encoding(VALUE str) {
350
- check_text_encoding_at(str, -1);
193
+ static se_encoding_validation_t resolve_encoding_validation(VALUE opt) {
194
+ if (opt == Qundef || NIL_P(opt))
195
+ return SE_VALIDATE_ENCODING_FULL;
196
+
197
+ if (SYMBOL_P(opt)) {
198
+ ID sym = SYM2ID(opt);
199
+ if (sym == id_full)
200
+ return SE_VALIDATE_ENCODING_FULL;
201
+ if (sym == id_prefix)
202
+ return SE_VALIDATE_ENCODING_PREFIX;
203
+ }
204
+
205
+ rb_raise(rb_eArgError, "validate_encoding: must be :full or :prefix");
351
206
  }
352
207
 
353
208
  typedef struct {
@@ -719,12 +574,13 @@ static VALUE text_at(const text_source_t *src, size_t i) {
719
574
  return RARRAY_AREF(src->snapshot, (long)i);
720
575
  }
721
576
 
722
- static VALUE snapshot_texts(VALUE texts, int is_array, size_t count) {
577
+ static VALUE snapshot_texts(VALUE texts, int is_array, size_t count,
578
+ se_encoding_validation_t validation) {
723
579
  VALUE snapshot = rb_ary_new_capa((long)count);
724
580
  for (size_t i = 0; i < count; i++) {
725
581
  VALUE s = is_array ? rb_ary_entry(texts, (long)i) : texts;
726
582
  Check_Type(s, T_STRING);
727
- check_text_encoding_at(s, is_array ? (long)i : -1);
583
+ check_text_encoding_mode(s, is_array ? (long)i : -1, validation);
728
584
  rb_ary_push(snapshot, s);
729
585
  }
730
586
  return snapshot;
@@ -737,24 +593,26 @@ static int build_input_indexed(const text_source_t *src, const size_t *indices,
737
593
 
738
594
  size_t total = 0;
739
595
  for (size_t j = 0; j < count; j++) {
740
- if (!checked_add_size(total, copy_lens[j], &total))
596
+ if (!se_checked_add_size(total, copy_lens[j], &total))
741
597
  return 0;
742
598
  }
743
599
 
744
- size_t offsets_bytes;
745
- size_t lengths_bytes;
746
- size_t meta_bytes;
747
- size_t allocation_bytes;
748
- size_t slots = count ? count : 1;
749
-
750
- if (!checked_mul_size(slots, sizeof(size_t), &offsets_bytes) ||
751
- !checked_mul_size(slots, sizeof(size_t), &lengths_bytes) ||
752
- !checked_add_size(offsets_bytes, lengths_bytes, &meta_bytes) ||
753
- !checked_add_size(meta_bytes, total ? total : 1, &allocation_bytes)) {
600
+ size_t offsets_bytes = 0;
601
+ size_t lengths_bytes = 0;
602
+ size_t meta_bytes = 0;
603
+ size_t allocation_bytes = 0;
604
+ size_t payload_bytes = total;
605
+ size_t malloc_bytes = 0;
606
+
607
+ if (!se_array_bytes(count, sizeof(size_t), &offsets_bytes) ||
608
+ !se_array_bytes(count, sizeof(size_t), &lengths_bytes) ||
609
+ !se_checked_add_size(offsets_bytes, lengths_bytes, &meta_bytes) ||
610
+ !se_checked_add_size(meta_bytes, payload_bytes, &allocation_bytes) ||
611
+ !se_alloc_bytes(allocation_bytes, 1u, &malloc_bytes)) {
754
612
  return 0;
755
613
  }
756
614
 
757
- uint8_t *allocation = (uint8_t *)malloc(allocation_bytes);
615
+ uint8_t *allocation = (uint8_t *)se_malloc(SE_ALLOC_BATCH_INPUT, malloc_bytes);
758
616
  if (!allocation)
759
617
  return 0;
760
618
 
@@ -783,7 +641,7 @@ static int build_input_indexed(const text_source_t *src, const size_t *indices,
783
641
  }
784
642
 
785
643
  static void free_input(batch_input_t *input) {
786
- free(input->allocation);
644
+ se_free(input->allocation);
787
645
  memset(input, 0, sizeof(*input));
788
646
  }
789
647
 
@@ -798,8 +656,8 @@ static VALUE binary_string_create(VALUE arg) {
798
656
  }
799
657
 
800
658
  static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
801
- if (!size_fits_long(bytes)) {
802
- free(ptr);
659
+ if (!se_size_fits_long(bytes)) {
660
+ se_free(ptr);
803
661
  rb_raise(rb_eArgError, "embedding output is too large");
804
662
  }
805
663
 
@@ -809,7 +667,7 @@ static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
809
667
 
810
668
  int state = 0;
811
669
  VALUE result = rb_protect(binary_string_create, (VALUE)(uintptr_t)&job, &state);
812
- free(ptr);
670
+ se_free(ptr);
813
671
  if (state)
814
672
  rb_jump_tag(state);
815
673
  return result;
@@ -817,23 +675,17 @@ static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
817
675
 
818
676
  static VALUE binary_string_from_floats(float *ptr, size_t count, se_vector_format_t format) {
819
677
  size_t bytes;
820
- if (!checked_mul_size(count, vector_format_element_bytes(format), &bytes) ||
821
- !size_fits_long(bytes)) {
822
- free(ptr);
678
+ if (!se_checked_mul_size(count, se_vector_format_element_bytes(format), &bytes) ||
679
+ !se_size_fits_long(bytes)) {
680
+ se_free(ptr);
823
681
  rb_raise(rb_eArgError, "embedding output is too large");
824
682
  }
825
683
 
826
684
  if (format == SE_VECTOR_FORMAT_F32)
827
685
  return binary_string_from_malloc(ptr, bytes);
828
686
 
829
- uint8_t *encoded = (uint8_t *)malloc(bytes ? bytes : 1);
830
- if (!encoded) {
831
- free(ptr);
832
- rb_raise(rb_eNoMemError, "out of memory while encoding embedding output");
833
- }
834
- encode_f16_from_floats(encoded, ptr, count);
835
- free(ptr);
836
- return binary_string_from_malloc(encoded, bytes);
687
+ se_encode_f16_from_floats((uint8_t *)ptr, ptr, count);
688
+ return binary_string_from_malloc(ptr, bytes);
837
689
  }
838
690
 
839
691
  typedef struct {
@@ -844,17 +696,47 @@ typedef struct {
844
696
  size_t *copy_lens;
845
697
  } embed_run_t;
846
698
 
699
+ typedef enum {
700
+ EMBED_RUN_ALLOC_OK = 0,
701
+ EMBED_RUN_ALLOC_OVERFLOW,
702
+ EMBED_RUN_ALLOC_OOM
703
+ } embed_run_alloc_status_t;
704
+
847
705
  static void embed_run_free(embed_run_t *run) {
848
- free(run->out);
849
- free(run->stats);
850
- free(run->targets);
851
- free(run->pending);
852
- free(run->copy_lens);
706
+ se_free(run->out);
707
+ se_free(run->stats);
708
+ se_free(run->targets);
709
+ se_free(run->pending);
710
+ se_free(run->copy_lens);
853
711
  memset(run, 0, sizeof(*run));
854
712
  }
855
713
 
714
+ static embed_run_alloc_status_t embed_run_alloc(embed_run_t *run, size_t count, size_t floats) {
715
+ size_t out_bytes = 0;
716
+ size_t stats_bytes = 0;
717
+ size_t targets_bytes = 0;
718
+
719
+ memset(run, 0, sizeof(*run));
720
+ if (!se_alloc_bytes(floats, sizeof(float), &out_bytes) ||
721
+ !se_alloc_bytes(count, sizeof(se_token_stats_t), &stats_bytes) ||
722
+ !se_alloc_bytes(count, sizeof(size_t), &targets_bytes))
723
+ return EMBED_RUN_ALLOC_OVERFLOW;
724
+
725
+ run->out = (float *)se_calloc(SE_ALLOC_BATCH_OUTPUT, 1, out_bytes);
726
+ run->stats = (se_token_stats_t *)se_calloc(SE_ALLOC_BATCH_STATS, 1, stats_bytes);
727
+ run->targets = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
728
+ run->pending = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
729
+ run->copy_lens = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
730
+ if (run->out && run->stats && run->targets && run->pending && run->copy_lens)
731
+ return EMBED_RUN_ALLOC_OK;
732
+
733
+ embed_run_free(run);
734
+ return EMBED_RUN_ALLOC_OOM;
735
+ }
736
+
856
737
  static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t count,
857
738
  VALUE max_tokens_opt, se_vector_format_t format,
739
+ se_encoding_validation_t validation,
858
740
  se_token_stats_t *stats_out) {
859
741
  model_wrapper_t *w = get_model(self);
860
742
  const se_model_t *model = &w->model;
@@ -862,29 +744,23 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
862
744
  const uint32_t max_tokens = resolve_max_tokens(model, max_tokens_opt);
863
745
 
864
746
  text_source_t source;
865
- source.snapshot = snapshot_texts(texts, is_array, count);
747
+ source.snapshot = snapshot_texts(texts, is_array, count, validation);
866
748
  source.is_array = is_array;
867
749
  const text_source_t *src = &source;
868
750
 
869
751
  size_t floats;
870
752
  size_t out_bytes;
871
- if (!checked_mul_size(count, dim, &floats) ||
872
- !checked_mul_size(floats, vector_format_element_bytes(format), &out_bytes) ||
873
- !size_fits_long(out_bytes))
753
+ if (!se_checked_mul_size(count, dim, &floats) ||
754
+ !se_checked_mul_size(floats, se_vector_format_element_bytes(format), &out_bytes) ||
755
+ !se_size_fits_long(out_bytes))
874
756
  rb_raise(rb_eArgError, "embedding output is too large");
875
757
 
876
758
  embed_run_t run;
877
- memset(&run, 0, sizeof(run));
878
- size_t slots = count ? count : 1;
879
- run.out = (float *)calloc(floats ? floats : 1, sizeof(float));
880
- run.stats = (se_token_stats_t *)calloc(slots, sizeof(se_token_stats_t));
881
- run.targets = (size_t *)calloc(slots, sizeof(size_t));
882
- run.pending = (size_t *)calloc(slots, sizeof(size_t));
883
- run.copy_lens = (size_t *)calloc(slots, sizeof(size_t));
884
- if (!run.out || !run.stats || !run.targets || !run.pending || !run.copy_lens) {
885
- embed_run_free(&run);
759
+ embed_run_alloc_status_t alloc_status = embed_run_alloc(&run, count, floats);
760
+ if (alloc_status == EMBED_RUN_ALLOC_OVERFLOW)
761
+ rb_raise(rb_eArgError, "embedding output is too large");
762
+ if (alloc_status == EMBED_RUN_ALLOC_OOM)
886
763
  rb_raise(rb_eNoMemError, "out of memory");
887
- }
888
764
 
889
765
  size_t initial = prefix_initial_target(max_tokens);
890
766
  size_t npending = count;
@@ -964,13 +840,13 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
964
840
  if (stats_out && count)
965
841
  *stats_out = run.stats[0];
966
842
 
967
- free(run.stats);
843
+ se_free(run.stats);
968
844
  run.stats = NULL;
969
- free(run.targets);
845
+ se_free(run.targets);
970
846
  run.targets = NULL;
971
- free(run.pending);
847
+ se_free(run.pending);
972
848
  run.pending = NULL;
973
- free(run.copy_lens);
849
+ se_free(run.copy_lens);
974
850
  run.copy_lens = NULL;
975
851
 
976
852
  float *out = run.out;
@@ -982,10 +858,10 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
982
858
  }
983
859
 
984
860
  static VALUE embed_batch_internal(VALUE self, VALUE texts, VALUE max_tokens_opt,
985
- se_vector_format_t format) {
861
+ se_vector_format_t format, se_encoding_validation_t validation) {
986
862
  Check_Type(texts, T_ARRAY);
987
863
  return embed_texts_internal(self, texts, 1, (size_t)RARRAY_LEN(texts), max_tokens_opt, format,
988
- NULL);
864
+ validation, NULL);
989
865
  }
990
866
 
991
867
  static VALUE model_embed_batch(int argc, VALUE *argv, VALUE self) {
@@ -995,26 +871,30 @@ static VALUE model_embed_batch(int argc, VALUE *argv, VALUE self) {
995
871
 
996
872
  VALUE max_tokens = lookup_option(opts, id_max_tokens);
997
873
  se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
998
- return embed_batch_internal(self, texts, max_tokens, format);
874
+ se_encoding_validation_t validation =
875
+ resolve_encoding_validation(lookup_option(opts, id_validate_encoding));
876
+ return embed_batch_internal(self, texts, max_tokens, format, validation);
999
877
  }
1000
878
 
1001
879
  static VALUE embed_one_via_batch(VALUE self, VALUE text, VALUE max_tokens_opt,
1002
- se_vector_format_t format, se_token_stats_t *stats) {
1003
- return embed_texts_internal(self, text, 0, 1, max_tokens_opt, format, stats);
880
+ se_vector_format_t format, se_encoding_validation_t validation,
881
+ se_token_stats_t *stats) {
882
+ return embed_texts_internal(self, text, 0, 1, max_tokens_opt, format, validation, stats);
1004
883
  }
1005
884
 
1006
885
  static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
1007
- se_vector_format_t format, se_token_stats_t *stats) {
886
+ se_vector_format_t format, se_encoding_validation_t validation,
887
+ se_token_stats_t *stats) {
1008
888
  model_wrapper_t *w = get_model(self);
1009
889
  Check_Type(text, T_STRING);
1010
- check_text_encoding(text);
890
+ check_text_encoding_mode(text, -1, validation);
1011
891
 
1012
892
  if (format != SE_VECTOR_FORMAT_F32 || (size_t)RSTRING_LEN(text) >= SE_GVL_UNLOCK_THRESHOLD)
1013
- return embed_one_via_batch(self, text, max_tokens_opt, format, stats);
893
+ return embed_one_via_batch(self, text, max_tokens_opt, format, validation, stats);
1014
894
 
1015
895
  const uint32_t dim = w->model.meta.dim;
1016
896
  size_t out_bytes;
1017
- if (!checked_mul_size(dim, sizeof(float), &out_bytes) || !size_fits_long(out_bytes))
897
+ if (!se_checked_mul_size(dim, sizeof(float), &out_bytes) || !se_size_fits_long(out_bytes))
1018
898
  rb_raise(rb_eArgError, "embedding output is too large");
1019
899
 
1020
900
  VALUE result = rb_str_new(NULL, (long)out_bytes);
@@ -1049,7 +929,9 @@ static VALUE model_embed(int argc, VALUE *argv, VALUE self) {
1049
929
  rb_scan_args(argc, argv, "1:", &text, &opts);
1050
930
  reject_parallel_threads(opts);
1051
931
  return embed_one_value(self, text, lookup_option(opts, id_max_tokens),
1052
- resolve_vector_format(lookup_option(opts, id_format)), NULL);
932
+ resolve_vector_format(lookup_option(opts, id_format)),
933
+ resolve_encoding_validation(lookup_option(opts, id_validate_encoding)),
934
+ NULL);
1053
935
  }
1054
936
 
1055
937
  static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
@@ -1058,8 +940,10 @@ static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
1058
940
  reject_parallel_threads(opts);
1059
941
 
1060
942
  se_token_stats_t stats;
1061
- VALUE vector = embed_one_value(self, text, lookup_option(opts, id_max_tokens),
1062
- resolve_vector_format(lookup_option(opts, id_format)), &stats);
943
+ VALUE vector = embed_one_value(
944
+ self, text, lookup_option(opts, id_max_tokens),
945
+ resolve_vector_format(lookup_option(opts, id_format)),
946
+ resolve_encoding_validation(lookup_option(opts, id_validate_encoding)), &stats);
1063
947
 
1064
948
  VALUE hash = rb_hash_new();
1065
949
  rb_hash_aset(hash, ID2SYM(id_vector), vector);
@@ -1075,7 +959,8 @@ static VALUE model_tokenize(int argc, VALUE *argv, VALUE self) {
1075
959
 
1076
960
  model_wrapper_t *w = get_model(self);
1077
961
  Check_Type(text, T_STRING);
1078
- check_text_encoding(text);
962
+ check_text_encoding_mode(
963
+ text, -1, resolve_encoding_validation(lookup_option(opts, id_validate_encoding)));
1079
964
 
1080
965
  uint32_t max_tokens = resolve_max_tokens(&w->model, lookup_option(opts, id_max_tokens));
1081
966
 
@@ -1183,7 +1068,11 @@ static VALUE embed_token_ids_body(VALUE arg) {
1183
1068
  run->ids[i] = (uint32_t)conv.value;
1184
1069
  }
1185
1070
 
1186
- run->out = (float *)calloc(run->model->meta.dim ? run->model->meta.dim : 1, sizeof(float));
1071
+ size_t out_bytes;
1072
+ if (!se_alloc_bytes(run->model->meta.dim, sizeof(float), &out_bytes))
1073
+ rb_raise(rb_eArgError, "embedding output is too large");
1074
+
1075
+ run->out = (float *)se_calloc(SE_ALLOC_BATCH_OUTPUT, 1, out_bytes);
1187
1076
  if (!run->out)
1188
1077
  rb_raise(rb_eNoMemError, "out of memory");
1189
1078
 
@@ -1214,7 +1103,7 @@ static VALUE embed_token_ids_body(VALUE arg) {
1214
1103
  if (run->stats_out)
1215
1104
  *run->stats_out = run->stats;
1216
1105
 
1217
- free(run->ids);
1106
+ se_free(run->ids);
1218
1107
  run->ids = NULL;
1219
1108
 
1220
1109
  float *out = run->out;
@@ -1224,8 +1113,8 @@ static VALUE embed_token_ids_body(VALUE arg) {
1224
1113
 
1225
1114
  static VALUE embed_token_ids_ensure(VALUE arg) {
1226
1115
  ids_run_t *run = (ids_run_t *)(uintptr_t)arg;
1227
- free(run->ids);
1228
- free(run->out);
1116
+ se_free(run->ids);
1117
+ se_free(run->out);
1229
1118
  run->ids = NULL;
1230
1119
  run->out = NULL;
1231
1120
  return Qnil;
@@ -1247,12 +1136,13 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
1247
1136
  }
1248
1137
 
1249
1138
  size_t ids_bytes;
1250
- if (!checked_mul_size(n ? n : 1, sizeof(uint32_t), &ids_bytes))
1139
+ if (!se_alloc_bytes(n, sizeof(uint32_t), &ids_bytes))
1251
1140
  rb_raise(rb_eArgError, "token id array is too large");
1252
1141
 
1253
1142
  size_t out_bytes;
1254
- if (!checked_mul_size(w->model.meta.dim, vector_format_element_bytes(format), &out_bytes) ||
1255
- !size_fits_long(out_bytes))
1143
+ if (!se_checked_mul_size(w->model.meta.dim, se_vector_format_element_bytes(format),
1144
+ &out_bytes) ||
1145
+ !se_size_fits_long(out_bytes))
1256
1146
  rb_raise(rb_eArgError, "embedding output is too large");
1257
1147
 
1258
1148
  ids_run_t run;
@@ -1265,7 +1155,7 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
1265
1155
  run.format = format;
1266
1156
  run.stats_out = stats_out;
1267
1157
  run.truncated = truncated;
1268
- run.ids = (uint32_t *)malloc(ids_bytes);
1158
+ run.ids = (uint32_t *)se_malloc(SE_ALLOC_TOKEN_IDS, ids_bytes);
1269
1159
  if (!run.ids)
1270
1160
  rb_raise(rb_eNoMemError, "out of memory");
1271
1161
 
@@ -1302,23 +1192,9 @@ static VALUE model_embed_token_ids_with_stats(int argc, VALUE *argv, VALUE self)
1302
1192
  return hash;
1303
1193
  }
1304
1194
 
1305
- typedef struct {
1306
- const float *q;
1307
- const void *m;
1308
- se_vector_format_t format;
1309
- size_t dim;
1310
- size_t rows;
1311
- long k;
1312
- size_t *best_idx;
1313
- float *best_score;
1314
- int cosine;
1315
- float inv_query_norm;
1316
- volatile sig_atomic_t cancelled;
1317
- } topk_job_t;
1318
-
1319
1195
  typedef struct {
1320
1196
  VALUE matrix;
1321
- topk_job_t job;
1197
+ se_topk_job_t job;
1322
1198
  float *q_copy;
1323
1199
  float *matrix_copy;
1324
1200
  size_t matrix_bytes;
@@ -1330,460 +1206,11 @@ static int ptr_is_float_aligned(const void *ptr) {
1330
1206
  }
1331
1207
 
1332
1208
  static void topk_unblock_cancel(void *arg) {
1333
- topk_job_t *job = (topk_job_t *)arg;
1209
+ se_topk_job_t *job = (se_topk_job_t *)arg;
1334
1210
  if (job)
1335
1211
  job->cancelled = 1;
1336
1212
  }
1337
1213
 
1338
- static float dot_product_unrolled(const float *q, const float *row, size_t dim) {
1339
- size_t j = 0;
1340
- #if defined(SE_HAVE_NEON)
1341
- float32x4_t a0 = vdupq_n_f32(0.0f);
1342
- float32x4_t a1 = vdupq_n_f32(0.0f);
1343
- float32x4_t a2 = vdupq_n_f32(0.0f);
1344
- float32x4_t a3 = vdupq_n_f32(0.0f);
1345
- for (; j + 15 < dim; j += 16) {
1346
- a0 = vmlaq_f32(a0, vld1q_f32(q + j), vld1q_f32(row + j));
1347
- a1 = vmlaq_f32(a1, vld1q_f32(q + j + 4), vld1q_f32(row + j + 4));
1348
- a2 = vmlaq_f32(a2, vld1q_f32(q + j + 8), vld1q_f32(row + j + 8));
1349
- a3 = vmlaq_f32(a3, vld1q_f32(q + j + 12), vld1q_f32(row + j + 12));
1350
- }
1351
- float32x4_t sumv = vaddq_f32(vaddq_f32(a0, a1), vaddq_f32(a2, a3));
1352
- #if defined(__aarch64__)
1353
- float dot = vaddvq_f32(sumv);
1354
- #else
1355
- float32x2_t pair = vadd_f32(vget_low_f32(sumv), vget_high_f32(sumv));
1356
- pair = vpadd_f32(pair, pair);
1357
- float dot = vget_lane_f32(pair, 0);
1358
- #endif
1359
- #elif defined(SE_HAVE_SSE)
1360
- __m128 a0 = _mm_setzero_ps();
1361
- __m128 a1 = _mm_setzero_ps();
1362
- __m128 a2 = _mm_setzero_ps();
1363
- __m128 a3 = _mm_setzero_ps();
1364
- for (; j + 15 < dim; j += 16) {
1365
- a0 = _mm_add_ps(a0, _mm_mul_ps(_mm_loadu_ps(q + j), _mm_loadu_ps(row + j)));
1366
- a1 = _mm_add_ps(a1, _mm_mul_ps(_mm_loadu_ps(q + j + 4), _mm_loadu_ps(row + j + 4)));
1367
- a2 = _mm_add_ps(a2, _mm_mul_ps(_mm_loadu_ps(q + j + 8), _mm_loadu_ps(row + j + 8)));
1368
- a3 = _mm_add_ps(a3, _mm_mul_ps(_mm_loadu_ps(q + j + 12), _mm_loadu_ps(row + j + 12)));
1369
- }
1370
- __m128 sumv = _mm_add_ps(_mm_add_ps(a0, a1), _mm_add_ps(a2, a3));
1371
- float tmp[4];
1372
- _mm_storeu_ps(tmp, sumv);
1373
- float dot = (tmp[0] + tmp[1]) + (tmp[2] + tmp[3]);
1374
- #else
1375
- float s0 = 0.0f;
1376
- float s1 = 0.0f;
1377
- float s2 = 0.0f;
1378
- float s3 = 0.0f;
1379
- float s4 = 0.0f;
1380
- float s5 = 0.0f;
1381
- float s6 = 0.0f;
1382
- float s7 = 0.0f;
1383
- for (; j + 7 < dim; j += 8) {
1384
- s0 += q[j] * row[j];
1385
- s1 += q[j + 1] * row[j + 1];
1386
- s2 += q[j + 2] * row[j + 2];
1387
- s3 += q[j + 3] * row[j + 3];
1388
- s4 += q[j + 4] * row[j + 4];
1389
- s5 += q[j + 5] * row[j + 5];
1390
- s6 += q[j + 6] * row[j + 6];
1391
- s7 += q[j + 7] * row[j + 7];
1392
- }
1393
- float dot = (s0 + s1) + (s2 + s3) + (s4 + s5) + (s6 + s7);
1394
- #endif
1395
- for (; j < dim; j++)
1396
- dot += q[j] * row[j];
1397
- return dot;
1398
- }
1399
-
1400
- static float dot_and_row_sq_unrolled(const float *q, const float *row, size_t dim,
1401
- float *row_sq_out) {
1402
- size_t j = 0;
1403
- #if defined(SE_HAVE_NEON)
1404
- float32x4_t d0 = vdupq_n_f32(0.0f);
1405
- float32x4_t d1 = vdupq_n_f32(0.0f);
1406
- float32x4_t d2 = vdupq_n_f32(0.0f);
1407
- float32x4_t d3 = vdupq_n_f32(0.0f);
1408
- float32x4_t s0 = vdupq_n_f32(0.0f);
1409
- float32x4_t s1 = vdupq_n_f32(0.0f);
1410
- float32x4_t s2 = vdupq_n_f32(0.0f);
1411
- float32x4_t s3 = vdupq_n_f32(0.0f);
1412
- for (; j + 15 < dim; j += 16) {
1413
- float32x4_t q0 = vld1q_f32(q + j);
1414
- float32x4_t r0 = vld1q_f32(row + j);
1415
- float32x4_t q1 = vld1q_f32(q + j + 4);
1416
- float32x4_t r1 = vld1q_f32(row + j + 4);
1417
- float32x4_t q2 = vld1q_f32(q + j + 8);
1418
- float32x4_t r2 = vld1q_f32(row + j + 8);
1419
- float32x4_t q3 = vld1q_f32(q + j + 12);
1420
- float32x4_t r3 = vld1q_f32(row + j + 12);
1421
- d0 = vmlaq_f32(d0, q0, r0);
1422
- d1 = vmlaq_f32(d1, q1, r1);
1423
- d2 = vmlaq_f32(d2, q2, r2);
1424
- d3 = vmlaq_f32(d3, q3, r3);
1425
- s0 = vmlaq_f32(s0, r0, r0);
1426
- s1 = vmlaq_f32(s1, r1, r1);
1427
- s2 = vmlaq_f32(s2, r2, r2);
1428
- s3 = vmlaq_f32(s3, r3, r3);
1429
- }
1430
- float32x4_t dotv = vaddq_f32(vaddq_f32(d0, d1), vaddq_f32(d2, d3));
1431
- float32x4_t sqv = vaddq_f32(vaddq_f32(s0, s1), vaddq_f32(s2, s3));
1432
- #if defined(__aarch64__)
1433
- float dot = vaddvq_f32(dotv);
1434
- float row_sq = vaddvq_f32(sqv);
1435
- #else
1436
- float32x2_t pair = vadd_f32(vget_low_f32(dotv), vget_high_f32(dotv));
1437
- pair = vpadd_f32(pair, pair);
1438
- float dot = vget_lane_f32(pair, 0);
1439
- pair = vadd_f32(vget_low_f32(sqv), vget_high_f32(sqv));
1440
- pair = vpadd_f32(pair, pair);
1441
- float row_sq = vget_lane_f32(pair, 0);
1442
- #endif
1443
- #elif defined(SE_HAVE_SSE)
1444
- __m128 d0 = _mm_setzero_ps();
1445
- __m128 d1 = _mm_setzero_ps();
1446
- __m128 d2 = _mm_setzero_ps();
1447
- __m128 d3 = _mm_setzero_ps();
1448
- __m128 s0 = _mm_setzero_ps();
1449
- __m128 s1 = _mm_setzero_ps();
1450
- __m128 s2 = _mm_setzero_ps();
1451
- __m128 s3 = _mm_setzero_ps();
1452
- for (; j + 15 < dim; j += 16) {
1453
- __m128 q0 = _mm_loadu_ps(q + j);
1454
- __m128 r0 = _mm_loadu_ps(row + j);
1455
- __m128 q1 = _mm_loadu_ps(q + j + 4);
1456
- __m128 r1 = _mm_loadu_ps(row + j + 4);
1457
- __m128 q2 = _mm_loadu_ps(q + j + 8);
1458
- __m128 r2 = _mm_loadu_ps(row + j + 8);
1459
- __m128 q3 = _mm_loadu_ps(q + j + 12);
1460
- __m128 r3 = _mm_loadu_ps(row + j + 12);
1461
- d0 = _mm_add_ps(d0, _mm_mul_ps(q0, r0));
1462
- d1 = _mm_add_ps(d1, _mm_mul_ps(q1, r1));
1463
- d2 = _mm_add_ps(d2, _mm_mul_ps(q2, r2));
1464
- d3 = _mm_add_ps(d3, _mm_mul_ps(q3, r3));
1465
- s0 = _mm_add_ps(s0, _mm_mul_ps(r0, r0));
1466
- s1 = _mm_add_ps(s1, _mm_mul_ps(r1, r1));
1467
- s2 = _mm_add_ps(s2, _mm_mul_ps(r2, r2));
1468
- s3 = _mm_add_ps(s3, _mm_mul_ps(r3, r3));
1469
- }
1470
- __m128 dotv = _mm_add_ps(_mm_add_ps(d0, d1), _mm_add_ps(d2, d3));
1471
- __m128 sqv = _mm_add_ps(_mm_add_ps(s0, s1), _mm_add_ps(s2, s3));
1472
- float tmp[4];
1473
- _mm_storeu_ps(tmp, dotv);
1474
- float dot = (tmp[0] + tmp[1]) + (tmp[2] + tmp[3]);
1475
- _mm_storeu_ps(tmp, sqv);
1476
- float row_sq = (tmp[0] + tmp[1]) + (tmp[2] + tmp[3]);
1477
- #else
1478
- float d0 = 0.0f;
1479
- float d1 = 0.0f;
1480
- float d2 = 0.0f;
1481
- float d3 = 0.0f;
1482
- float s0 = 0.0f;
1483
- float s1 = 0.0f;
1484
- float s2 = 0.0f;
1485
- float s3 = 0.0f;
1486
- for (; j + 3 < dim; j += 4) {
1487
- float r0 = row[j];
1488
- float r1 = row[j + 1];
1489
- float r2 = row[j + 2];
1490
- float r3 = row[j + 3];
1491
- d0 += q[j] * r0;
1492
- d1 += q[j + 1] * r1;
1493
- d2 += q[j + 2] * r2;
1494
- d3 += q[j + 3] * r3;
1495
- s0 += r0 * r0;
1496
- s1 += r1 * r1;
1497
- s2 += r2 * r2;
1498
- s3 += r3 * r3;
1499
- }
1500
- float dot = (d0 + d1) + (d2 + d3);
1501
- float row_sq = (s0 + s1) + (s2 + s3);
1502
- #endif
1503
- for (; j < dim; j++) {
1504
- float r = row[j];
1505
- dot += q[j] * r;
1506
- row_sq += r * r;
1507
- }
1508
- *row_sq_out = row_sq;
1509
- return dot;
1510
- }
1511
-
1512
- #if defined(SE_NEED_F16_LUT)
1513
- static float dot_product_f16_lut(const float *q, const uint8_t *row, size_t dim) {
1514
- size_t j = 0;
1515
- float s0 = 0.0f;
1516
- float s1 = 0.0f;
1517
- float s2 = 0.0f;
1518
- float s3 = 0.0f;
1519
- for (; j + 3 < dim; j += 4) {
1520
- s0 += q[j] * se_f16_lut[read_u16le(row + j * 2)];
1521
- s1 += q[j + 1] * se_f16_lut[read_u16le(row + (j + 1) * 2)];
1522
- s2 += q[j + 2] * se_f16_lut[read_u16le(row + (j + 2) * 2)];
1523
- s3 += q[j + 3] * se_f16_lut[read_u16le(row + (j + 3) * 2)];
1524
- }
1525
- float dot = (s0 + s1) + (s2 + s3);
1526
- for (; j < dim; j++)
1527
- dot += q[j] * se_f16_lut[read_u16le(row + j * 2)];
1528
- return dot;
1529
- }
1530
-
1531
- static float dot_and_row_sq_f16_lut(const float *q, const uint8_t *row, size_t dim,
1532
- float *row_sq_out) {
1533
- size_t j = 0;
1534
- float d0 = 0.0f;
1535
- float d1 = 0.0f;
1536
- float d2 = 0.0f;
1537
- float d3 = 0.0f;
1538
- float s0 = 0.0f;
1539
- float s1 = 0.0f;
1540
- float s2 = 0.0f;
1541
- float s3 = 0.0f;
1542
- for (; j + 3 < dim; j += 4) {
1543
- float r0 = se_f16_lut[read_u16le(row + j * 2)];
1544
- float r1 = se_f16_lut[read_u16le(row + (j + 1) * 2)];
1545
- float r2 = se_f16_lut[read_u16le(row + (j + 2) * 2)];
1546
- float r3 = se_f16_lut[read_u16le(row + (j + 3) * 2)];
1547
- d0 += q[j] * r0;
1548
- d1 += q[j + 1] * r1;
1549
- d2 += q[j + 2] * r2;
1550
- d3 += q[j + 3] * r3;
1551
- s0 += r0 * r0;
1552
- s1 += r1 * r1;
1553
- s2 += r2 * r2;
1554
- s3 += r3 * r3;
1555
- }
1556
- float dot = (d0 + d1) + (d2 + d3);
1557
- float row_sq = (s0 + s1) + (s2 + s3);
1558
- for (; j < dim; j++) {
1559
- float r = se_f16_lut[read_u16le(row + j * 2)];
1560
- dot += q[j] * r;
1561
- row_sq += r * r;
1562
- }
1563
- *row_sq_out = row_sq;
1564
- return dot;
1565
- }
1566
- #endif /* SE_NEED_F16_LUT */
1567
-
1568
- #if defined(SE_HAVE_NEON_FP16)
1569
- static float dot_product_f16_neon(const float *q, const uint8_t *row, size_t dim) {
1570
- size_t j = 0;
1571
- float32x4_t a0 = vdupq_n_f32(0.0f);
1572
- float32x4_t a1 = vdupq_n_f32(0.0f);
1573
- for (; j + 7 < dim; j += 8) {
1574
- float16x4_t h0 =
1575
- vreinterpret_f16_u16(vld1_u16((const uint16_t *)(const void *)(row + j * 2)));
1576
- float16x4_t h1 =
1577
- vreinterpret_f16_u16(vld1_u16((const uint16_t *)(const void *)(row + (j + 4) * 2)));
1578
- float32x4_t r0 = vcvt_f32_f16(h0);
1579
- float32x4_t r1 = vcvt_f32_f16(h1);
1580
- a0 = vmlaq_f32(a0, vld1q_f32(q + j), r0);
1581
- a1 = vmlaq_f32(a1, vld1q_f32(q + j + 4), r1);
1582
- }
1583
- float32x4_t sumv = vaddq_f32(a0, a1);
1584
- float dot = vaddvq_f32(sumv);
1585
- for (; j < dim; j++)
1586
- dot += q[j] * f16_bits_to_float(read_u16le(row + j * 2));
1587
- return dot;
1588
- }
1589
-
1590
- static float dot_and_row_sq_f16_neon(const float *q, const uint8_t *row, size_t dim,
1591
- float *row_sq_out) {
1592
- size_t j = 0;
1593
- float32x4_t d0 = vdupq_n_f32(0.0f);
1594
- float32x4_t d1 = vdupq_n_f32(0.0f);
1595
- float32x4_t s0 = vdupq_n_f32(0.0f);
1596
- float32x4_t s1 = vdupq_n_f32(0.0f);
1597
- for (; j + 7 < dim; j += 8) {
1598
- float16x4_t h0 =
1599
- vreinterpret_f16_u16(vld1_u16((const uint16_t *)(const void *)(row + j * 2)));
1600
- float16x4_t h1 =
1601
- vreinterpret_f16_u16(vld1_u16((const uint16_t *)(const void *)(row + (j + 4) * 2)));
1602
- float32x4_t r0 = vcvt_f32_f16(h0);
1603
- float32x4_t r1 = vcvt_f32_f16(h1);
1604
- d0 = vmlaq_f32(d0, vld1q_f32(q + j), r0);
1605
- d1 = vmlaq_f32(d1, vld1q_f32(q + j + 4), r1);
1606
- s0 = vmlaq_f32(s0, r0, r0);
1607
- s1 = vmlaq_f32(s1, r1, r1);
1608
- }
1609
- float dot = vaddvq_f32(vaddq_f32(d0, d1));
1610
- float row_sq = vaddvq_f32(vaddq_f32(s0, s1));
1611
- for (; j < dim; j++) {
1612
- float r = f16_bits_to_float(read_u16le(row + j * 2));
1613
- dot += q[j] * r;
1614
- row_sq += r * r;
1615
- }
1616
- *row_sq_out = row_sq;
1617
- return dot;
1618
- }
1619
- #endif
1620
-
1621
- #if defined(SE_HAVE_X86_F16C_TARGET)
1622
- __attribute__((target("f16c,avx"))) static float hsum256_f16c(__m256 v) {
1623
- __m128 low = _mm256_castps256_ps128(v);
1624
- __m128 high = _mm256_extractf128_ps(v, 1);
1625
- __m128 sum = _mm_add_ps(low, high);
1626
- float tmp[4];
1627
- _mm_storeu_ps(tmp, sum);
1628
- return (tmp[0] + tmp[1]) + (tmp[2] + tmp[3]);
1629
- }
1630
-
1631
- __attribute__((target("f16c,avx"))) static float
1632
- dot_product_f16_f16c(const float *q, const uint8_t *row, size_t dim) {
1633
- size_t j = 0;
1634
- __m256 a0 = _mm256_setzero_ps();
1635
- __m256 a1 = _mm256_setzero_ps();
1636
- for (; j + 15 < dim; j += 16) {
1637
- __m256 r0 = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + j * 2)));
1638
- __m256 r1 =
1639
- _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + (j + 8) * 2)));
1640
- a0 = _mm256_add_ps(a0, _mm256_mul_ps(_mm256_loadu_ps(q + j), r0));
1641
- a1 = _mm256_add_ps(a1, _mm256_mul_ps(_mm256_loadu_ps(q + j + 8), r1));
1642
- }
1643
- float dot = hsum256_f16c(_mm256_add_ps(a0, a1));
1644
- for (; j + 7 < dim; j += 8) {
1645
- __m256 r = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + j * 2)));
1646
- dot += hsum256_f16c(_mm256_mul_ps(_mm256_loadu_ps(q + j), r));
1647
- }
1648
- for (; j < dim; j++)
1649
- dot += q[j] * f16_bits_to_float(read_u16le(row + j * 2));
1650
- return dot;
1651
- }
1652
-
1653
- __attribute__((target("f16c,avx"))) static float
1654
- dot_and_row_sq_f16_f16c(const float *q, const uint8_t *row, size_t dim, float *row_sq_out) {
1655
- size_t j = 0;
1656
- __m256 d0 = _mm256_setzero_ps();
1657
- __m256 d1 = _mm256_setzero_ps();
1658
- __m256 s0 = _mm256_setzero_ps();
1659
- __m256 s1 = _mm256_setzero_ps();
1660
- for (; j + 15 < dim; j += 16) {
1661
- __m256 r0 = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + j * 2)));
1662
- __m256 r1 =
1663
- _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + (j + 8) * 2)));
1664
- d0 = _mm256_add_ps(d0, _mm256_mul_ps(_mm256_loadu_ps(q + j), r0));
1665
- d1 = _mm256_add_ps(d1, _mm256_mul_ps(_mm256_loadu_ps(q + j + 8), r1));
1666
- s0 = _mm256_add_ps(s0, _mm256_mul_ps(r0, r0));
1667
- s1 = _mm256_add_ps(s1, _mm256_mul_ps(r1, r1));
1668
- }
1669
- float dot = hsum256_f16c(_mm256_add_ps(d0, d1));
1670
- float row_sq = hsum256_f16c(_mm256_add_ps(s0, s1));
1671
- for (; j + 7 < dim; j += 8) {
1672
- __m256 r = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(const void *)(row + j * 2)));
1673
- dot += hsum256_f16c(_mm256_mul_ps(_mm256_loadu_ps(q + j), r));
1674
- row_sq += hsum256_f16c(_mm256_mul_ps(r, r));
1675
- }
1676
- for (; j < dim; j++) {
1677
- float r = f16_bits_to_float(read_u16le(row + j * 2));
1678
- dot += q[j] * r;
1679
- row_sq += r * r;
1680
- }
1681
- *row_sq_out = row_sq;
1682
- return dot;
1683
- }
1684
- #endif
1685
-
1686
- static float dot_product_f16(const float *q, const uint8_t *row, size_t dim) {
1687
- #if defined(SE_HAVE_NEON_FP16)
1688
- return dot_product_f16_neon(q, row, dim);
1689
- #else
1690
- #if defined(SE_HAVE_X86_F16C_TARGET)
1691
- if (se_f16_backend == SE_F16_BACKEND_F16C)
1692
- return dot_product_f16_f16c(q, row, dim);
1693
- #endif
1694
- return dot_product_f16_lut(q, row, dim);
1695
- #endif
1696
- }
1697
-
1698
- static float dot_and_row_sq_f16(const float *q, const uint8_t *row, size_t dim, float *row_sq_out) {
1699
- #if defined(SE_HAVE_NEON_FP16)
1700
- return dot_and_row_sq_f16_neon(q, row, dim, row_sq_out);
1701
- #else
1702
- #if defined(SE_HAVE_X86_F16C_TARGET)
1703
- if (se_f16_backend == SE_F16_BACKEND_F16C)
1704
- return dot_and_row_sq_f16_f16c(q, row, dim, row_sq_out);
1705
- #endif
1706
- return dot_and_row_sq_f16_lut(q, row, dim, row_sq_out);
1707
- #endif
1708
- }
1709
-
1710
- static void select_f16_backend(void) {
1711
- #if defined(SE_HAVE_NEON_FP16)
1712
- se_f16_backend = SE_F16_BACKEND_NEON_FP16;
1713
- #else
1714
- #if defined(SE_HAVE_X86_F16C_TARGET)
1715
- if (detect_x86_f16c()) {
1716
- se_f16_backend = SE_F16_BACKEND_F16C;
1717
- return;
1718
- }
1719
- #endif
1720
- se_f16_backend = SE_F16_BACKEND_LUT;
1721
- init_f16_lut();
1722
- #endif
1723
- }
1724
-
1725
- static VALUE se_simd_backend(VALUE self) {
1726
- (void)self;
1727
- switch (se_f16_backend) {
1728
- case SE_F16_BACKEND_NEON_FP16:
1729
- return rb_str_new_cstr("neon-fp16");
1730
- case SE_F16_BACKEND_F16C:
1731
- return rb_str_new_cstr("f16c");
1732
- default:
1733
- return rb_str_new_cstr("lut");
1734
- }
1735
- }
1736
-
1737
- static float cosine_score(float dot, float row_sq, float inv_query_norm) {
1738
- if (row_sq > 0.0f)
1739
- return dot * inv_query_norm / sqrtf(row_sq);
1740
- if (row_sq == 0.0f)
1741
- return 0.0f;
1742
- return NAN;
1743
- }
1744
-
1745
- static void *topk_execute(void *arg) {
1746
- topk_job_t *job = (topk_job_t *)arg;
1747
- for (size_t r = 0; r < job->rows; r++) {
1748
- if ((r & 1023u) == 0 && job->cancelled)
1749
- return NULL;
1750
- float score;
1751
-
1752
- if (job->format == SE_VECTOR_FORMAT_F16) {
1753
- const uint8_t *row = (const uint8_t *)job->m + r * job->dim * 2u;
1754
- if (job->cosine) {
1755
- float row_sq = 0.0f;
1756
- score = dot_and_row_sq_f16(job->q, row, job->dim, &row_sq);
1757
- score = cosine_score(score, row_sq, job->inv_query_norm);
1758
- } else {
1759
- score = dot_product_f16(job->q, row, job->dim);
1760
- }
1761
- } else {
1762
- const float *row = (const float *)job->m + r * job->dim;
1763
- if (job->cosine) {
1764
- float row_sq = 0.0f;
1765
- score = dot_and_row_sq_unrolled(job->q, row, job->dim, &row_sq);
1766
- score = cosine_score(score, row_sq, job->inv_query_norm);
1767
- } else {
1768
- score = dot_product_unrolled(job->q, row, job->dim);
1769
- }
1770
- }
1771
-
1772
- if (!(score > job->best_score[job->k - 1]))
1773
- continue;
1774
-
1775
- long pos = job->k - 1;
1776
- while (pos > 0 && job->best_score[pos - 1] < score) {
1777
- job->best_score[pos] = job->best_score[pos - 1];
1778
- job->best_idx[pos] = job->best_idx[pos - 1];
1779
- pos--;
1780
- }
1781
- job->best_score[pos] = score;
1782
- job->best_idx[pos] = r;
1783
- }
1784
- return NULL;
1785
- }
1786
-
1787
1214
  static VALUE topk_body(VALUE arg) {
1788
1215
  topk_run_t *run = (topk_run_t *)(uintptr_t)arg;
1789
1216
  const char *matrix_ptr = RSTRING_PTR(run->matrix);
@@ -1796,17 +1223,17 @@ static VALUE topk_body(VALUE arg) {
1796
1223
  }
1797
1224
 
1798
1225
  if (run->job.cosine) {
1799
- float query_sq = dot_product_unrolled(run->job.q, run->job.q, run->job.dim);
1226
+ float query_sq = se_dot_product_f32(run->job.q, run->job.q, run->job.dim);
1800
1227
  if (!(query_sq > 0.0f))
1801
1228
  rb_raise(rb_eArgError, "cosine_top_k needs a query with a non-zero norm");
1802
1229
  run->job.inv_query_norm = 1.0f / sqrtf(query_sq);
1803
1230
  }
1804
1231
 
1805
1232
  if (run->release_gvl) {
1806
- rb_thread_call_without_gvl(topk_execute, &run->job, topk_unblock_cancel, &run->job);
1233
+ rb_thread_call_without_gvl(se_topk_execute, &run->job, topk_unblock_cancel, &run->job);
1807
1234
  rb_thread_check_ints();
1808
1235
  } else {
1809
- topk_execute(&run->job);
1236
+ se_topk_execute(&run->job);
1810
1237
  rb_thread_check_ints();
1811
1238
  }
1812
1239
 
@@ -1827,10 +1254,10 @@ static VALUE topk_body(VALUE arg) {
1827
1254
 
1828
1255
  static VALUE topk_ensure(VALUE arg) {
1829
1256
  topk_run_t *run = (topk_run_t *)(uintptr_t)arg;
1830
- free(run->q_copy);
1831
- free(run->matrix_copy);
1832
- free(run->job.best_idx);
1833
- free(run->job.best_score);
1257
+ se_free(run->q_copy);
1258
+ se_free(run->matrix_copy);
1259
+ se_free(run->job.best_idx);
1260
+ se_free(run->job.best_score);
1834
1261
  run->q_copy = NULL;
1835
1262
  run->matrix_copy = NULL;
1836
1263
  run->job.best_idx = NULL;
@@ -1894,11 +1321,11 @@ static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
1894
1321
  Check_Type(matrix, T_STRING);
1895
1322
 
1896
1323
  se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
1897
- size_t element_bytes = vector_format_element_bytes(format);
1324
+ size_t element_bytes = se_vector_format_element_bytes(format);
1898
1325
  size_t dim = topk_required_dim(opts);
1899
1326
 
1900
1327
  size_t row_bytes;
1901
- if (!checked_mul_size(dim, element_bytes, &row_bytes) || !size_fits_long(row_bytes))
1328
+ if (!se_checked_mul_size(dim, element_bytes, &row_bytes) || !se_size_fits_long(row_bytes))
1902
1329
  rb_raise(rb_eArgError, "dim: is too large");
1903
1330
 
1904
1331
  if ((size_t)RSTRING_LEN(query) != row_bytes)
@@ -1938,14 +1365,22 @@ static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
1938
1365
  run.job.inv_query_norm = 1.0f;
1939
1366
 
1940
1367
  size_t q_float_bytes;
1941
- if (!checked_mul_size(dim, sizeof(float), &q_float_bytes))
1368
+ if (!se_checked_mul_size(dim, sizeof(float), &q_float_bytes))
1942
1369
  rb_raise(rb_eArgError, "dim: is too large");
1943
1370
 
1944
- run.q_copy = (float *)malloc(q_float_bytes);
1945
- run.job.best_idx = (size_t *)calloc((size_t)k, sizeof(size_t));
1946
- run.job.best_score = (float *)malloc((size_t)k * sizeof(float));
1371
+ run.q_copy = (float *)se_malloc(SE_ALLOC_TOPK_QUERY, q_float_bytes);
1372
+ size_t best_idx_bytes = 0;
1373
+ size_t best_score_bytes = 0;
1374
+ if (!se_array_bytes((size_t)k, sizeof(size_t), &best_idx_bytes) ||
1375
+ !se_array_bytes((size_t)k, sizeof(float), &best_score_bytes)) {
1376
+ topk_ensure((VALUE)(uintptr_t)&run);
1377
+ rb_raise(rb_eArgError, "k is too large");
1378
+ }
1379
+
1380
+ run.job.best_idx = (size_t *)se_calloc(SE_ALLOC_TOPK_BEST, 1, best_idx_bytes);
1381
+ run.job.best_score = (float *)se_malloc(SE_ALLOC_TOPK_BEST, best_score_bytes);
1947
1382
  if (needs_copy)
1948
- run.matrix_copy = (float *)malloc(matrix_bytes ? matrix_bytes : 1);
1383
+ run.matrix_copy = (float *)se_malloc(SE_ALLOC_TOPK_MATRIX_COPY, matrix_bytes);
1949
1384
  if (!run.q_copy || !run.job.best_idx || !run.job.best_score ||
1950
1385
  (needs_copy && !run.matrix_copy)) {
1951
1386
  topk_ensure((VALUE)(uintptr_t)&run);
@@ -1953,7 +1388,7 @@ static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
1953
1388
  }
1954
1389
 
1955
1390
  if (format == SE_VECTOR_FORMAT_F16)
1956
- decode_f16_to_floats(run.q_copy, (const uint8_t *)RSTRING_PTR(query), dim);
1391
+ se_decode_f16_to_floats(run.q_copy, (const uint8_t *)RSTRING_PTR(query), dim);
1957
1392
  else
1958
1393
  memcpy(run.q_copy, RSTRING_PTR(query), row_bytes);
1959
1394
  run.job.q = run.q_copy;
@@ -1982,14 +1417,14 @@ static VALUE se_encode_f16(VALUE self, VALUE ary) {
1982
1417
 
1983
1418
  long n = RARRAY_LEN(ary);
1984
1419
  size_t bytes;
1985
- if (!checked_mul_size((size_t)n, 2, &bytes) || !size_fits_long(bytes))
1420
+ if (!se_checked_mul_size((size_t)n, 2, &bytes) || !se_size_fits_long(bytes))
1986
1421
  rb_raise(rb_eArgError, "vector is too large");
1987
1422
 
1988
1423
  VALUE out = rb_str_new(NULL, (long)bytes);
1989
1424
  rb_enc_associate(out, binary_encoding);
1990
1425
  for (long i = 0; i < n; i++) {
1991
1426
  double v = NUM2DBL(rb_ary_entry(ary, i));
1992
- write_u16le((uint8_t *)RSTRING_PTR(out) + (size_t)i * 2, float_to_f16_bits((float)v));
1427
+ se_write_f16le((uint8_t *)RSTRING_PTR(out) + (size_t)i * 2, (float)v);
1993
1428
  }
1994
1429
  return out;
1995
1430
  }
@@ -2005,14 +1440,52 @@ static VALUE se_decode_f16(VALUE self, VALUE blob) {
2005
1440
  VALUE out = rb_ary_new_capa(n / 2);
2006
1441
  for (long i = 0; i < n / 2; i++) {
2007
1442
  const uint8_t *src = (const uint8_t *)RSTRING_PTR(blob) + (size_t)i * 2;
2008
- rb_ary_push(out, DBL2NUM((double)f16_bits_to_float(read_u16le(src))));
1443
+ rb_ary_push(out, DBL2NUM((double)se_read_f16le(src)));
2009
1444
  }
2010
1445
  RB_GC_GUARD(blob);
2011
1446
  return out;
2012
1447
  }
2013
1448
 
1449
+ #if SE_ENABLE_ALLOC_STATS
1450
+ static VALUE se_alloc_stats_hash(VALUE self) {
1451
+ (void)self;
1452
+ se_alloc_stats_t stats[SE_ALLOC_CATEGORY_COUNT];
1453
+ se_alloc_stats_snapshot(stats);
1454
+
1455
+ VALUE out = rb_hash_new();
1456
+ ID id_current_bytes = rb_intern("current_bytes");
1457
+ ID id_peak_bytes = rb_intern("peak_bytes");
1458
+ ID id_total_allocated_bytes = rb_intern("total_allocated_bytes");
1459
+ ID id_total_freed_bytes = rb_intern("total_freed_bytes");
1460
+ ID id_alloc_count = rb_intern("alloc_count");
1461
+ ID id_realloc_count = rb_intern("realloc_count");
1462
+ ID id_free_count = rb_intern("free_count");
1463
+
1464
+ for (int i = 0; i < SE_ALLOC_CATEGORY_COUNT; i++) {
1465
+ VALUE item = rb_hash_new();
1466
+ rb_hash_aset(item, ID2SYM(id_current_bytes), SIZET2NUM(stats[i].current_bytes));
1467
+ rb_hash_aset(item, ID2SYM(id_peak_bytes), SIZET2NUM(stats[i].peak_bytes));
1468
+ rb_hash_aset(item, ID2SYM(id_total_allocated_bytes),
1469
+ SIZET2NUM(stats[i].total_allocated_bytes));
1470
+ rb_hash_aset(item, ID2SYM(id_total_freed_bytes), SIZET2NUM(stats[i].total_freed_bytes));
1471
+ rb_hash_aset(item, ID2SYM(id_alloc_count), SIZET2NUM(stats[i].alloc_count));
1472
+ rb_hash_aset(item, ID2SYM(id_realloc_count), SIZET2NUM(stats[i].realloc_count));
1473
+ rb_hash_aset(item, ID2SYM(id_free_count), SIZET2NUM(stats[i].free_count));
1474
+ rb_hash_aset(out, ID2SYM(rb_intern(se_alloc_category_name((se_alloc_category_t)i))), item);
1475
+ }
1476
+
1477
+ return out;
1478
+ }
1479
+
1480
+ static VALUE se_alloc_stats_reset_bang(VALUE self) {
1481
+ (void)self;
1482
+ se_alloc_stats_reset();
1483
+ return Qnil;
1484
+ }
1485
+ #endif
1486
+
2014
1487
  RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
2015
- select_f16_backend();
1488
+ se_select_f16_backend();
2016
1489
  binary_encoding = rb_ascii8bit_encoding();
2017
1490
  utf8_encoding = rb_utf8_encoding();
2018
1491
  id_join = rb_intern("join");
@@ -2028,6 +1501,9 @@ RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
2028
1501
  id_dim = rb_intern("dim");
2029
1502
  id_allow_unfrozen = rb_intern("allow_unfrozen");
2030
1503
 
1504
+ id_validate_encoding = rb_intern("validate_encoding");
1505
+ id_full = rb_intern("full");
1506
+ id_prefix = rb_intern("prefix");
2031
1507
  mStaticEmbeddings = rb_define_module("StaticEmbeddings");
2032
1508
  cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
2033
1509
 
@@ -2063,6 +1539,11 @@ RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
2063
1539
  rb_define_singleton_method(mStaticEmbeddings, "encode_f16", se_encode_f16, 1);
2064
1540
  rb_define_singleton_method(mStaticEmbeddings, "decode_f16", se_decode_f16, 1);
2065
1541
  rb_define_singleton_method(mStaticEmbeddings, "simd_backend", se_simd_backend, 0);
1542
+ #if SE_ENABLE_ALLOC_STATS
1543
+ rb_define_singleton_method(mStaticEmbeddings, "__alloc_stats__", se_alloc_stats_hash, 0);
1544
+ rb_define_singleton_method(mStaticEmbeddings, "__alloc_stats_reset__",
1545
+ se_alloc_stats_reset_bang, 0);
1546
+ #endif
2066
1547
 
2067
1548
  rb_define_const(mStaticEmbeddings, "FORMAT_VERSION", UINT2NUM(SE_FORMAT_VERSION));
2068
1549
  rb_define_const(mStaticEmbeddings, "TOKENIZER_BERT_WORDPIECE_V1",