static_embeddings 0.1.1 → 0.1.3
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 +197 -0
- data/README.md +172 -275
- data/Rakefile +95 -0
- data/benchmark/_support.rb +196 -0
- data/benchmark/core_paths.rb +57 -0
- data/benchmark/gvl_threshold.rb +48 -0
- data/docs/ARCHITECTURE.md +56 -6
- data/docs/BENCHMARKING.md +87 -0
- data/docs/LIMITATIONS.md +91 -0
- data/docs/MODEL_AUDIT.md +29 -10
- data/docs/PERFORMANCE.md +112 -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 +238 -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 +277 -744
- data/lib/static_embeddings/format.rb +22 -11
- data/lib/static_embeddings/version.rb +1 -1
- data/static_embeddings.gemspec +2 -0
- data/tools/benchmark.rb +13 -4
- metadata +10 -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 {
|
|
@@ -648,6 +503,51 @@ static VALUE lookup_option(VALUE opts, ID id) {
|
|
|
648
503
|
return rb_hash_lookup2(opts, ID2SYM(id), Qundef);
|
|
649
504
|
}
|
|
650
505
|
|
|
506
|
+
typedef struct {
|
|
507
|
+
const ID *allowed;
|
|
508
|
+
size_t count;
|
|
509
|
+
} keyword_check_t;
|
|
510
|
+
|
|
511
|
+
static int reject_unknown_keyword_i(VALUE key, VALUE value, VALUE arg) {
|
|
512
|
+
const keyword_check_t *check = (const keyword_check_t *)arg;
|
|
513
|
+
(void)value;
|
|
514
|
+
|
|
515
|
+
if (!SYMBOL_P(key))
|
|
516
|
+
rb_raise(rb_eArgError, "keyword must be a Symbol");
|
|
517
|
+
|
|
518
|
+
ID id = SYM2ID(key);
|
|
519
|
+
for (size_t i = 0; i < check->count; i++) {
|
|
520
|
+
if (id == check->allowed[i])
|
|
521
|
+
return ST_CONTINUE;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
VALUE names = rb_ary_new_capa((long)check->count);
|
|
525
|
+
for (size_t i = 0; i < check->count; i++)
|
|
526
|
+
rb_ary_push(names, rb_sprintf(":%s", rb_id2name(check->allowed[i])));
|
|
527
|
+
|
|
528
|
+
const char *name = rb_id2name(id);
|
|
529
|
+
rb_raise(rb_eArgError, "unknown keyword: :%s (accepted: %" PRIsVALUE ")", name ? name : "?",
|
|
530
|
+
rb_ary_join(names, rb_str_new_cstr(", ")));
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
static void check_keywords(VALUE opts, const ID *allowed, size_t count) {
|
|
534
|
+
if (NIL_P(opts))
|
|
535
|
+
return;
|
|
536
|
+
if (!RB_TYPE_P(opts, T_HASH))
|
|
537
|
+
rb_raise(rb_eArgError, "keywords must be a Hash");
|
|
538
|
+
|
|
539
|
+
keyword_check_t check;
|
|
540
|
+
check.allowed = allowed;
|
|
541
|
+
check.count = count;
|
|
542
|
+
rb_hash_foreach(opts, reject_unknown_keyword_i, (VALUE)&check);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
#define SE_CHECK_KEYWORDS(opts, ...) \
|
|
546
|
+
do { \
|
|
547
|
+
const ID se_allowed_[] = {__VA_ARGS__}; \
|
|
548
|
+
check_keywords((opts), se_allowed_, sizeof(se_allowed_) / sizeof(se_allowed_[0])); \
|
|
549
|
+
} while (0)
|
|
550
|
+
|
|
651
551
|
static void reject_parallel_threads(VALUE opts) {
|
|
652
552
|
VALUE v = lookup_option(opts, id_threads);
|
|
653
553
|
if (v == Qundef || v == Qnil)
|
|
@@ -719,12 +619,13 @@ static VALUE text_at(const text_source_t *src, size_t i) {
|
|
|
719
619
|
return RARRAY_AREF(src->snapshot, (long)i);
|
|
720
620
|
}
|
|
721
621
|
|
|
722
|
-
static VALUE snapshot_texts(VALUE texts, int is_array, size_t count
|
|
622
|
+
static VALUE snapshot_texts(VALUE texts, int is_array, size_t count,
|
|
623
|
+
se_encoding_validation_t validation) {
|
|
723
624
|
VALUE snapshot = rb_ary_new_capa((long)count);
|
|
724
625
|
for (size_t i = 0; i < count; i++) {
|
|
725
626
|
VALUE s = is_array ? rb_ary_entry(texts, (long)i) : texts;
|
|
726
627
|
Check_Type(s, T_STRING);
|
|
727
|
-
|
|
628
|
+
check_text_encoding_mode(s, is_array ? (long)i : -1, validation);
|
|
728
629
|
rb_ary_push(snapshot, s);
|
|
729
630
|
}
|
|
730
631
|
return snapshot;
|
|
@@ -737,24 +638,26 @@ static int build_input_indexed(const text_source_t *src, const size_t *indices,
|
|
|
737
638
|
|
|
738
639
|
size_t total = 0;
|
|
739
640
|
for (size_t j = 0; j < count; j++) {
|
|
740
|
-
if (!
|
|
641
|
+
if (!se_checked_add_size(total, copy_lens[j], &total))
|
|
741
642
|
return 0;
|
|
742
643
|
}
|
|
743
644
|
|
|
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
|
-
!
|
|
645
|
+
size_t offsets_bytes = 0;
|
|
646
|
+
size_t lengths_bytes = 0;
|
|
647
|
+
size_t meta_bytes = 0;
|
|
648
|
+
size_t allocation_bytes = 0;
|
|
649
|
+
size_t payload_bytes = total;
|
|
650
|
+
size_t malloc_bytes = 0;
|
|
651
|
+
|
|
652
|
+
if (!se_array_bytes(count, sizeof(size_t), &offsets_bytes) ||
|
|
653
|
+
!se_array_bytes(count, sizeof(size_t), &lengths_bytes) ||
|
|
654
|
+
!se_checked_add_size(offsets_bytes, lengths_bytes, &meta_bytes) ||
|
|
655
|
+
!se_checked_add_size(meta_bytes, payload_bytes, &allocation_bytes) ||
|
|
656
|
+
!se_alloc_bytes(allocation_bytes, 1u, &malloc_bytes)) {
|
|
754
657
|
return 0;
|
|
755
658
|
}
|
|
756
659
|
|
|
757
|
-
uint8_t *allocation = (uint8_t *)
|
|
660
|
+
uint8_t *allocation = (uint8_t *)se_malloc(SE_ALLOC_BATCH_INPUT, malloc_bytes);
|
|
758
661
|
if (!allocation)
|
|
759
662
|
return 0;
|
|
760
663
|
|
|
@@ -783,7 +686,7 @@ static int build_input_indexed(const text_source_t *src, const size_t *indices,
|
|
|
783
686
|
}
|
|
784
687
|
|
|
785
688
|
static void free_input(batch_input_t *input) {
|
|
786
|
-
|
|
689
|
+
se_free(input->allocation);
|
|
787
690
|
memset(input, 0, sizeof(*input));
|
|
788
691
|
}
|
|
789
692
|
|
|
@@ -798,8 +701,8 @@ static VALUE binary_string_create(VALUE arg) {
|
|
|
798
701
|
}
|
|
799
702
|
|
|
800
703
|
static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
|
|
801
|
-
if (!
|
|
802
|
-
|
|
704
|
+
if (!se_size_fits_long(bytes)) {
|
|
705
|
+
se_free(ptr);
|
|
803
706
|
rb_raise(rb_eArgError, "embedding output is too large");
|
|
804
707
|
}
|
|
805
708
|
|
|
@@ -809,7 +712,7 @@ static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
|
|
|
809
712
|
|
|
810
713
|
int state = 0;
|
|
811
714
|
VALUE result = rb_protect(binary_string_create, (VALUE)(uintptr_t)&job, &state);
|
|
812
|
-
|
|
715
|
+
se_free(ptr);
|
|
813
716
|
if (state)
|
|
814
717
|
rb_jump_tag(state);
|
|
815
718
|
return result;
|
|
@@ -817,23 +720,17 @@ static VALUE binary_string_from_malloc(void *ptr, size_t bytes) {
|
|
|
817
720
|
|
|
818
721
|
static VALUE binary_string_from_floats(float *ptr, size_t count, se_vector_format_t format) {
|
|
819
722
|
size_t bytes;
|
|
820
|
-
if (!
|
|
821
|
-
!
|
|
822
|
-
|
|
723
|
+
if (!se_checked_mul_size(count, se_vector_format_element_bytes(format), &bytes) ||
|
|
724
|
+
!se_size_fits_long(bytes)) {
|
|
725
|
+
se_free(ptr);
|
|
823
726
|
rb_raise(rb_eArgError, "embedding output is too large");
|
|
824
727
|
}
|
|
825
728
|
|
|
826
729
|
if (format == SE_VECTOR_FORMAT_F32)
|
|
827
730
|
return binary_string_from_malloc(ptr, bytes);
|
|
828
731
|
|
|
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);
|
|
732
|
+
se_encode_f16_from_floats((uint8_t *)ptr, ptr, count);
|
|
733
|
+
return binary_string_from_malloc(ptr, bytes);
|
|
837
734
|
}
|
|
838
735
|
|
|
839
736
|
typedef struct {
|
|
@@ -844,17 +741,47 @@ typedef struct {
|
|
|
844
741
|
size_t *copy_lens;
|
|
845
742
|
} embed_run_t;
|
|
846
743
|
|
|
744
|
+
typedef enum {
|
|
745
|
+
EMBED_RUN_ALLOC_OK = 0,
|
|
746
|
+
EMBED_RUN_ALLOC_OVERFLOW,
|
|
747
|
+
EMBED_RUN_ALLOC_OOM
|
|
748
|
+
} embed_run_alloc_status_t;
|
|
749
|
+
|
|
847
750
|
static void embed_run_free(embed_run_t *run) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
751
|
+
se_free(run->out);
|
|
752
|
+
se_free(run->stats);
|
|
753
|
+
se_free(run->targets);
|
|
754
|
+
se_free(run->pending);
|
|
755
|
+
se_free(run->copy_lens);
|
|
853
756
|
memset(run, 0, sizeof(*run));
|
|
854
757
|
}
|
|
855
758
|
|
|
759
|
+
static embed_run_alloc_status_t embed_run_alloc(embed_run_t *run, size_t count, size_t floats) {
|
|
760
|
+
size_t out_bytes = 0;
|
|
761
|
+
size_t stats_bytes = 0;
|
|
762
|
+
size_t targets_bytes = 0;
|
|
763
|
+
|
|
764
|
+
memset(run, 0, sizeof(*run));
|
|
765
|
+
if (!se_alloc_bytes(floats, sizeof(float), &out_bytes) ||
|
|
766
|
+
!se_alloc_bytes(count, sizeof(se_token_stats_t), &stats_bytes) ||
|
|
767
|
+
!se_alloc_bytes(count, sizeof(size_t), &targets_bytes))
|
|
768
|
+
return EMBED_RUN_ALLOC_OVERFLOW;
|
|
769
|
+
|
|
770
|
+
run->out = (float *)se_calloc(SE_ALLOC_BATCH_OUTPUT, 1, out_bytes);
|
|
771
|
+
run->stats = (se_token_stats_t *)se_calloc(SE_ALLOC_BATCH_STATS, 1, stats_bytes);
|
|
772
|
+
run->targets = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
|
|
773
|
+
run->pending = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
|
|
774
|
+
run->copy_lens = (size_t *)se_calloc(SE_ALLOC_BATCH_INDEX, 1, targets_bytes);
|
|
775
|
+
if (run->out && run->stats && run->targets && run->pending && run->copy_lens)
|
|
776
|
+
return EMBED_RUN_ALLOC_OK;
|
|
777
|
+
|
|
778
|
+
embed_run_free(run);
|
|
779
|
+
return EMBED_RUN_ALLOC_OOM;
|
|
780
|
+
}
|
|
781
|
+
|
|
856
782
|
static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t count,
|
|
857
783
|
VALUE max_tokens_opt, se_vector_format_t format,
|
|
784
|
+
se_encoding_validation_t validation,
|
|
858
785
|
se_token_stats_t *stats_out) {
|
|
859
786
|
model_wrapper_t *w = get_model(self);
|
|
860
787
|
const se_model_t *model = &w->model;
|
|
@@ -862,29 +789,23 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
|
|
|
862
789
|
const uint32_t max_tokens = resolve_max_tokens(model, max_tokens_opt);
|
|
863
790
|
|
|
864
791
|
text_source_t source;
|
|
865
|
-
source.snapshot = snapshot_texts(texts, is_array, count);
|
|
792
|
+
source.snapshot = snapshot_texts(texts, is_array, count, validation);
|
|
866
793
|
source.is_array = is_array;
|
|
867
794
|
const text_source_t *src = &source;
|
|
868
795
|
|
|
869
796
|
size_t floats;
|
|
870
797
|
size_t out_bytes;
|
|
871
|
-
if (!
|
|
872
|
-
!
|
|
873
|
-
!
|
|
798
|
+
if (!se_checked_mul_size(count, dim, &floats) ||
|
|
799
|
+
!se_checked_mul_size(floats, se_vector_format_element_bytes(format), &out_bytes) ||
|
|
800
|
+
!se_size_fits_long(out_bytes))
|
|
874
801
|
rb_raise(rb_eArgError, "embedding output is too large");
|
|
875
802
|
|
|
876
803
|
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);
|
|
804
|
+
embed_run_alloc_status_t alloc_status = embed_run_alloc(&run, count, floats);
|
|
805
|
+
if (alloc_status == EMBED_RUN_ALLOC_OVERFLOW)
|
|
806
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
807
|
+
if (alloc_status == EMBED_RUN_ALLOC_OOM)
|
|
886
808
|
rb_raise(rb_eNoMemError, "out of memory");
|
|
887
|
-
}
|
|
888
809
|
|
|
889
810
|
size_t initial = prefix_initial_target(max_tokens);
|
|
890
811
|
size_t npending = count;
|
|
@@ -964,13 +885,13 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
|
|
|
964
885
|
if (stats_out && count)
|
|
965
886
|
*stats_out = run.stats[0];
|
|
966
887
|
|
|
967
|
-
|
|
888
|
+
se_free(run.stats);
|
|
968
889
|
run.stats = NULL;
|
|
969
|
-
|
|
890
|
+
se_free(run.targets);
|
|
970
891
|
run.targets = NULL;
|
|
971
|
-
|
|
892
|
+
se_free(run.pending);
|
|
972
893
|
run.pending = NULL;
|
|
973
|
-
|
|
894
|
+
se_free(run.copy_lens);
|
|
974
895
|
run.copy_lens = NULL;
|
|
975
896
|
|
|
976
897
|
float *out = run.out;
|
|
@@ -982,39 +903,44 @@ static VALUE embed_texts_internal(VALUE self, VALUE texts, int is_array, size_t
|
|
|
982
903
|
}
|
|
983
904
|
|
|
984
905
|
static VALUE embed_batch_internal(VALUE self, VALUE texts, VALUE max_tokens_opt,
|
|
985
|
-
se_vector_format_t format) {
|
|
906
|
+
se_vector_format_t format, se_encoding_validation_t validation) {
|
|
986
907
|
Check_Type(texts, T_ARRAY);
|
|
987
908
|
return embed_texts_internal(self, texts, 1, (size_t)RARRAY_LEN(texts), max_tokens_opt, format,
|
|
988
|
-
NULL);
|
|
909
|
+
validation, NULL);
|
|
989
910
|
}
|
|
990
911
|
|
|
991
912
|
static VALUE model_embed_batch(int argc, VALUE *argv, VALUE self) {
|
|
992
913
|
VALUE texts, opts;
|
|
993
914
|
rb_scan_args(argc, argv, "1:", &texts, &opts);
|
|
915
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
994
916
|
reject_parallel_threads(opts);
|
|
995
917
|
|
|
996
918
|
VALUE max_tokens = lookup_option(opts, id_max_tokens);
|
|
997
919
|
se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
|
|
998
|
-
|
|
920
|
+
se_encoding_validation_t validation =
|
|
921
|
+
resolve_encoding_validation(lookup_option(opts, id_validate_encoding));
|
|
922
|
+
return embed_batch_internal(self, texts, max_tokens, format, validation);
|
|
999
923
|
}
|
|
1000
924
|
|
|
1001
925
|
static VALUE embed_one_via_batch(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
1002
|
-
se_vector_format_t format,
|
|
1003
|
-
|
|
926
|
+
se_vector_format_t format, se_encoding_validation_t validation,
|
|
927
|
+
se_token_stats_t *stats) {
|
|
928
|
+
return embed_texts_internal(self, text, 0, 1, max_tokens_opt, format, validation, stats);
|
|
1004
929
|
}
|
|
1005
930
|
|
|
1006
931
|
static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
1007
|
-
se_vector_format_t format,
|
|
932
|
+
se_vector_format_t format, se_encoding_validation_t validation,
|
|
933
|
+
se_token_stats_t *stats) {
|
|
1008
934
|
model_wrapper_t *w = get_model(self);
|
|
1009
935
|
Check_Type(text, T_STRING);
|
|
1010
|
-
|
|
936
|
+
check_text_encoding_mode(text, -1, validation);
|
|
1011
937
|
|
|
1012
938
|
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);
|
|
939
|
+
return embed_one_via_batch(self, text, max_tokens_opt, format, validation, stats);
|
|
1014
940
|
|
|
1015
941
|
const uint32_t dim = w->model.meta.dim;
|
|
1016
942
|
size_t out_bytes;
|
|
1017
|
-
if (!
|
|
943
|
+
if (!se_checked_mul_size(dim, sizeof(float), &out_bytes) || !se_size_fits_long(out_bytes))
|
|
1018
944
|
rb_raise(rb_eArgError, "embedding output is too large");
|
|
1019
945
|
|
|
1020
946
|
VALUE result = rb_str_new(NULL, (long)out_bytes);
|
|
@@ -1047,19 +973,25 @@ static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
|
1047
973
|
static VALUE model_embed(int argc, VALUE *argv, VALUE self) {
|
|
1048
974
|
VALUE text, opts;
|
|
1049
975
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
976
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
1050
977
|
reject_parallel_threads(opts);
|
|
1051
978
|
return embed_one_value(self, text, lookup_option(opts, id_max_tokens),
|
|
1052
|
-
resolve_vector_format(lookup_option(opts, id_format)),
|
|
979
|
+
resolve_vector_format(lookup_option(opts, id_format)),
|
|
980
|
+
resolve_encoding_validation(lookup_option(opts, id_validate_encoding)),
|
|
981
|
+
NULL);
|
|
1053
982
|
}
|
|
1054
983
|
|
|
1055
984
|
static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
1056
985
|
VALUE text, opts;
|
|
1057
986
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
987
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
1058
988
|
reject_parallel_threads(opts);
|
|
1059
989
|
|
|
1060
990
|
se_token_stats_t stats;
|
|
1061
|
-
VALUE vector = embed_one_value(
|
|
1062
|
-
|
|
991
|
+
VALUE vector = embed_one_value(
|
|
992
|
+
self, text, lookup_option(opts, id_max_tokens),
|
|
993
|
+
resolve_vector_format(lookup_option(opts, id_format)),
|
|
994
|
+
resolve_encoding_validation(lookup_option(opts, id_validate_encoding)), &stats);
|
|
1063
995
|
|
|
1064
996
|
VALUE hash = rb_hash_new();
|
|
1065
997
|
rb_hash_aset(hash, ID2SYM(id_vector), vector);
|
|
@@ -1072,10 +1004,12 @@ static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
|
1072
1004
|
static VALUE model_tokenize(int argc, VALUE *argv, VALUE self) {
|
|
1073
1005
|
VALUE text, opts;
|
|
1074
1006
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1007
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_validate_encoding);
|
|
1075
1008
|
|
|
1076
1009
|
model_wrapper_t *w = get_model(self);
|
|
1077
1010
|
Check_Type(text, T_STRING);
|
|
1078
|
-
|
|
1011
|
+
check_text_encoding_mode(
|
|
1012
|
+
text, -1, resolve_encoding_validation(lookup_option(opts, id_validate_encoding)));
|
|
1079
1013
|
|
|
1080
1014
|
uint32_t max_tokens = resolve_max_tokens(&w->model, lookup_option(opts, id_max_tokens));
|
|
1081
1015
|
|
|
@@ -1183,7 +1117,11 @@ static VALUE embed_token_ids_body(VALUE arg) {
|
|
|
1183
1117
|
run->ids[i] = (uint32_t)conv.value;
|
|
1184
1118
|
}
|
|
1185
1119
|
|
|
1186
|
-
|
|
1120
|
+
size_t out_bytes;
|
|
1121
|
+
if (!se_alloc_bytes(run->model->meta.dim, sizeof(float), &out_bytes))
|
|
1122
|
+
rb_raise(rb_eArgError, "embedding output is too large");
|
|
1123
|
+
|
|
1124
|
+
run->out = (float *)se_calloc(SE_ALLOC_BATCH_OUTPUT, 1, out_bytes);
|
|
1187
1125
|
if (!run->out)
|
|
1188
1126
|
rb_raise(rb_eNoMemError, "out of memory");
|
|
1189
1127
|
|
|
@@ -1214,7 +1152,7 @@ static VALUE embed_token_ids_body(VALUE arg) {
|
|
|
1214
1152
|
if (run->stats_out)
|
|
1215
1153
|
*run->stats_out = run->stats;
|
|
1216
1154
|
|
|
1217
|
-
|
|
1155
|
+
se_free(run->ids);
|
|
1218
1156
|
run->ids = NULL;
|
|
1219
1157
|
|
|
1220
1158
|
float *out = run->out;
|
|
@@ -1224,8 +1162,8 @@ static VALUE embed_token_ids_body(VALUE arg) {
|
|
|
1224
1162
|
|
|
1225
1163
|
static VALUE embed_token_ids_ensure(VALUE arg) {
|
|
1226
1164
|
ids_run_t *run = (ids_run_t *)(uintptr_t)arg;
|
|
1227
|
-
|
|
1228
|
-
|
|
1165
|
+
se_free(run->ids);
|
|
1166
|
+
se_free(run->out);
|
|
1229
1167
|
run->ids = NULL;
|
|
1230
1168
|
run->out = NULL;
|
|
1231
1169
|
return Qnil;
|
|
@@ -1247,12 +1185,13 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
|
|
|
1247
1185
|
}
|
|
1248
1186
|
|
|
1249
1187
|
size_t ids_bytes;
|
|
1250
|
-
if (!
|
|
1188
|
+
if (!se_alloc_bytes(n, sizeof(uint32_t), &ids_bytes))
|
|
1251
1189
|
rb_raise(rb_eArgError, "token id array is too large");
|
|
1252
1190
|
|
|
1253
1191
|
size_t out_bytes;
|
|
1254
|
-
if (!
|
|
1255
|
-
|
|
1192
|
+
if (!se_checked_mul_size(w->model.meta.dim, se_vector_format_element_bytes(format),
|
|
1193
|
+
&out_bytes) ||
|
|
1194
|
+
!se_size_fits_long(out_bytes))
|
|
1256
1195
|
rb_raise(rb_eArgError, "embedding output is too large");
|
|
1257
1196
|
|
|
1258
1197
|
ids_run_t run;
|
|
@@ -1265,7 +1204,7 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
|
|
|
1265
1204
|
run.format = format;
|
|
1266
1205
|
run.stats_out = stats_out;
|
|
1267
1206
|
run.truncated = truncated;
|
|
1268
|
-
run.ids = (uint32_t *)
|
|
1207
|
+
run.ids = (uint32_t *)se_malloc(SE_ALLOC_TOKEN_IDS, ids_bytes);
|
|
1269
1208
|
if (!run.ids)
|
|
1270
1209
|
rb_raise(rb_eNoMemError, "out of memory");
|
|
1271
1210
|
|
|
@@ -1278,6 +1217,7 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
|
|
|
1278
1217
|
static VALUE model_embed_token_ids(int argc, VALUE *argv, VALUE self) {
|
|
1279
1218
|
VALUE ids_value, opts;
|
|
1280
1219
|
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1220
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_threads);
|
|
1281
1221
|
reject_parallel_threads(opts);
|
|
1282
1222
|
return embed_token_ids_value(self, ids_value, lookup_option(opts, id_max_tokens),
|
|
1283
1223
|
resolve_vector_format(lookup_option(opts, id_format)), NULL);
|
|
@@ -1286,6 +1226,7 @@ static VALUE model_embed_token_ids(int argc, VALUE *argv, VALUE self) {
|
|
|
1286
1226
|
static VALUE model_embed_token_ids_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
1287
1227
|
VALUE ids_value, opts;
|
|
1288
1228
|
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1229
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_threads);
|
|
1289
1230
|
reject_parallel_threads(opts);
|
|
1290
1231
|
|
|
1291
1232
|
se_token_stats_t stats;
|
|
@@ -1302,23 +1243,9 @@ static VALUE model_embed_token_ids_with_stats(int argc, VALUE *argv, VALUE self)
|
|
|
1302
1243
|
return hash;
|
|
1303
1244
|
}
|
|
1304
1245
|
|
|
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
1246
|
typedef struct {
|
|
1320
1247
|
VALUE matrix;
|
|
1321
|
-
|
|
1248
|
+
se_topk_job_t job;
|
|
1322
1249
|
float *q_copy;
|
|
1323
1250
|
float *matrix_copy;
|
|
1324
1251
|
size_t matrix_bytes;
|
|
@@ -1330,460 +1257,11 @@ static int ptr_is_float_aligned(const void *ptr) {
|
|
|
1330
1257
|
}
|
|
1331
1258
|
|
|
1332
1259
|
static void topk_unblock_cancel(void *arg) {
|
|
1333
|
-
|
|
1260
|
+
se_topk_job_t *job = (se_topk_job_t *)arg;
|
|
1334
1261
|
if (job)
|
|
1335
1262
|
job->cancelled = 1;
|
|
1336
1263
|
}
|
|
1337
1264
|
|
|
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
1265
|
static VALUE topk_body(VALUE arg) {
|
|
1788
1266
|
topk_run_t *run = (topk_run_t *)(uintptr_t)arg;
|
|
1789
1267
|
const char *matrix_ptr = RSTRING_PTR(run->matrix);
|
|
@@ -1796,17 +1274,17 @@ static VALUE topk_body(VALUE arg) {
|
|
|
1796
1274
|
}
|
|
1797
1275
|
|
|
1798
1276
|
if (run->job.cosine) {
|
|
1799
|
-
float query_sq =
|
|
1277
|
+
float query_sq = se_dot_product_f32(run->job.q, run->job.q, run->job.dim);
|
|
1800
1278
|
if (!(query_sq > 0.0f))
|
|
1801
1279
|
rb_raise(rb_eArgError, "cosine_top_k needs a query with a non-zero norm");
|
|
1802
1280
|
run->job.inv_query_norm = 1.0f / sqrtf(query_sq);
|
|
1803
1281
|
}
|
|
1804
1282
|
|
|
1805
1283
|
if (run->release_gvl) {
|
|
1806
|
-
rb_thread_call_without_gvl(
|
|
1284
|
+
rb_thread_call_without_gvl(se_topk_execute, &run->job, topk_unblock_cancel, &run->job);
|
|
1807
1285
|
rb_thread_check_ints();
|
|
1808
1286
|
} else {
|
|
1809
|
-
|
|
1287
|
+
se_topk_execute(&run->job);
|
|
1810
1288
|
rb_thread_check_ints();
|
|
1811
1289
|
}
|
|
1812
1290
|
|
|
@@ -1827,10 +1305,10 @@ static VALUE topk_body(VALUE arg) {
|
|
|
1827
1305
|
|
|
1828
1306
|
static VALUE topk_ensure(VALUE arg) {
|
|
1829
1307
|
topk_run_t *run = (topk_run_t *)(uintptr_t)arg;
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1308
|
+
se_free(run->q_copy);
|
|
1309
|
+
se_free(run->matrix_copy);
|
|
1310
|
+
se_free(run->job.best_idx);
|
|
1311
|
+
se_free(run->job.best_score);
|
|
1834
1312
|
run->q_copy = NULL;
|
|
1835
1313
|
run->matrix_copy = NULL;
|
|
1836
1314
|
run->job.best_idx = NULL;
|
|
@@ -1888,17 +1366,18 @@ static void topk_check_matrix(VALUE matrix, size_t matrix_bytes, se_vector_forma
|
|
|
1888
1366
|
static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
|
|
1889
1367
|
VALUE query, matrix, k_val, opts;
|
|
1890
1368
|
rb_scan_args(argc, argv, "3:", &query, &matrix, &k_val, &opts);
|
|
1369
|
+
SE_CHECK_KEYWORDS(opts, id_dim, id_format, id_allow_unfrozen);
|
|
1891
1370
|
(void)self;
|
|
1892
1371
|
|
|
1893
1372
|
Check_Type(query, T_STRING);
|
|
1894
1373
|
Check_Type(matrix, T_STRING);
|
|
1895
1374
|
|
|
1896
1375
|
se_vector_format_t format = resolve_vector_format(lookup_option(opts, id_format));
|
|
1897
|
-
size_t element_bytes =
|
|
1376
|
+
size_t element_bytes = se_vector_format_element_bytes(format);
|
|
1898
1377
|
size_t dim = topk_required_dim(opts);
|
|
1899
1378
|
|
|
1900
1379
|
size_t row_bytes;
|
|
1901
|
-
if (!
|
|
1380
|
+
if (!se_checked_mul_size(dim, element_bytes, &row_bytes) || !se_size_fits_long(row_bytes))
|
|
1902
1381
|
rb_raise(rb_eArgError, "dim: is too large");
|
|
1903
1382
|
|
|
1904
1383
|
if ((size_t)RSTRING_LEN(query) != row_bytes)
|
|
@@ -1938,14 +1417,22 @@ static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
|
|
|
1938
1417
|
run.job.inv_query_norm = 1.0f;
|
|
1939
1418
|
|
|
1940
1419
|
size_t q_float_bytes;
|
|
1941
|
-
if (!
|
|
1420
|
+
if (!se_checked_mul_size(dim, sizeof(float), &q_float_bytes))
|
|
1942
1421
|
rb_raise(rb_eArgError, "dim: is too large");
|
|
1943
1422
|
|
|
1944
|
-
run.q_copy = (float *)
|
|
1945
|
-
|
|
1946
|
-
|
|
1423
|
+
run.q_copy = (float *)se_malloc(SE_ALLOC_TOPK_QUERY, q_float_bytes);
|
|
1424
|
+
size_t best_idx_bytes = 0;
|
|
1425
|
+
size_t best_score_bytes = 0;
|
|
1426
|
+
if (!se_array_bytes((size_t)k, sizeof(size_t), &best_idx_bytes) ||
|
|
1427
|
+
!se_array_bytes((size_t)k, sizeof(float), &best_score_bytes)) {
|
|
1428
|
+
topk_ensure((VALUE)(uintptr_t)&run);
|
|
1429
|
+
rb_raise(rb_eArgError, "k is too large");
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
run.job.best_idx = (size_t *)se_calloc(SE_ALLOC_TOPK_BEST, 1, best_idx_bytes);
|
|
1433
|
+
run.job.best_score = (float *)se_malloc(SE_ALLOC_TOPK_BEST, best_score_bytes);
|
|
1947
1434
|
if (needs_copy)
|
|
1948
|
-
run.matrix_copy = (float *)
|
|
1435
|
+
run.matrix_copy = (float *)se_malloc(SE_ALLOC_TOPK_MATRIX_COPY, matrix_bytes);
|
|
1949
1436
|
if (!run.q_copy || !run.job.best_idx || !run.job.best_score ||
|
|
1950
1437
|
(needs_copy && !run.matrix_copy)) {
|
|
1951
1438
|
topk_ensure((VALUE)(uintptr_t)&run);
|
|
@@ -1953,7 +1440,7 @@ static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
|
|
|
1953
1440
|
}
|
|
1954
1441
|
|
|
1955
1442
|
if (format == SE_VECTOR_FORMAT_F16)
|
|
1956
|
-
|
|
1443
|
+
se_decode_f16_to_floats(run.q_copy, (const uint8_t *)RSTRING_PTR(query), dim);
|
|
1957
1444
|
else
|
|
1958
1445
|
memcpy(run.q_copy, RSTRING_PTR(query), row_bytes);
|
|
1959
1446
|
run.job.q = run.q_copy;
|
|
@@ -1982,14 +1469,14 @@ static VALUE se_encode_f16(VALUE self, VALUE ary) {
|
|
|
1982
1469
|
|
|
1983
1470
|
long n = RARRAY_LEN(ary);
|
|
1984
1471
|
size_t bytes;
|
|
1985
|
-
if (!
|
|
1472
|
+
if (!se_checked_mul_size((size_t)n, 2, &bytes) || !se_size_fits_long(bytes))
|
|
1986
1473
|
rb_raise(rb_eArgError, "vector is too large");
|
|
1987
1474
|
|
|
1988
1475
|
VALUE out = rb_str_new(NULL, (long)bytes);
|
|
1989
1476
|
rb_enc_associate(out, binary_encoding);
|
|
1990
1477
|
for (long i = 0; i < n; i++) {
|
|
1991
1478
|
double v = NUM2DBL(rb_ary_entry(ary, i));
|
|
1992
|
-
|
|
1479
|
+
se_write_f16le((uint8_t *)RSTRING_PTR(out) + (size_t)i * 2, (float)v);
|
|
1993
1480
|
}
|
|
1994
1481
|
return out;
|
|
1995
1482
|
}
|
|
@@ -2005,14 +1492,52 @@ static VALUE se_decode_f16(VALUE self, VALUE blob) {
|
|
|
2005
1492
|
VALUE out = rb_ary_new_capa(n / 2);
|
|
2006
1493
|
for (long i = 0; i < n / 2; i++) {
|
|
2007
1494
|
const uint8_t *src = (const uint8_t *)RSTRING_PTR(blob) + (size_t)i * 2;
|
|
2008
|
-
rb_ary_push(out, DBL2NUM((double)
|
|
1495
|
+
rb_ary_push(out, DBL2NUM((double)se_read_f16le(src)));
|
|
2009
1496
|
}
|
|
2010
1497
|
RB_GC_GUARD(blob);
|
|
2011
1498
|
return out;
|
|
2012
1499
|
}
|
|
2013
1500
|
|
|
1501
|
+
#if SE_ENABLE_ALLOC_STATS
|
|
1502
|
+
static VALUE se_alloc_stats_hash(VALUE self) {
|
|
1503
|
+
(void)self;
|
|
1504
|
+
se_alloc_stats_t stats[SE_ALLOC_CATEGORY_COUNT];
|
|
1505
|
+
se_alloc_stats_snapshot(stats);
|
|
1506
|
+
|
|
1507
|
+
VALUE out = rb_hash_new();
|
|
1508
|
+
ID id_current_bytes = rb_intern("current_bytes");
|
|
1509
|
+
ID id_peak_bytes = rb_intern("peak_bytes");
|
|
1510
|
+
ID id_total_allocated_bytes = rb_intern("total_allocated_bytes");
|
|
1511
|
+
ID id_total_freed_bytes = rb_intern("total_freed_bytes");
|
|
1512
|
+
ID id_alloc_count = rb_intern("alloc_count");
|
|
1513
|
+
ID id_realloc_count = rb_intern("realloc_count");
|
|
1514
|
+
ID id_free_count = rb_intern("free_count");
|
|
1515
|
+
|
|
1516
|
+
for (int i = 0; i < SE_ALLOC_CATEGORY_COUNT; i++) {
|
|
1517
|
+
VALUE item = rb_hash_new();
|
|
1518
|
+
rb_hash_aset(item, ID2SYM(id_current_bytes), SIZET2NUM(stats[i].current_bytes));
|
|
1519
|
+
rb_hash_aset(item, ID2SYM(id_peak_bytes), SIZET2NUM(stats[i].peak_bytes));
|
|
1520
|
+
rb_hash_aset(item, ID2SYM(id_total_allocated_bytes),
|
|
1521
|
+
SIZET2NUM(stats[i].total_allocated_bytes));
|
|
1522
|
+
rb_hash_aset(item, ID2SYM(id_total_freed_bytes), SIZET2NUM(stats[i].total_freed_bytes));
|
|
1523
|
+
rb_hash_aset(item, ID2SYM(id_alloc_count), SIZET2NUM(stats[i].alloc_count));
|
|
1524
|
+
rb_hash_aset(item, ID2SYM(id_realloc_count), SIZET2NUM(stats[i].realloc_count));
|
|
1525
|
+
rb_hash_aset(item, ID2SYM(id_free_count), SIZET2NUM(stats[i].free_count));
|
|
1526
|
+
rb_hash_aset(out, ID2SYM(rb_intern(se_alloc_category_name((se_alloc_category_t)i))), item);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
return out;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
static VALUE se_alloc_stats_reset_bang(VALUE self) {
|
|
1533
|
+
(void)self;
|
|
1534
|
+
se_alloc_stats_reset();
|
|
1535
|
+
return Qnil;
|
|
1536
|
+
}
|
|
1537
|
+
#endif
|
|
1538
|
+
|
|
2014
1539
|
RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
|
|
2015
|
-
|
|
1540
|
+
se_select_f16_backend();
|
|
2016
1541
|
binary_encoding = rb_ascii8bit_encoding();
|
|
2017
1542
|
utf8_encoding = rb_utf8_encoding();
|
|
2018
1543
|
id_join = rb_intern("join");
|
|
@@ -2028,6 +1553,9 @@ RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
|
|
|
2028
1553
|
id_dim = rb_intern("dim");
|
|
2029
1554
|
id_allow_unfrozen = rb_intern("allow_unfrozen");
|
|
2030
1555
|
|
|
1556
|
+
id_validate_encoding = rb_intern("validate_encoding");
|
|
1557
|
+
id_full = rb_intern("full");
|
|
1558
|
+
id_prefix = rb_intern("prefix");
|
|
2031
1559
|
mStaticEmbeddings = rb_define_module("StaticEmbeddings");
|
|
2032
1560
|
cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
|
|
2033
1561
|
|
|
@@ -2063,6 +1591,11 @@ RUBY_FUNC_EXPORTED void Init_static_embeddings(void) {
|
|
|
2063
1591
|
rb_define_singleton_method(mStaticEmbeddings, "encode_f16", se_encode_f16, 1);
|
|
2064
1592
|
rb_define_singleton_method(mStaticEmbeddings, "decode_f16", se_decode_f16, 1);
|
|
2065
1593
|
rb_define_singleton_method(mStaticEmbeddings, "simd_backend", se_simd_backend, 0);
|
|
1594
|
+
#if SE_ENABLE_ALLOC_STATS
|
|
1595
|
+
rb_define_singleton_method(mStaticEmbeddings, "__alloc_stats__", se_alloc_stats_hash, 0);
|
|
1596
|
+
rb_define_singleton_method(mStaticEmbeddings, "__alloc_stats_reset__",
|
|
1597
|
+
se_alloc_stats_reset_bang, 0);
|
|
1598
|
+
#endif
|
|
2066
1599
|
|
|
2067
1600
|
rb_define_const(mStaticEmbeddings, "FORMAT_VERSION", UINT2NUM(SE_FORMAT_VERSION));
|
|
2068
1601
|
rb_define_const(mStaticEmbeddings, "TOKENIZER_BERT_WORDPIECE_V1",
|