static_embeddings 0.1.1
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 +7 -0
- data/CHANGELOG.md +136 -0
- data/LICENSE.txt +21 -0
- data/README.md +439 -0
- data/docs/ARCHITECTURE.md +278 -0
- data/docs/MODEL_AUDIT.md +61 -0
- data/docs/PERFORMANCE.md +60 -0
- data/exe/static_embeddings +6 -0
- data/ext/static_embeddings/extconf.rb +44 -0
- data/ext/static_embeddings/se_embed.c +192 -0
- data/ext/static_embeddings/se_format.c +762 -0
- data/ext/static_embeddings/se_internal.h +266 -0
- data/ext/static_embeddings/se_tokenizer.c +506 -0
- data/ext/static_embeddings/se_unicode.c +120 -0
- data/ext/static_embeddings/static_embeddings.c +2070 -0
- data/lib/static_embeddings/cli.rb +178 -0
- data/lib/static_embeddings/converter.rb +284 -0
- data/lib/static_embeddings/errors.rb +6 -0
- data/lib/static_embeddings/format.rb +289 -0
- data/lib/static_embeddings/model.rb +48 -0
- data/lib/static_embeddings/paths.rb +29 -0
- data/lib/static_embeddings/reference.rb +191 -0
- data/lib/static_embeddings/safetensors.rb +87 -0
- data/lib/static_embeddings/unicode_tables.rb +127 -0
- data/lib/static_embeddings/version.rb +3 -0
- data/lib/static_embeddings.rb +118 -0
- data/static_embeddings.gemspec +45 -0
- data/tools/benchmark.rb +38 -0
- data/tools/build_demo_model.rb +16 -0
- data/tools/check_model2vec_parity.rb +107 -0
- data/tools/make_fixture_model.rb +165 -0
- metadata +134 -0
|
@@ -0,0 +1,2070 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/encoding.h>
|
|
3
|
+
#include <ruby/thread.h>
|
|
4
|
+
|
|
5
|
+
#ifdef HAVE_RUBY_FIBER_SCHEDULER_H
|
|
6
|
+
#include <ruby/fiber/scheduler.h>
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
#include <float.h>
|
|
10
|
+
#include <limits.h>
|
|
11
|
+
#include <math.h>
|
|
12
|
+
#include <signal.h>
|
|
13
|
+
#include <stdint.h>
|
|
14
|
+
#include <stdio.h>
|
|
15
|
+
#include <stdlib.h>
|
|
16
|
+
#include <string.h>
|
|
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
|
+
#include "se_internal.h"
|
|
38
|
+
|
|
39
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
40
|
+
#define SE_NORETURN __attribute__((noreturn))
|
|
41
|
+
#else
|
|
42
|
+
#define SE_NORETURN
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
#define SE_GVL_UNLOCK_THRESHOLD 2048
|
|
46
|
+
#define SE_FIBER_THRESHOLD 2048
|
|
47
|
+
#define SE_IDS_GVL_UNLOCK_THRESHOLD 256
|
|
48
|
+
#define SE_TOPK_GVL_UNLOCK_THRESHOLD (1024 * 1024)
|
|
49
|
+
#define SE_PREFIX_BYTES_PER_TOKEN 16
|
|
50
|
+
#define SE_PREFIX_MIN_BYTES 4096
|
|
51
|
+
#define SE_PREFIX_MAX_BYTES 65536
|
|
52
|
+
#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
|
+
|
|
77
|
+
static int string_equals_literal(VALUE str, const char *lit) {
|
|
78
|
+
size_t n = strlen(lit);
|
|
79
|
+
return (size_t)RSTRING_LEN(str) == n && memcmp(RSTRING_PTR(str), lit, n) == 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static se_vector_format_t resolve_vector_format(VALUE opt) {
|
|
83
|
+
if (opt == Qundef || opt == Qnil)
|
|
84
|
+
return SE_VECTOR_FORMAT_F32;
|
|
85
|
+
|
|
86
|
+
VALUE name;
|
|
87
|
+
if (SYMBOL_P(opt)) {
|
|
88
|
+
name = rb_sym2str(opt);
|
|
89
|
+
} else if (RB_TYPE_P(opt, T_STRING)) {
|
|
90
|
+
name = opt;
|
|
91
|
+
} else {
|
|
92
|
+
rb_raise(rb_eArgError, "format must be :f32, :float32, :f16, or :float16");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (string_equals_literal(name, "f32") || string_equals_literal(name, "float32"))
|
|
96
|
+
return SE_VECTOR_FORMAT_F32;
|
|
97
|
+
if (string_equals_literal(name, "f16") || string_equals_literal(name, "float16"))
|
|
98
|
+
return SE_VECTOR_FORMAT_F16;
|
|
99
|
+
|
|
100
|
+
rb_raise(rb_eArgError, "unsupported embedding format %" PRIsVALUE " (expected :f32 or :f16)",
|
|
101
|
+
opt);
|
|
102
|
+
}
|
|
103
|
+
|
|
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
|
+
static VALUE mStaticEmbeddings;
|
|
246
|
+
static VALUE cModel;
|
|
247
|
+
static VALUE cFiber;
|
|
248
|
+
static VALUE eError;
|
|
249
|
+
static VALUE eInvalidModel;
|
|
250
|
+
static VALUE eUnsupportedModel;
|
|
251
|
+
static VALUE eEncodingError;
|
|
252
|
+
static VALUE eEmptyInput;
|
|
253
|
+
|
|
254
|
+
static rb_encoding *binary_encoding;
|
|
255
|
+
static rb_encoding *utf8_encoding;
|
|
256
|
+
|
|
257
|
+
static ID id_join;
|
|
258
|
+
static ID id_kill;
|
|
259
|
+
static ID id_max_tokens;
|
|
260
|
+
static ID id_threads;
|
|
261
|
+
static ID id_format;
|
|
262
|
+
static ID id_blocking_p;
|
|
263
|
+
static ID id_vector;
|
|
264
|
+
static ID id_token_count;
|
|
265
|
+
static ID id_unk_count;
|
|
266
|
+
static ID id_truncated;
|
|
267
|
+
static ID id_dim;
|
|
268
|
+
static ID id_allow_unfrozen;
|
|
269
|
+
|
|
270
|
+
RUBY_FUNC_EXPORTED void Init_static_embeddings(void);
|
|
271
|
+
|
|
272
|
+
typedef struct {
|
|
273
|
+
se_model_t model;
|
|
274
|
+
int open;
|
|
275
|
+
} model_wrapper_t;
|
|
276
|
+
|
|
277
|
+
static void model_free(void *ptr) {
|
|
278
|
+
model_wrapper_t *w = (model_wrapper_t *)ptr;
|
|
279
|
+
if (!w)
|
|
280
|
+
return;
|
|
281
|
+
if (w->open)
|
|
282
|
+
se_model_close(&w->model);
|
|
283
|
+
xfree(w);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
static size_t model_memsize(const void *ptr) {
|
|
287
|
+
const model_wrapper_t *w = (const model_wrapper_t *)ptr;
|
|
288
|
+
if (!w)
|
|
289
|
+
return 0;
|
|
290
|
+
return sizeof(model_wrapper_t) + se_model_memsize(&w->model);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static const rb_data_type_t model_type = {"StaticEmbeddings/Model",
|
|
294
|
+
{NULL, model_free, model_memsize, NULL, {0}},
|
|
295
|
+
0,
|
|
296
|
+
0,
|
|
297
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
|
298
|
+
|
|
299
|
+
static model_wrapper_t *get_model(VALUE self) {
|
|
300
|
+
model_wrapper_t *w;
|
|
301
|
+
TypedData_Get_Struct(self, model_wrapper_t, &model_type, w);
|
|
302
|
+
if (!w->open)
|
|
303
|
+
rb_raise(eError, "model is closed");
|
|
304
|
+
return w;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static VALUE error_class_for(se_status_t status) {
|
|
308
|
+
switch (status) {
|
|
309
|
+
case SE_ERR_INVALID_FORMAT:
|
|
310
|
+
case SE_ERR_IO:
|
|
311
|
+
return eInvalidModel;
|
|
312
|
+
case SE_ERR_UNSUPPORTED_VERSION:
|
|
313
|
+
case SE_ERR_UNSUPPORTED_TOKENIZER:
|
|
314
|
+
return eUnsupportedModel;
|
|
315
|
+
case SE_ERR_INVALID_UTF8:
|
|
316
|
+
return eEncodingError;
|
|
317
|
+
case SE_ERR_EMPTY_INPUT:
|
|
318
|
+
return eEmptyInput;
|
|
319
|
+
default:
|
|
320
|
+
return eError;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
static void raise_se(const se_error_t *err) SE_NORETURN;
|
|
325
|
+
|
|
326
|
+
static void raise_se(const se_error_t *err) {
|
|
327
|
+
rb_raise(error_class_for(err->status), "%s", err->message);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static void check_text_encoding_at(VALUE str, long index) {
|
|
331
|
+
rb_encoding *enc = rb_enc_get(str);
|
|
332
|
+
if (enc != utf8_encoding && enc != rb_usascii_encoding()) {
|
|
333
|
+
if (index >= 0) {
|
|
334
|
+
rb_raise(eEncodingError,
|
|
335
|
+
"input[%ld]: expected UTF-8 or US-ASCII, got %s (transcode explicitly)", index,
|
|
336
|
+
rb_enc_name(enc));
|
|
337
|
+
}
|
|
338
|
+
rb_raise(eEncodingError, "expected UTF-8 or US-ASCII, got %s (transcode explicitly)",
|
|
339
|
+
rb_enc_name(enc));
|
|
340
|
+
}
|
|
341
|
+
int cr = rb_enc_str_coderange(str);
|
|
342
|
+
if (cr != ENC_CODERANGE_VALID && cr != ENC_CODERANGE_7BIT) {
|
|
343
|
+
if (index >= 0)
|
|
344
|
+
rb_raise(eEncodingError, "input[%ld]: string is not valid %s", index, rb_enc_name(enc));
|
|
345
|
+
rb_raise(eEncodingError, "string is not valid %s", rb_enc_name(enc));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static void check_text_encoding(VALUE str) {
|
|
350
|
+
check_text_encoding_at(str, -1);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
typedef struct {
|
|
354
|
+
const uint8_t *base;
|
|
355
|
+
size_t *offsets;
|
|
356
|
+
size_t *lengths;
|
|
357
|
+
size_t count;
|
|
358
|
+
void *allocation;
|
|
359
|
+
} batch_input_t;
|
|
360
|
+
|
|
361
|
+
typedef struct {
|
|
362
|
+
const se_model_t *model;
|
|
363
|
+
batch_input_t input;
|
|
364
|
+
uint32_t max_tokens;
|
|
365
|
+
float *out;
|
|
366
|
+
se_token_stats_t *stats;
|
|
367
|
+
const size_t *out_index;
|
|
368
|
+
size_t next_index;
|
|
369
|
+
int failed;
|
|
370
|
+
size_t failed_index;
|
|
371
|
+
volatile sig_atomic_t cancelled;
|
|
372
|
+
se_error_t error;
|
|
373
|
+
} batch_job_t;
|
|
374
|
+
|
|
375
|
+
static void job_fail(batch_job_t *job, se_error_t err, size_t index) {
|
|
376
|
+
if (job->failed)
|
|
377
|
+
return;
|
|
378
|
+
job->failed = 1;
|
|
379
|
+
job->failed_index = index;
|
|
380
|
+
job->error = err;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
static void batch_worker_run(batch_job_t *job, se_scratch_t *scratch) {
|
|
384
|
+
const uint32_t dim = job->model->meta.dim;
|
|
385
|
+
|
|
386
|
+
for (;;) {
|
|
387
|
+
if (job->cancelled) {
|
|
388
|
+
se_error_set(&job->error, SE_ERR_INTERNAL, "operation cancelled");
|
|
389
|
+
job->failed = 1;
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
size_t i = job->next_index++;
|
|
394
|
+
if (i >= job->input.count || job->failed)
|
|
395
|
+
return;
|
|
396
|
+
|
|
397
|
+
const uint8_t *text = job->input.base + job->input.offsets[i];
|
|
398
|
+
size_t len = job->input.lengths[i];
|
|
399
|
+
size_t row = job->out_index ? job->out_index[i] : i;
|
|
400
|
+
|
|
401
|
+
if (!se_scratch_reserve(scratch, dim)) {
|
|
402
|
+
se_error_t err;
|
|
403
|
+
se_error_set(&err, SE_ERR_OOM, "out of memory while sizing scratch buffers");
|
|
404
|
+
job_fail(job, err, i);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
se_error_t err;
|
|
409
|
+
se_error_clear(&err);
|
|
410
|
+
se_status_t rc =
|
|
411
|
+
se_embed_one(job->model, scratch, text, len, job->max_tokens, job->out + row * dim,
|
|
412
|
+
&job->stats[row], &err, &job->cancelled);
|
|
413
|
+
if (rc != SE_OK) {
|
|
414
|
+
job_fail(job, err, i);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
static void *batch_execute(void *arg) {
|
|
421
|
+
batch_job_t *job = (batch_job_t *)arg;
|
|
422
|
+
se_scratch_t scratch;
|
|
423
|
+
se_scratch_init(&scratch);
|
|
424
|
+
batch_worker_run(job, &scratch);
|
|
425
|
+
se_scratch_free(&scratch);
|
|
426
|
+
return NULL;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
static void unblock_cancel(void *arg) {
|
|
430
|
+
batch_job_t *job = (batch_job_t *)arg;
|
|
431
|
+
if (job)
|
|
432
|
+
job->cancelled = 1;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
static VALUE current_fiber_scheduler(void) {
|
|
436
|
+
#ifdef HAVE_RUBY_FIBER_SCHEDULER_H
|
|
437
|
+
VALUE sched = rb_fiber_scheduler_current();
|
|
438
|
+
if (sched == Qnil || sched == Qfalse)
|
|
439
|
+
return Qnil;
|
|
440
|
+
return sched;
|
|
441
|
+
#else
|
|
442
|
+
return Qnil;
|
|
443
|
+
#endif
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
static int current_fiber_is_blocking(void) {
|
|
447
|
+
#ifdef HAVE_RUBY_FIBER_SCHEDULER_H
|
|
448
|
+
if (NIL_P(cFiber) || !rb_respond_to(cFiber, id_blocking_p))
|
|
449
|
+
return 1;
|
|
450
|
+
return RTEST(rb_funcall(cFiber, id_blocking_p, 0));
|
|
451
|
+
#else
|
|
452
|
+
return 1;
|
|
453
|
+
#endif
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
typedef struct {
|
|
457
|
+
batch_job_t *job;
|
|
458
|
+
VALUE thread;
|
|
459
|
+
} fiber_worker_t;
|
|
460
|
+
|
|
461
|
+
static void fiber_worker_mark(void *ptr) {
|
|
462
|
+
fiber_worker_t *w = (fiber_worker_t *)ptr;
|
|
463
|
+
if (w)
|
|
464
|
+
rb_gc_mark(w->thread);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static void fiber_worker_free(void *ptr) {
|
|
468
|
+
xfree(ptr);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
static const rb_data_type_t fiber_worker_type = {
|
|
472
|
+
"StaticEmbeddings/FiberWorker",
|
|
473
|
+
{fiber_worker_mark, fiber_worker_free, NULL, NULL, {0}},
|
|
474
|
+
0,
|
|
475
|
+
0,
|
|
476
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
|
477
|
+
|
|
478
|
+
static VALUE fiber_thread_body(void *arg) {
|
|
479
|
+
fiber_worker_t *w = (fiber_worker_t *)arg;
|
|
480
|
+
rb_thread_call_without_gvl(batch_execute, w->job, unblock_cancel, w->job);
|
|
481
|
+
return Qnil;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
static VALUE fiber_worker_wait(VALUE wrapper) {
|
|
485
|
+
fiber_worker_t *w;
|
|
486
|
+
TypedData_Get_Struct(wrapper, fiber_worker_t, &fiber_worker_type, w);
|
|
487
|
+
w->thread = rb_thread_create(fiber_thread_body, w);
|
|
488
|
+
rb_funcall(w->thread, id_join, 0);
|
|
489
|
+
w->thread = Qnil;
|
|
490
|
+
return Qnil;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
static VALUE fiber_worker_ensure(VALUE wrapper) {
|
|
494
|
+
fiber_worker_t *w;
|
|
495
|
+
TypedData_Get_Struct(wrapper, fiber_worker_t, &fiber_worker_type, w);
|
|
496
|
+
if (!NIL_P(w->thread)) {
|
|
497
|
+
w->job->cancelled = 1;
|
|
498
|
+
rb_funcall(w->thread, id_kill, 0);
|
|
499
|
+
rb_funcall(w->thread, id_join, 0);
|
|
500
|
+
}
|
|
501
|
+
return Qnil;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static void run_job(batch_job_t *job, size_t total_bytes) {
|
|
505
|
+
VALUE scheduler = current_fiber_scheduler();
|
|
506
|
+
|
|
507
|
+
if (scheduler != Qnil && !current_fiber_is_blocking() && total_bytes >= SE_FIBER_THRESHOLD) {
|
|
508
|
+
fiber_worker_t *w;
|
|
509
|
+
VALUE wrapper = TypedData_Make_Struct(rb_cObject, fiber_worker_t, &fiber_worker_type, w);
|
|
510
|
+
w->job = job;
|
|
511
|
+
w->thread = Qnil;
|
|
512
|
+
rb_ensure(fiber_worker_wait, wrapper, fiber_worker_ensure, wrapper);
|
|
513
|
+
RB_GC_GUARD(wrapper);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (total_bytes >= SE_GVL_UNLOCK_THRESHOLD) {
|
|
518
|
+
rb_thread_call_without_gvl(batch_execute, job, unblock_cancel, job);
|
|
519
|
+
rb_thread_check_ints();
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
batch_execute(job);
|
|
524
|
+
rb_thread_check_ints();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
static VALUE protected_run_job(VALUE arg) {
|
|
528
|
+
batch_job_t *job = (batch_job_t *)(uintptr_t)arg;
|
|
529
|
+
size_t total_bytes = 0;
|
|
530
|
+
for (size_t i = 0; i < job->input.count; i++)
|
|
531
|
+
total_bytes += job->input.lengths[i];
|
|
532
|
+
run_job(job, total_bytes);
|
|
533
|
+
return Qnil;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
static VALUE model_alloc(VALUE klass) {
|
|
537
|
+
model_wrapper_t *w;
|
|
538
|
+
VALUE obj = TypedData_Make_Struct(klass, model_wrapper_t, &model_type, w);
|
|
539
|
+
memset(w, 0, sizeof(*w));
|
|
540
|
+
return obj;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
static VALUE model_initialize(VALUE self, VALUE path) {
|
|
544
|
+
model_wrapper_t *w;
|
|
545
|
+
TypedData_Get_Struct(self, model_wrapper_t, &model_type, w);
|
|
546
|
+
|
|
547
|
+
Check_Type(path, T_STRING);
|
|
548
|
+
if (memchr(RSTRING_PTR(path), '\0', (size_t)RSTRING_LEN(path)))
|
|
549
|
+
rb_raise(rb_eArgError, "path contains a null byte");
|
|
550
|
+
|
|
551
|
+
se_error_t err;
|
|
552
|
+
se_error_clear(&err);
|
|
553
|
+
if (se_model_open(&w->model, StringValueCStr(path), &err) != SE_OK)
|
|
554
|
+
raise_se(&err);
|
|
555
|
+
|
|
556
|
+
w->open = 1;
|
|
557
|
+
rb_ivar_set(self, rb_intern("@path"), rb_str_new_frozen(path));
|
|
558
|
+
return self;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
static VALUE model_close(VALUE self) {
|
|
562
|
+
model_wrapper_t *w;
|
|
563
|
+
TypedData_Get_Struct(self, model_wrapper_t, &model_type, w);
|
|
564
|
+
if (w->open) {
|
|
565
|
+
se_model_close(&w->model);
|
|
566
|
+
w->open = 0;
|
|
567
|
+
}
|
|
568
|
+
return Qnil;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
static VALUE model_closed_p(VALUE self) {
|
|
572
|
+
model_wrapper_t *w;
|
|
573
|
+
TypedData_Get_Struct(self, model_wrapper_t, &model_type, w);
|
|
574
|
+
return w->open ? Qfalse : Qtrue;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
static VALUE model_dim(VALUE self) {
|
|
578
|
+
return UINT2NUM(get_model(self)->model.meta.dim);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
static VALUE model_vocab_size(VALUE self) {
|
|
582
|
+
return UINT2NUM(get_model(self)->model.meta.vocab_size);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
static VALUE model_max_tokens(VALUE self) {
|
|
586
|
+
return UINT2NUM(get_model(self)->model.meta.max_tokens_default);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
static VALUE model_normalized_p(VALUE self) {
|
|
590
|
+
return get_model(self)->model.meta.normalization_type == SE_NORMALIZATION_L2 ? Qtrue : Qfalse;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
static VALUE model_lowercase_p(VALUE self) {
|
|
594
|
+
return get_model(self)->model.meta.do_lower_case ? Qtrue : Qfalse;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
static VALUE model_unk_id(VALUE self) {
|
|
598
|
+
return UINT2NUM(get_model(self)->model.meta.unk_id);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
static VALUE model_provenance_json(VALUE self) {
|
|
602
|
+
model_wrapper_t *w = get_model(self);
|
|
603
|
+
if (!w->model.provenance)
|
|
604
|
+
return Qnil;
|
|
605
|
+
return rb_enc_str_new(w->model.provenance, (long)w->model.provenance_size, utf8_encoding);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
static VALUE model_mapped_bytes(VALUE self) {
|
|
609
|
+
return SIZET2NUM(get_model(self)->model.map_size);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
typedef struct {
|
|
613
|
+
const se_model_t *model;
|
|
614
|
+
size_t pages;
|
|
615
|
+
} warmup_job_t;
|
|
616
|
+
|
|
617
|
+
static void *warmup_execute(void *arg) {
|
|
618
|
+
warmup_job_t *job = (warmup_job_t *)arg;
|
|
619
|
+
job->pages = se_model_warmup(job->model);
|
|
620
|
+
return NULL;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
static VALUE model_warmup(VALUE self) {
|
|
624
|
+
model_wrapper_t *w = get_model(self);
|
|
625
|
+
warmup_job_t job;
|
|
626
|
+
job.model = &w->model;
|
|
627
|
+
job.pages = 0;
|
|
628
|
+
rb_thread_call_without_gvl(warmup_execute, &job, NULL, NULL);
|
|
629
|
+
RB_GC_GUARD(self);
|
|
630
|
+
return self;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
static uint32_t resolve_max_tokens(const se_model_t *model, VALUE opt) {
|
|
634
|
+
if (opt == Qundef || opt == Qnil)
|
|
635
|
+
return model->meta.max_tokens_default;
|
|
636
|
+
if (opt == Qfalse)
|
|
637
|
+
return 0;
|
|
638
|
+
unsigned long long v = NUM2ULL(opt);
|
|
639
|
+
if (v == 0 || v > UINT32_MAX)
|
|
640
|
+
rb_raise(rb_eArgError,
|
|
641
|
+
"max_tokens must be between 1 and 4294967295, or false for unlimited");
|
|
642
|
+
return (uint32_t)v;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
static VALUE lookup_option(VALUE opts, ID id) {
|
|
646
|
+
if (NIL_P(opts))
|
|
647
|
+
return Qundef;
|
|
648
|
+
return rb_hash_lookup2(opts, ID2SYM(id), Qundef);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
static void reject_parallel_threads(VALUE opts) {
|
|
652
|
+
VALUE v = lookup_option(opts, id_threads);
|
|
653
|
+
if (v == Qundef || v == Qnil)
|
|
654
|
+
return;
|
|
655
|
+
long threads = NUM2LONG(v);
|
|
656
|
+
if (threads != 1)
|
|
657
|
+
rb_raise(
|
|
658
|
+
rb_eArgError,
|
|
659
|
+
"threads: is not supported by the runtime; run batches in application workers instead");
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
static size_t prefix_initial_target(uint32_t max_tokens) {
|
|
663
|
+
if (max_tokens == 0)
|
|
664
|
+
return 0;
|
|
665
|
+
|
|
666
|
+
size_t target = (size_t)max_tokens;
|
|
667
|
+
if (target > SE_PREFIX_MAX_BYTES / SE_PREFIX_BYTES_PER_TOKEN)
|
|
668
|
+
return SE_PREFIX_MAX_BYTES;
|
|
669
|
+
|
|
670
|
+
target *= SE_PREFIX_BYTES_PER_TOKEN;
|
|
671
|
+
if (target < SE_PREFIX_MIN_BYTES)
|
|
672
|
+
target = SE_PREFIX_MIN_BYTES;
|
|
673
|
+
if (target > SE_PREFIX_MAX_BYTES)
|
|
674
|
+
target = SE_PREFIX_MAX_BYTES;
|
|
675
|
+
return target;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
static size_t prefix_boundary_len(const se_model_t *model, VALUE text, size_t target) {
|
|
679
|
+
size_t full_len = (size_t)RSTRING_LEN(text);
|
|
680
|
+
if (target >= full_len)
|
|
681
|
+
return full_len;
|
|
682
|
+
|
|
683
|
+
const uint8_t *ptr = (const uint8_t *)RSTRING_PTR(text);
|
|
684
|
+
return se_prefix_boundary_len(model, ptr, full_len, target, SE_PREFIX_BACKSCAN_BYTES);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
static size_t grow_target(size_t target, size_t full_len) {
|
|
688
|
+
if (target == 0 || target > full_len / 2)
|
|
689
|
+
return full_len;
|
|
690
|
+
return target * 2;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
static size_t resolve_copy_len(const se_model_t *model, VALUE text, size_t *target,
|
|
694
|
+
uint32_t max_tokens) {
|
|
695
|
+
size_t full_len = (size_t)RSTRING_LEN(text);
|
|
696
|
+
if (max_tokens == 0 || *target == 0 || *target >= full_len) {
|
|
697
|
+
*target = full_len;
|
|
698
|
+
return full_len;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
for (;;) {
|
|
702
|
+
size_t copy_len = prefix_boundary_len(model, text, *target);
|
|
703
|
+
if (copy_len)
|
|
704
|
+
return copy_len;
|
|
705
|
+
if (*target >= full_len) {
|
|
706
|
+
*target = full_len;
|
|
707
|
+
return full_len;
|
|
708
|
+
}
|
|
709
|
+
*target = grow_target(*target, full_len);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
typedef struct {
|
|
714
|
+
VALUE snapshot;
|
|
715
|
+
int is_array;
|
|
716
|
+
} text_source_t;
|
|
717
|
+
|
|
718
|
+
static VALUE text_at(const text_source_t *src, size_t i) {
|
|
719
|
+
return RARRAY_AREF(src->snapshot, (long)i);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
static VALUE snapshot_texts(VALUE texts, int is_array, size_t count) {
|
|
723
|
+
VALUE snapshot = rb_ary_new_capa((long)count);
|
|
724
|
+
for (size_t i = 0; i < count; i++) {
|
|
725
|
+
VALUE s = is_array ? rb_ary_entry(texts, (long)i) : texts;
|
|
726
|
+
Check_Type(s, T_STRING);
|
|
727
|
+
check_text_encoding_at(s, is_array ? (long)i : -1);
|
|
728
|
+
rb_ary_push(snapshot, s);
|
|
729
|
+
}
|
|
730
|
+
return snapshot;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
static int build_input_indexed(const text_source_t *src, const size_t *indices,
|
|
734
|
+
const size_t *copy_lens, size_t count, batch_input_t *input,
|
|
735
|
+
size_t *total_bytes) {
|
|
736
|
+
memset(input, 0, sizeof(*input));
|
|
737
|
+
|
|
738
|
+
size_t total = 0;
|
|
739
|
+
for (size_t j = 0; j < count; j++) {
|
|
740
|
+
if (!checked_add_size(total, copy_lens[j], &total))
|
|
741
|
+
return 0;
|
|
742
|
+
}
|
|
743
|
+
|
|
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)) {
|
|
754
|
+
return 0;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
uint8_t *allocation = (uint8_t *)malloc(allocation_bytes);
|
|
758
|
+
if (!allocation)
|
|
759
|
+
return 0;
|
|
760
|
+
|
|
761
|
+
size_t *offsets = (size_t *)allocation;
|
|
762
|
+
size_t *lengths = (size_t *)(allocation + offsets_bytes);
|
|
763
|
+
uint8_t *buf = allocation + meta_bytes;
|
|
764
|
+
|
|
765
|
+
size_t cursor = 0;
|
|
766
|
+
for (size_t j = 0; j < count; j++) {
|
|
767
|
+
VALUE s = text_at(src, indices[j]);
|
|
768
|
+
size_t len = copy_lens[j];
|
|
769
|
+
if (len)
|
|
770
|
+
memcpy(buf + cursor, RSTRING_PTR(s), len);
|
|
771
|
+
offsets[j] = cursor;
|
|
772
|
+
lengths[j] = len;
|
|
773
|
+
cursor += len;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
input->base = buf;
|
|
777
|
+
input->offsets = offsets;
|
|
778
|
+
input->lengths = lengths;
|
|
779
|
+
input->count = count;
|
|
780
|
+
input->allocation = allocation;
|
|
781
|
+
*total_bytes = total;
|
|
782
|
+
return 1;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
static void free_input(batch_input_t *input) {
|
|
786
|
+
free(input->allocation);
|
|
787
|
+
memset(input, 0, sizeof(*input));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
typedef struct {
|
|
791
|
+
const void *ptr;
|
|
792
|
+
size_t bytes;
|
|
793
|
+
} binary_string_job_t;
|
|
794
|
+
|
|
795
|
+
static VALUE binary_string_create(VALUE arg) {
|
|
796
|
+
binary_string_job_t *job = (binary_string_job_t *)(uintptr_t)arg;
|
|
797
|
+
return rb_enc_str_new((const char *)job->ptr, (long)job->bytes, binary_encoding);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
|
|
801
|
+
if (!size_fits_long(bytes)) {
|
|
802
|
+
free(ptr);
|
|
803
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
binary_string_job_t job;
|
|
807
|
+
job.ptr = ptr;
|
|
808
|
+
job.bytes = bytes;
|
|
809
|
+
|
|
810
|
+
int state = 0;
|
|
811
|
+
VALUE result = rb_protect(binary_string_create, (VALUE)(uintptr_t)&job, &state);
|
|
812
|
+
free(ptr);
|
|
813
|
+
if (state)
|
|
814
|
+
rb_jump_tag(state);
|
|
815
|
+
return result;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
static VALUE binary_string_from_floats(float *ptr, size_t count, se_vector_format_t format) {
|
|
819
|
+
size_t bytes;
|
|
820
|
+
if (!checked_mul_size(count, vector_format_element_bytes(format), &bytes) ||
|
|
821
|
+
!size_fits_long(bytes)) {
|
|
822
|
+
free(ptr);
|
|
823
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (format == SE_VECTOR_FORMAT_F32)
|
|
827
|
+
return binary_string_from_malloc(ptr, bytes);
|
|
828
|
+
|
|
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);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
typedef struct {
|
|
840
|
+
float *out;
|
|
841
|
+
se_token_stats_t *stats;
|
|
842
|
+
size_t *targets;
|
|
843
|
+
size_t *pending;
|
|
844
|
+
size_t *copy_lens;
|
|
845
|
+
} embed_run_t;
|
|
846
|
+
|
|
847
|
+
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);
|
|
853
|
+
memset(run, 0, sizeof(*run));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t count,
|
|
857
|
+
VALUE max_tokens_opt, se_vector_format_t format,
|
|
858
|
+
se_token_stats_t *stats_out) {
|
|
859
|
+
model_wrapper_t *w = get_model(self);
|
|
860
|
+
const se_model_t *model = &w->model;
|
|
861
|
+
const uint32_t dim = model->meta.dim;
|
|
862
|
+
const uint32_t max_tokens = resolve_max_tokens(model, max_tokens_opt);
|
|
863
|
+
|
|
864
|
+
text_source_t source;
|
|
865
|
+
source.snapshot = snapshot_texts(texts, is_array, count);
|
|
866
|
+
source.is_array = is_array;
|
|
867
|
+
const text_source_t *src = &source;
|
|
868
|
+
|
|
869
|
+
size_t floats;
|
|
870
|
+
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))
|
|
874
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
875
|
+
|
|
876
|
+
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);
|
|
886
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
size_t initial = prefix_initial_target(max_tokens);
|
|
890
|
+
size_t npending = count;
|
|
891
|
+
for (size_t i = 0; i < count; i++) {
|
|
892
|
+
run.targets[i] = initial;
|
|
893
|
+
run.pending[i] = i;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
while (npending) {
|
|
897
|
+
for (size_t j = 0; j < npending; j++) {
|
|
898
|
+
size_t i = run.pending[j];
|
|
899
|
+
VALUE s = text_at(src, i);
|
|
900
|
+
run.copy_lens[j] = resolve_copy_len(model, s, &run.targets[i], max_tokens);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
batch_job_t job;
|
|
904
|
+
memset(&job, 0, sizeof(job));
|
|
905
|
+
job.model = model;
|
|
906
|
+
job.max_tokens = max_tokens;
|
|
907
|
+
job.out = run.out;
|
|
908
|
+
job.stats = run.stats;
|
|
909
|
+
job.out_index = run.pending;
|
|
910
|
+
|
|
911
|
+
size_t total_bytes = 0;
|
|
912
|
+
if (!build_input_indexed(src, run.pending, run.copy_lens, npending, &job.input,
|
|
913
|
+
&total_bytes)) {
|
|
914
|
+
embed_run_free(&run);
|
|
915
|
+
rb_raise(rb_eNoMemError, "out of memory while staging the input batch");
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
int state = 0;
|
|
919
|
+
rb_protect(protected_run_job, (VALUE)(uintptr_t)&job, &state);
|
|
920
|
+
if (state) {
|
|
921
|
+
job.cancelled = 1;
|
|
922
|
+
free_input(&job.input);
|
|
923
|
+
embed_run_free(&run);
|
|
924
|
+
rb_jump_tag(state);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
free_input(&job.input);
|
|
928
|
+
|
|
929
|
+
if (job.failed) {
|
|
930
|
+
size_t sub = job.failed_index < npending ? job.failed_index : 0;
|
|
931
|
+
size_t original = run.pending[sub];
|
|
932
|
+
|
|
933
|
+
if (job.error.status == SE_ERR_INVALID_UTF8 &&
|
|
934
|
+
run.copy_lens[sub] < (size_t)RSTRING_LEN(text_at(src, original))) {
|
|
935
|
+
run.targets[original] =
|
|
936
|
+
grow_target(run.targets[original], (size_t)RSTRING_LEN(text_at(src, original)));
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
se_error_t err = job.error;
|
|
941
|
+
embed_run_free(&run);
|
|
942
|
+
if (src->is_array) {
|
|
943
|
+
se_error_t wrapped;
|
|
944
|
+
wrapped.status = err.status;
|
|
945
|
+
snprintf(wrapped.message, sizeof(wrapped.message), "input[%zu]: %s", original,
|
|
946
|
+
err.message);
|
|
947
|
+
raise_se(&wrapped);
|
|
948
|
+
}
|
|
949
|
+
raise_se(&err);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
size_t next = 0;
|
|
953
|
+
for (size_t j = 0; j < npending; j++) {
|
|
954
|
+
size_t i = run.pending[j];
|
|
955
|
+
size_t full_len = (size_t)RSTRING_LEN(text_at(src, i));
|
|
956
|
+
if (max_tokens != 0 && run.copy_lens[j] < full_len && !run.stats[i].truncated) {
|
|
957
|
+
run.targets[i] = grow_target(run.targets[i], full_len);
|
|
958
|
+
run.pending[next++] = i;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
npending = next;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (stats_out && count)
|
|
965
|
+
*stats_out = run.stats[0];
|
|
966
|
+
|
|
967
|
+
free(run.stats);
|
|
968
|
+
run.stats = NULL;
|
|
969
|
+
free(run.targets);
|
|
970
|
+
run.targets = NULL;
|
|
971
|
+
free(run.pending);
|
|
972
|
+
run.pending = NULL;
|
|
973
|
+
free(run.copy_lens);
|
|
974
|
+
run.copy_lens = NULL;
|
|
975
|
+
|
|
976
|
+
float *out = run.out;
|
|
977
|
+
run.out = NULL;
|
|
978
|
+
VALUE snapshot_guard = source.snapshot;
|
|
979
|
+
RB_GC_GUARD(snapshot_guard);
|
|
980
|
+
(void)out_bytes;
|
|
981
|
+
return binary_string_from_floats(out, floats, format);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
static VALUE embed_batch_internal(VALUE self, VALUE texts, VALUE max_tokens_opt,
|
|
985
|
+
se_vector_format_t format) {
|
|
986
|
+
Check_Type(texts, T_ARRAY);
|
|
987
|
+
return embed_texts_internal(self, texts, 1, (size_t)RARRAY_LEN(texts), max_tokens_opt, format,
|
|
988
|
+
NULL);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
static VALUE model_embed_batch(int argc, VALUE *argv, VALUE self) {
|
|
992
|
+
VALUE texts, opts;
|
|
993
|
+
rb_scan_args(argc, argv, "1:", &texts, &opts);
|
|
994
|
+
reject_parallel_threads(opts);
|
|
995
|
+
|
|
996
|
+
VALUE max_tokens = lookup_option(opts, id_max_tokens);
|
|
997
|
+
se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
|
|
998
|
+
return embed_batch_internal(self, texts, max_tokens, format);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
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);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
1007
|
+
se_vector_format_t format, se_token_stats_t *stats) {
|
|
1008
|
+
model_wrapper_t *w = get_model(self);
|
|
1009
|
+
Check_Type(text, T_STRING);
|
|
1010
|
+
check_text_encoding(text);
|
|
1011
|
+
|
|
1012
|
+
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);
|
|
1014
|
+
|
|
1015
|
+
const uint32_t dim = w->model.meta.dim;
|
|
1016
|
+
size_t out_bytes;
|
|
1017
|
+
if (!checked_mul_size(dim, sizeof(float), &out_bytes) || !size_fits_long(out_bytes))
|
|
1018
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
1019
|
+
|
|
1020
|
+
VALUE result = rb_str_new(NULL, (long)out_bytes);
|
|
1021
|
+
rb_enc_associate(result, binary_encoding);
|
|
1022
|
+
float *out = (float *)RSTRING_PTR(result);
|
|
1023
|
+
|
|
1024
|
+
se_scratch_t scratch;
|
|
1025
|
+
se_scratch_init(&scratch);
|
|
1026
|
+
if (!se_scratch_reserve(&scratch, dim)) {
|
|
1027
|
+
se_scratch_free(&scratch);
|
|
1028
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
se_error_t err;
|
|
1032
|
+
se_error_clear(&err);
|
|
1033
|
+
se_token_stats_t local_stats;
|
|
1034
|
+
se_status_t rc =
|
|
1035
|
+
se_embed_one(&w->model, &scratch, (const uint8_t *)RSTRING_PTR(text),
|
|
1036
|
+
(size_t)RSTRING_LEN(text), resolve_max_tokens(&w->model, max_tokens_opt), out,
|
|
1037
|
+
stats ? stats : &local_stats, &err, NULL);
|
|
1038
|
+
se_scratch_free(&scratch);
|
|
1039
|
+
RB_GC_GUARD(text);
|
|
1040
|
+
|
|
1041
|
+
if (rc != SE_OK)
|
|
1042
|
+
raise_se(&err);
|
|
1043
|
+
|
|
1044
|
+
return result;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
static VALUE model_embed(int argc, VALUE *argv, VALUE self) {
|
|
1048
|
+
VALUE text, opts;
|
|
1049
|
+
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1050
|
+
reject_parallel_threads(opts);
|
|
1051
|
+
return embed_one_value(self, text, lookup_option(opts, id_max_tokens),
|
|
1052
|
+
resolve_vector_format(lookup_option(opts, id_format)), NULL);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
1056
|
+
VALUE text, opts;
|
|
1057
|
+
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1058
|
+
reject_parallel_threads(opts);
|
|
1059
|
+
|
|
1060
|
+
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);
|
|
1063
|
+
|
|
1064
|
+
VALUE hash = rb_hash_new();
|
|
1065
|
+
rb_hash_aset(hash, ID2SYM(id_vector), vector);
|
|
1066
|
+
rb_hash_aset(hash, ID2SYM(id_token_count), UINT2NUM(stats.token_count));
|
|
1067
|
+
rb_hash_aset(hash, ID2SYM(id_unk_count), UINT2NUM(stats.unk_count));
|
|
1068
|
+
rb_hash_aset(hash, ID2SYM(id_truncated), stats.truncated ? Qtrue : Qfalse);
|
|
1069
|
+
return hash;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
static VALUE model_tokenize(int argc, VALUE *argv, VALUE self) {
|
|
1073
|
+
VALUE text, opts;
|
|
1074
|
+
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1075
|
+
|
|
1076
|
+
model_wrapper_t *w = get_model(self);
|
|
1077
|
+
Check_Type(text, T_STRING);
|
|
1078
|
+
check_text_encoding(text);
|
|
1079
|
+
|
|
1080
|
+
uint32_t max_tokens = resolve_max_tokens(&w->model, lookup_option(opts, id_max_tokens));
|
|
1081
|
+
|
|
1082
|
+
se_scratch_t scratch;
|
|
1083
|
+
se_scratch_init(&scratch);
|
|
1084
|
+
if (!se_scratch_reserve(&scratch, w->model.meta.dim)) {
|
|
1085
|
+
se_scratch_free(&scratch);
|
|
1086
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
se_token_stats_t stats;
|
|
1090
|
+
se_error_t err;
|
|
1091
|
+
se_error_clear(&err);
|
|
1092
|
+
se_status_t rc = se_tokenize(&w->model, &scratch, (const uint8_t *)RSTRING_PTR(text),
|
|
1093
|
+
(size_t)RSTRING_LEN(text), max_tokens, &stats, &err, NULL);
|
|
1094
|
+
if (rc != SE_OK) {
|
|
1095
|
+
se_scratch_free(&scratch);
|
|
1096
|
+
raise_se(&err);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
VALUE ids = rb_ary_new_capa((long)stats.token_count);
|
|
1100
|
+
for (uint32_t i = 0; i < stats.token_count; i++)
|
|
1101
|
+
rb_ary_push(ids, UINT2NUM(scratch.ids[i]));
|
|
1102
|
+
|
|
1103
|
+
se_scratch_free(&scratch);
|
|
1104
|
+
RB_GC_GUARD(text);
|
|
1105
|
+
return ids;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
typedef struct {
|
|
1109
|
+
const se_model_t *model;
|
|
1110
|
+
const uint32_t *ids;
|
|
1111
|
+
size_t n_ids;
|
|
1112
|
+
float *out;
|
|
1113
|
+
se_token_stats_t stats;
|
|
1114
|
+
volatile sig_atomic_t cancelled;
|
|
1115
|
+
se_error_t error;
|
|
1116
|
+
} ids_job_t;
|
|
1117
|
+
|
|
1118
|
+
typedef struct {
|
|
1119
|
+
VALUE ids_value;
|
|
1120
|
+
const se_model_t *model;
|
|
1121
|
+
uint32_t max_tokens;
|
|
1122
|
+
uint32_t *ids;
|
|
1123
|
+
size_t n;
|
|
1124
|
+
float *out;
|
|
1125
|
+
size_t out_floats;
|
|
1126
|
+
se_vector_format_t format;
|
|
1127
|
+
se_token_stats_t *stats_out;
|
|
1128
|
+
se_token_stats_t stats;
|
|
1129
|
+
int truncated;
|
|
1130
|
+
} ids_run_t;
|
|
1131
|
+
|
|
1132
|
+
typedef struct {
|
|
1133
|
+
VALUE array;
|
|
1134
|
+
long index;
|
|
1135
|
+
unsigned long long value;
|
|
1136
|
+
} num2ull_job_t;
|
|
1137
|
+
|
|
1138
|
+
static VALUE num2ull_at_value(VALUE arg) {
|
|
1139
|
+
num2ull_job_t *job = (num2ull_job_t *)(uintptr_t)arg;
|
|
1140
|
+
job->value = NUM2ULL(rb_ary_entry(job->array, job->index));
|
|
1141
|
+
return Qnil;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
static void *ids_execute(void *arg) {
|
|
1145
|
+
ids_job_t *job = (ids_job_t *)arg;
|
|
1146
|
+
se_scratch_t scratch;
|
|
1147
|
+
se_scratch_init(&scratch);
|
|
1148
|
+
if (!se_scratch_reserve(&scratch, job->model->meta.dim)) {
|
|
1149
|
+
se_error_set(&job->error, SE_ERR_OOM, "out of memory while sizing scratch buffers");
|
|
1150
|
+
se_scratch_free(&scratch);
|
|
1151
|
+
return NULL;
|
|
1152
|
+
}
|
|
1153
|
+
se_error_clear(&job->error);
|
|
1154
|
+
se_embed_ids(job->model, &scratch, job->ids, job->n_ids, job->out, &job->stats, &job->error,
|
|
1155
|
+
&job->cancelled);
|
|
1156
|
+
se_scratch_free(&scratch);
|
|
1157
|
+
return NULL;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
static void ids_unblock_cancel(void *arg) {
|
|
1161
|
+
ids_job_t *job = (ids_job_t *)arg;
|
|
1162
|
+
if (job)
|
|
1163
|
+
job->cancelled = 1;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
static VALUE embed_token_ids_body(VALUE arg) {
|
|
1167
|
+
ids_run_t *run = (ids_run_t *)(uintptr_t)arg;
|
|
1168
|
+
const uint32_t vocab_size = run->model->meta.vocab_size;
|
|
1169
|
+
|
|
1170
|
+
for (size_t i = 0; i < run->n; i++) {
|
|
1171
|
+
num2ull_job_t conv;
|
|
1172
|
+
conv.array = run->ids_value;
|
|
1173
|
+
conv.index = (long)i;
|
|
1174
|
+
conv.value = 0;
|
|
1175
|
+
|
|
1176
|
+
int state = 0;
|
|
1177
|
+
rb_protect(num2ull_at_value, (VALUE)(uintptr_t)&conv, &state);
|
|
1178
|
+
if (state)
|
|
1179
|
+
rb_jump_tag(state);
|
|
1180
|
+
|
|
1181
|
+
if (conv.value >= vocab_size)
|
|
1182
|
+
rb_raise(rb_eArgError, "token id at index %zu is out of range", i);
|
|
1183
|
+
run->ids[i] = (uint32_t)conv.value;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
run->out = (float *)calloc(run->model->meta.dim ? run->model->meta.dim : 1, sizeof(float));
|
|
1187
|
+
if (!run->out)
|
|
1188
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
1189
|
+
|
|
1190
|
+
ids_job_t job;
|
|
1191
|
+
memset(&job, 0, sizeof(job));
|
|
1192
|
+
job.model = run->model;
|
|
1193
|
+
job.ids = run->ids;
|
|
1194
|
+
job.n_ids = run->n;
|
|
1195
|
+
job.out = run->out;
|
|
1196
|
+
se_error_clear(&job.error);
|
|
1197
|
+
|
|
1198
|
+
if (run->n >= SE_IDS_GVL_UNLOCK_THRESHOLD) {
|
|
1199
|
+
rb_thread_call_without_gvl(ids_execute, &job, ids_unblock_cancel, &job);
|
|
1200
|
+
rb_thread_check_ints();
|
|
1201
|
+
if (job.cancelled)
|
|
1202
|
+
rb_raise(rb_eInterrupt, "operation cancelled");
|
|
1203
|
+
} else {
|
|
1204
|
+
ids_execute(&job);
|
|
1205
|
+
rb_thread_check_ints();
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (job.error.status != SE_OK)
|
|
1209
|
+
raise_se(&job.error);
|
|
1210
|
+
|
|
1211
|
+
run->stats = job.stats;
|
|
1212
|
+
if (run->truncated)
|
|
1213
|
+
run->stats.truncated = 1u;
|
|
1214
|
+
if (run->stats_out)
|
|
1215
|
+
*run->stats_out = run->stats;
|
|
1216
|
+
|
|
1217
|
+
free(run->ids);
|
|
1218
|
+
run->ids = NULL;
|
|
1219
|
+
|
|
1220
|
+
float *out = run->out;
|
|
1221
|
+
run->out = NULL;
|
|
1222
|
+
return binary_string_from_floats(out, run->out_floats, run->format);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
static VALUE embed_token_ids_ensure(VALUE arg) {
|
|
1226
|
+
ids_run_t *run = (ids_run_t *)(uintptr_t)arg;
|
|
1227
|
+
free(run->ids);
|
|
1228
|
+
free(run->out);
|
|
1229
|
+
run->ids = NULL;
|
|
1230
|
+
run->out = NULL;
|
|
1231
|
+
return Qnil;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens_opt,
|
|
1235
|
+
se_vector_format_t format, se_token_stats_t *stats_out) {
|
|
1236
|
+
model_wrapper_t *w = get_model(self);
|
|
1237
|
+
Check_Type(ids_value, T_ARRAY);
|
|
1238
|
+
|
|
1239
|
+
const uint32_t max_tokens = resolve_max_tokens(&w->model, max_tokens_opt);
|
|
1240
|
+
long n_long = RARRAY_LEN(ids_value);
|
|
1241
|
+
size_t n = (size_t)n_long;
|
|
1242
|
+
int truncated = 0;
|
|
1243
|
+
|
|
1244
|
+
if (max_tokens != 0 && n > (size_t)max_tokens) {
|
|
1245
|
+
n = (size_t)max_tokens;
|
|
1246
|
+
truncated = 1;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
size_t ids_bytes;
|
|
1250
|
+
if (!checked_mul_size(n ? n : 1, sizeof(uint32_t), &ids_bytes))
|
|
1251
|
+
rb_raise(rb_eArgError, "token id array is too large");
|
|
1252
|
+
|
|
1253
|
+
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))
|
|
1256
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
1257
|
+
|
|
1258
|
+
ids_run_t run;
|
|
1259
|
+
memset(&run, 0, sizeof(run));
|
|
1260
|
+
run.ids_value = ids_value;
|
|
1261
|
+
run.model = &w->model;
|
|
1262
|
+
run.max_tokens = max_tokens;
|
|
1263
|
+
run.n = n;
|
|
1264
|
+
run.out_floats = w->model.meta.dim;
|
|
1265
|
+
run.format = format;
|
|
1266
|
+
run.stats_out = stats_out;
|
|
1267
|
+
run.truncated = truncated;
|
|
1268
|
+
run.ids = (uint32_t *)malloc(ids_bytes);
|
|
1269
|
+
if (!run.ids)
|
|
1270
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
1271
|
+
|
|
1272
|
+
VALUE result = rb_ensure(embed_token_ids_body, (VALUE)(uintptr_t)&run, embed_token_ids_ensure,
|
|
1273
|
+
(VALUE)(uintptr_t)&run);
|
|
1274
|
+
RB_GC_GUARD(ids_value);
|
|
1275
|
+
return result;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
static VALUE model_embed_token_ids(int argc, VALUE *argv, VALUE self) {
|
|
1279
|
+
VALUE ids_value, opts;
|
|
1280
|
+
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1281
|
+
reject_parallel_threads(opts);
|
|
1282
|
+
return embed_token_ids_value(self, ids_value, lookup_option(opts, id_max_tokens),
|
|
1283
|
+
resolve_vector_format(lookup_option(opts, id_format)), NULL);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
static VALUE model_embed_token_ids_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
1287
|
+
VALUE ids_value, opts;
|
|
1288
|
+
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1289
|
+
reject_parallel_threads(opts);
|
|
1290
|
+
|
|
1291
|
+
se_token_stats_t stats;
|
|
1292
|
+
memset(&stats, 0, sizeof(stats));
|
|
1293
|
+
VALUE vector =
|
|
1294
|
+
embed_token_ids_value(self, ids_value, lookup_option(opts, id_max_tokens),
|
|
1295
|
+
resolve_vector_format(lookup_option(opts, id_format)), &stats);
|
|
1296
|
+
|
|
1297
|
+
VALUE hash = rb_hash_new();
|
|
1298
|
+
rb_hash_aset(hash, ID2SYM(id_vector), vector);
|
|
1299
|
+
rb_hash_aset(hash, ID2SYM(id_token_count), UINT2NUM(stats.token_count));
|
|
1300
|
+
rb_hash_aset(hash, ID2SYM(id_unk_count), UINT2NUM(stats.unk_count));
|
|
1301
|
+
rb_hash_aset(hash, ID2SYM(id_truncated), stats.truncated ? Qtrue : Qfalse);
|
|
1302
|
+
return hash;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
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
|
+
typedef struct {
|
|
1320
|
+
VALUE matrix;
|
|
1321
|
+
topk_job_t job;
|
|
1322
|
+
float *q_copy;
|
|
1323
|
+
float *matrix_copy;
|
|
1324
|
+
size_t matrix_bytes;
|
|
1325
|
+
int release_gvl;
|
|
1326
|
+
} topk_run_t;
|
|
1327
|
+
|
|
1328
|
+
static int ptr_is_float_aligned(const void *ptr) {
|
|
1329
|
+
return ((uintptr_t)ptr % sizeof(float)) == 0;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
static void topk_unblock_cancel(void *arg) {
|
|
1333
|
+
topk_job_t *job = (topk_job_t *)arg;
|
|
1334
|
+
if (job)
|
|
1335
|
+
job->cancelled = 1;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
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
|
+
static VALUE topk_body(VALUE arg) {
|
|
1788
|
+
topk_run_t *run = (topk_run_t *)(uintptr_t)arg;
|
|
1789
|
+
const char *matrix_ptr = RSTRING_PTR(run->matrix);
|
|
1790
|
+
|
|
1791
|
+
if (run->matrix_copy) {
|
|
1792
|
+
memcpy(run->matrix_copy, matrix_ptr, run->matrix_bytes);
|
|
1793
|
+
run->job.m = run->matrix_copy;
|
|
1794
|
+
} else {
|
|
1795
|
+
run->job.m = matrix_ptr;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
if (run->job.cosine) {
|
|
1799
|
+
float query_sq = dot_product_unrolled(run->job.q, run->job.q, run->job.dim);
|
|
1800
|
+
if (!(query_sq > 0.0f))
|
|
1801
|
+
rb_raise(rb_eArgError, "cosine_top_k needs a query with a non-zero norm");
|
|
1802
|
+
run->job.inv_query_norm = 1.0f / sqrtf(query_sq);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
if (run->release_gvl) {
|
|
1806
|
+
rb_thread_call_without_gvl(topk_execute, &run->job, topk_unblock_cancel, &run->job);
|
|
1807
|
+
rb_thread_check_ints();
|
|
1808
|
+
} else {
|
|
1809
|
+
topk_execute(&run->job);
|
|
1810
|
+
rb_thread_check_ints();
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
if (run->job.cancelled)
|
|
1814
|
+
rb_raise(rb_eInterrupt, "operation cancelled");
|
|
1815
|
+
|
|
1816
|
+
VALUE result = rb_ary_new_capa(run->job.k);
|
|
1817
|
+
for (long i = 0; i < run->job.k; i++) {
|
|
1818
|
+
if (run->job.best_score[i] == -INFINITY)
|
|
1819
|
+
break;
|
|
1820
|
+
VALUE pair = rb_ary_new_capa(2);
|
|
1821
|
+
rb_ary_push(pair, SIZET2NUM(run->job.best_idx[i]));
|
|
1822
|
+
rb_ary_push(pair, DBL2NUM((double)run->job.best_score[i]));
|
|
1823
|
+
rb_ary_push(result, pair);
|
|
1824
|
+
}
|
|
1825
|
+
return result;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
static VALUE topk_ensure(VALUE arg) {
|
|
1829
|
+
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);
|
|
1834
|
+
run->q_copy = NULL;
|
|
1835
|
+
run->matrix_copy = NULL;
|
|
1836
|
+
run->job.best_idx = NULL;
|
|
1837
|
+
run->job.best_score = NULL;
|
|
1838
|
+
return Qnil;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
static size_t topk_required_dim(VALUE opts) {
|
|
1842
|
+
VALUE v = lookup_option(opts, id_dim);
|
|
1843
|
+
if (v == Qundef || v == Qnil)
|
|
1844
|
+
rb_raise(rb_eArgError, "dim: is required (raw blobs carry no dimension or format tag); "
|
|
1845
|
+
"pass dim: model.dim, or call model.cosine_top_k / model.dot_top_k");
|
|
1846
|
+
|
|
1847
|
+
long dim = NUM2LONG(v);
|
|
1848
|
+
if (dim < 1)
|
|
1849
|
+
rb_raise(rb_eArgError, "dim: must be >= 1");
|
|
1850
|
+
return (size_t)dim;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
static void topk_check_matrix(VALUE matrix, size_t matrix_bytes, se_vector_format_t format,
|
|
1854
|
+
VALUE opts, int *release_gvl, int *needs_copy) {
|
|
1855
|
+
int large = matrix_bytes >= SE_TOPK_GVL_UNLOCK_THRESHOLD;
|
|
1856
|
+
int aligned = format != SE_VECTOR_FORMAT_F32 || ptr_is_float_aligned(RSTRING_PTR(matrix));
|
|
1857
|
+
VALUE allow_unfrozen = lookup_option(opts, id_allow_unfrozen);
|
|
1858
|
+
|
|
1859
|
+
*release_gvl = 0;
|
|
1860
|
+
*needs_copy = 0;
|
|
1861
|
+
|
|
1862
|
+
if (!large) {
|
|
1863
|
+
*needs_copy = !aligned;
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
if (!aligned)
|
|
1868
|
+
rb_raise(rb_eArgError,
|
|
1869
|
+
"matrix blob is not 4-byte aligned; copying %zu bytes would be silently "
|
|
1870
|
+
"expensive - pass a freshly packed String instead of a byteslice",
|
|
1871
|
+
matrix_bytes);
|
|
1872
|
+
|
|
1873
|
+
if (RB_OBJ_FROZEN(matrix)) {
|
|
1874
|
+
*release_gvl = 1;
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
if (allow_unfrozen != Qundef && RTEST(allow_unfrozen))
|
|
1879
|
+
return;
|
|
1880
|
+
|
|
1881
|
+
rb_raise(rb_eArgError,
|
|
1882
|
+
"a matrix of %zu bytes is scanned with the GVL released and must be frozen so that "
|
|
1883
|
+
"no other thread can mutate it; call matrix.freeze, or pass allow_unfrozen: true to "
|
|
1884
|
+
"scan it while holding the GVL",
|
|
1885
|
+
matrix_bytes);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
|
|
1889
|
+
VALUE query, matrix, k_val, opts;
|
|
1890
|
+
rb_scan_args(argc, argv, "3:", &query, &matrix, &k_val, &opts);
|
|
1891
|
+
(void)self;
|
|
1892
|
+
|
|
1893
|
+
Check_Type(query, T_STRING);
|
|
1894
|
+
Check_Type(matrix, T_STRING);
|
|
1895
|
+
|
|
1896
|
+
se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
|
|
1897
|
+
size_t element_bytes = vector_format_element_bytes(format);
|
|
1898
|
+
size_t dim = topk_required_dim(opts);
|
|
1899
|
+
|
|
1900
|
+
size_t row_bytes;
|
|
1901
|
+
if (!checked_mul_size(dim, element_bytes, &row_bytes) || !size_fits_long(row_bytes))
|
|
1902
|
+
rb_raise(rb_eArgError, "dim: is too large");
|
|
1903
|
+
|
|
1904
|
+
if ((size_t)RSTRING_LEN(query) != row_bytes)
|
|
1905
|
+
rb_raise(rb_eArgError, "query is %ld bytes but dim: %zu with format %s needs %zu bytes",
|
|
1906
|
+
RSTRING_LEN(query), dim, format == SE_VECTOR_FORMAT_F16 ? ":f16" : ":f32",
|
|
1907
|
+
row_bytes);
|
|
1908
|
+
|
|
1909
|
+
long mbytes = RSTRING_LEN(matrix);
|
|
1910
|
+
if ((size_t)mbytes % row_bytes != 0)
|
|
1911
|
+
rb_raise(rb_eArgError, "matrix is %ld bytes, which is not a whole number of %zu-byte rows",
|
|
1912
|
+
mbytes, row_bytes);
|
|
1913
|
+
|
|
1914
|
+
size_t rows = (size_t)mbytes / row_bytes;
|
|
1915
|
+
long k = NUM2LONG(k_val);
|
|
1916
|
+
if (k < 1)
|
|
1917
|
+
rb_raise(rb_eArgError, "k must be >= 1");
|
|
1918
|
+
if (rows == 0)
|
|
1919
|
+
return rb_ary_new();
|
|
1920
|
+
if ((size_t)k > rows)
|
|
1921
|
+
k = (long)rows;
|
|
1922
|
+
|
|
1923
|
+
size_t matrix_bytes = (size_t)mbytes;
|
|
1924
|
+
int release_gvl = 0;
|
|
1925
|
+
int needs_copy = 0;
|
|
1926
|
+
topk_check_matrix(matrix, matrix_bytes, format, opts, &release_gvl, &needs_copy);
|
|
1927
|
+
|
|
1928
|
+
topk_run_t run;
|
|
1929
|
+
memset(&run, 0, sizeof(run));
|
|
1930
|
+
run.matrix = matrix;
|
|
1931
|
+
run.matrix_bytes = matrix_bytes;
|
|
1932
|
+
run.release_gvl = release_gvl;
|
|
1933
|
+
run.job.format = format;
|
|
1934
|
+
run.job.dim = dim;
|
|
1935
|
+
run.job.rows = rows;
|
|
1936
|
+
run.job.k = k;
|
|
1937
|
+
run.job.cosine = cosine;
|
|
1938
|
+
run.job.inv_query_norm = 1.0f;
|
|
1939
|
+
|
|
1940
|
+
size_t q_float_bytes;
|
|
1941
|
+
if (!checked_mul_size(dim, sizeof(float), &q_float_bytes))
|
|
1942
|
+
rb_raise(rb_eArgError, "dim: is too large");
|
|
1943
|
+
|
|
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));
|
|
1947
|
+
if (needs_copy)
|
|
1948
|
+
run.matrix_copy = (float *)malloc(matrix_bytes ? matrix_bytes : 1);
|
|
1949
|
+
if (!run.q_copy || !run.job.best_idx || !run.job.best_score ||
|
|
1950
|
+
(needs_copy && !run.matrix_copy)) {
|
|
1951
|
+
topk_ensure((VALUE)(uintptr_t)&run);
|
|
1952
|
+
rb_raise(rb_eNoMemError, "out of memory");
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
if (format == SE_VECTOR_FORMAT_F16)
|
|
1956
|
+
decode_f16_to_floats(run.q_copy, (const uint8_t *)RSTRING_PTR(query), dim);
|
|
1957
|
+
else
|
|
1958
|
+
memcpy(run.q_copy, RSTRING_PTR(query), row_bytes);
|
|
1959
|
+
run.job.q = run.q_copy;
|
|
1960
|
+
|
|
1961
|
+
for (long i = 0; i < k; i++)
|
|
1962
|
+
run.job.best_score[i] = -INFINITY;
|
|
1963
|
+
|
|
1964
|
+
VALUE result =
|
|
1965
|
+
rb_ensure(topk_body, (VALUE)(uintptr_t)&run, topk_ensure, (VALUE)(uintptr_t)&run);
|
|
1966
|
+
RB_GC_GUARD(query);
|
|
1967
|
+
RB_GC_GUARD(matrix);
|
|
1968
|
+
return result;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
static VALUE se_cosine_top_k(int argc, VALUE *argv, VALUE self) {
|
|
1972
|
+
return top_k_impl(argc, argv, self, 1);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
static VALUE se_dot_top_k(int argc, VALUE *argv, VALUE self) {
|
|
1976
|
+
return top_k_impl(argc, argv, self, 0);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
static VALUE se_encode_f16(VALUE self, VALUE ary) {
|
|
1980
|
+
(void)self;
|
|
1981
|
+
Check_Type(ary, T_ARRAY);
|
|
1982
|
+
|
|
1983
|
+
long n = RARRAY_LEN(ary);
|
|
1984
|
+
size_t bytes;
|
|
1985
|
+
if (!checked_mul_size((size_t)n, 2, &bytes) || !size_fits_long(bytes))
|
|
1986
|
+
rb_raise(rb_eArgError, "vector is too large");
|
|
1987
|
+
|
|
1988
|
+
VALUE out = rb_str_new(NULL, (long)bytes);
|
|
1989
|
+
rb_enc_associate(out, binary_encoding);
|
|
1990
|
+
for (long i = 0; i < n; i++) {
|
|
1991
|
+
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));
|
|
1993
|
+
}
|
|
1994
|
+
return out;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
static VALUE se_decode_f16(VALUE self, VALUE blob) {
|
|
1998
|
+
(void)self;
|
|
1999
|
+
Check_Type(blob, T_STRING);
|
|
2000
|
+
|
|
2001
|
+
long n = RSTRING_LEN(blob);
|
|
2002
|
+
if (n % 2 != 0)
|
|
2003
|
+
rb_raise(rb_eArgError, "f16 blob byte size must be a multiple of 2");
|
|
2004
|
+
|
|
2005
|
+
VALUE out = rb_ary_new_capa(n / 2);
|
|
2006
|
+
for (long i = 0; i < n / 2; i++) {
|
|
2007
|
+
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))));
|
|
2009
|
+
}
|
|
2010
|
+
RB_GC_GUARD(blob);
|
|
2011
|
+
return out;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
|
|
2015
|
+
select_f16_backend();
|
|
2016
|
+
binary_encoding = rb_ascii8bit_encoding();
|
|
2017
|
+
utf8_encoding = rb_utf8_encoding();
|
|
2018
|
+
id_join = rb_intern("join");
|
|
2019
|
+
id_kill = rb_intern("kill");
|
|
2020
|
+
id_max_tokens = rb_intern("max_tokens");
|
|
2021
|
+
id_threads = rb_intern("threads");
|
|
2022
|
+
id_format = rb_intern("format");
|
|
2023
|
+
id_blocking_p = rb_intern("blocking?");
|
|
2024
|
+
id_vector = rb_intern("vector");
|
|
2025
|
+
id_token_count = rb_intern("token_count");
|
|
2026
|
+
id_unk_count = rb_intern("unk_count");
|
|
2027
|
+
id_truncated = rb_intern("truncated");
|
|
2028
|
+
id_dim = rb_intern("dim");
|
|
2029
|
+
id_allow_unfrozen = rb_intern("allow_unfrozen");
|
|
2030
|
+
|
|
2031
|
+
mStaticEmbeddings = rb_define_module("StaticEmbeddings");
|
|
2032
|
+
cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
|
|
2033
|
+
|
|
2034
|
+
eError = rb_define_class_under(mStaticEmbeddings, "Error", rb_eStandardError);
|
|
2035
|
+
eInvalidModel = rb_define_class_under(mStaticEmbeddings, "InvalidModelError", eError);
|
|
2036
|
+
eUnsupportedModel = rb_define_class_under(mStaticEmbeddings, "UnsupportedModelError", eError);
|
|
2037
|
+
eEncodingError = rb_define_class_under(mStaticEmbeddings, "EncodingError", eError);
|
|
2038
|
+
eEmptyInput = rb_define_class_under(mStaticEmbeddings, "EmptyInputError", eError);
|
|
2039
|
+
|
|
2040
|
+
cModel = rb_define_class_under(mStaticEmbeddings, "Model", rb_cObject);
|
|
2041
|
+
rb_define_alloc_func(cModel, model_alloc);
|
|
2042
|
+
rb_define_method(cModel, "initialize", model_initialize, 1);
|
|
2043
|
+
rb_define_method(cModel, "close", model_close, 0);
|
|
2044
|
+
rb_define_method(cModel, "closed?", model_closed_p, 0);
|
|
2045
|
+
rb_define_method(cModel, "dim", model_dim, 0);
|
|
2046
|
+
rb_define_method(cModel, "vocab_size", model_vocab_size, 0);
|
|
2047
|
+
rb_define_method(cModel, "max_tokens", model_max_tokens, 0);
|
|
2048
|
+
rb_define_method(cModel, "normalized?", model_normalized_p, 0);
|
|
2049
|
+
rb_define_method(cModel, "lowercase?", model_lowercase_p, 0);
|
|
2050
|
+
rb_define_method(cModel, "unk_id", model_unk_id, 0);
|
|
2051
|
+
rb_define_method(cModel, "provenance_json", model_provenance_json, 0);
|
|
2052
|
+
rb_define_method(cModel, "mapped_bytes", model_mapped_bytes, 0);
|
|
2053
|
+
rb_define_method(cModel, "warmup!", model_warmup, 0);
|
|
2054
|
+
rb_define_method(cModel, "embed", model_embed, -1);
|
|
2055
|
+
rb_define_method(cModel, "embed_batch", model_embed_batch, -1);
|
|
2056
|
+
rb_define_method(cModel, "embed_with_stats", model_embed_with_stats, -1);
|
|
2057
|
+
rb_define_method(cModel, "tokenize", model_tokenize, -1);
|
|
2058
|
+
rb_define_method(cModel, "embed_token_ids", model_embed_token_ids, -1);
|
|
2059
|
+
rb_define_method(cModel, "embed_token_ids_with_stats", model_embed_token_ids_with_stats, -1);
|
|
2060
|
+
|
|
2061
|
+
rb_define_singleton_method(mStaticEmbeddings, "cosine_top_k", se_cosine_top_k, -1);
|
|
2062
|
+
rb_define_singleton_method(mStaticEmbeddings, "dot_top_k", se_dot_top_k, -1);
|
|
2063
|
+
rb_define_singleton_method(mStaticEmbeddings, "encode_f16", se_encode_f16, 1);
|
|
2064
|
+
rb_define_singleton_method(mStaticEmbeddings, "decode_f16", se_decode_f16, 1);
|
|
2065
|
+
rb_define_singleton_method(mStaticEmbeddings, "simd_backend", se_simd_backend, 0);
|
|
2066
|
+
|
|
2067
|
+
rb_define_const(mStaticEmbeddings, "FORMAT_VERSION", UINT2NUM(SE_FORMAT_VERSION));
|
|
2068
|
+
rb_define_const(mStaticEmbeddings, "TOKENIZER_BERT_WORDPIECE_V1",
|
|
2069
|
+
UINT2NUM(SE_TOKENIZER_BERT_WORDPIECE_V1));
|
|
2070
|
+
}
|