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 +4 -4
- data/CHANGELOG.md +121 -0
- data/README.md +167 -276
- data/docs/ARCHITECTURE.md +56 -6
- data/docs/MODEL_AUDIT.md +21 -8
- data/docs/PERFORMANCE.md +104 -36
- data/ext/static_embeddings/extconf.rb +4 -0
- data/ext/static_embeddings/se_alloc_stats.c +244 -0
- data/ext/static_embeddings/se_f16.c +378 -0
- data/ext/static_embeddings/se_format.c +235 -148
- data/ext/static_embeddings/se_internal.h +159 -0
- data/ext/static_embeddings/se_tokenizer.c +144 -41
- data/ext/static_embeddings/se_topk.c +236 -0
- data/ext/static_embeddings/static_embeddings.c +225 -744
- data/lib/static_embeddings/format.rb +22 -11
- data/lib/static_embeddings/version.rb +1 -1
- data/tools/benchmark.rb +13 -4
- metadata +4 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
|
350
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
!
|
|
753
|
-
!
|
|
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 *)
|
|
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
|
-
|
|
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 (!
|
|
802
|
-
|
|
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
|
-
|
|
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 (!
|
|
821
|
-
!
|
|
822
|
-
|
|
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
|
-
|
|
830
|
-
|
|
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
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
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 (!
|
|
872
|
-
!
|
|
873
|
-
!
|
|
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
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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
|
-
|
|
843
|
+
se_free(run.stats);
|
|
968
844
|
run.stats = NULL;
|
|
969
|
-
|
|
845
|
+
se_free(run.targets);
|
|
970
846
|
run.targets = NULL;
|
|
971
|
-
|
|
847
|
+
se_free(run.pending);
|
|
972
848
|
run.pending = NULL;
|
|
973
|
-
|
|
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
|
-
|
|
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,
|
|
1003
|
-
|
|
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,
|
|
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
|
-
|
|
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 (!
|
|
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)),
|
|
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(
|
|
1062
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1228
|
-
|
|
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 (!
|
|
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 (!
|
|
1255
|
-
|
|
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 *)
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 *)
|
|
1945
|
-
|
|
1946
|
-
|
|
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 *)
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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",
|