static_embeddings 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +121 -0
- data/README.md +167 -276
- data/docs/ARCHITECTURE.md +56 -6
- data/docs/MODEL_AUDIT.md +21 -8
- data/docs/PERFORMANCE.md +104 -36
- data/ext/static_embeddings/extconf.rb +4 -0
- data/ext/static_embeddings/se_alloc_stats.c +244 -0
- data/ext/static_embeddings/se_f16.c +378 -0
- data/ext/static_embeddings/se_format.c +235 -148
- data/ext/static_embeddings/se_internal.h +159 -0
- data/ext/static_embeddings/se_tokenizer.c +144 -41
- data/ext/static_embeddings/se_topk.c +236 -0
- data/ext/static_embeddings/static_embeddings.c +225 -744
- data/lib/static_embeddings/format.rb +22 -11
- data/lib/static_embeddings/version.rb +1 -1
- data/tools/benchmark.rb +13 -4
- metadata +4 -1
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
#ifndef O_CLOEXEC
|
|
15
15
|
#define O_CLOEXEC 0
|
|
16
16
|
#endif
|
|
17
|
+
#else
|
|
18
|
+
#include <windows.h>
|
|
17
19
|
#endif
|
|
18
20
|
|
|
19
21
|
void se_error_clear(se_error_t *err) {
|
|
@@ -278,6 +280,68 @@ static se_status_t parse_trie(se_trie_t *trie, const uint8_t *base, struct secti
|
|
|
278
280
|
return SE_OK;
|
|
279
281
|
}
|
|
280
282
|
|
|
283
|
+
static int valid_unicode_codepoint(uint32_t cp) {
|
|
284
|
+
return cp <= 0x10FFFFu && !(cp >= 0xD800u && cp <= 0xDFFFu);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static se_status_t validate_norm_map(const se_map_entry_t *entries, uint32_t count,
|
|
288
|
+
const char *name, se_error_t *err) {
|
|
289
|
+
uint32_t prev = 0;
|
|
290
|
+
|
|
291
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
292
|
+
const se_map_entry_t *entry = &entries[i];
|
|
293
|
+
if (entry->len > SE_ARRAY_LEN(entry->out)) {
|
|
294
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s map entry %u has len %u", name, i,
|
|
295
|
+
entry->len);
|
|
296
|
+
return SE_ERR_INVALID_FORMAT;
|
|
297
|
+
}
|
|
298
|
+
if (!valid_unicode_codepoint(entry->cp)) {
|
|
299
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s map entry %u has invalid codepoint", name,
|
|
300
|
+
i);
|
|
301
|
+
return SE_ERR_INVALID_FORMAT;
|
|
302
|
+
}
|
|
303
|
+
for (uint32_t k = 0; k < entry->len; k++) {
|
|
304
|
+
if (!valid_unicode_codepoint(entry->out[k])) {
|
|
305
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT,
|
|
306
|
+
"%s map entry %u output %u is not a valid codepoint", name, i, k);
|
|
307
|
+
return SE_ERR_INVALID_FORMAT;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (i && entry->cp <= prev) {
|
|
311
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s map is not strictly sorted", name);
|
|
312
|
+
return SE_ERR_INVALID_FORMAT;
|
|
313
|
+
}
|
|
314
|
+
prev = entry->cp;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return SE_OK;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
static se_status_t validate_norm_ranges(const se_range_t *ranges, uint32_t count, const char *name,
|
|
321
|
+
se_error_t *err) {
|
|
322
|
+
uint32_t prev_hi = 0;
|
|
323
|
+
|
|
324
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
325
|
+
const se_range_t *range = &ranges[i];
|
|
326
|
+
if (!valid_unicode_codepoint(range->hi)) {
|
|
327
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s range %u exceeds the Unicode range", name,
|
|
328
|
+
i);
|
|
329
|
+
return SE_ERR_INVALID_FORMAT;
|
|
330
|
+
}
|
|
331
|
+
if (range->lo > range->hi) {
|
|
332
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s range %u is inverted", name, i);
|
|
333
|
+
return SE_ERR_INVALID_FORMAT;
|
|
334
|
+
}
|
|
335
|
+
if (i && range->lo <= prev_hi) {
|
|
336
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "%s ranges overlap or are unsorted", name);
|
|
337
|
+
return SE_ERR_INVALID_FORMAT;
|
|
338
|
+
}
|
|
339
|
+
prev_hi = range->hi;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return SE_OK;
|
|
343
|
+
}
|
|
344
|
+
|
|
281
345
|
static se_status_t parse_norm_tables(se_model_t *model, const uint8_t *base, struct section sec,
|
|
282
346
|
se_error_t *err) {
|
|
283
347
|
memset(&model->norm, 0, sizeof(model->norm));
|
|
@@ -338,66 +402,112 @@ static se_status_t parse_norm_tables(se_model_t *model, const uint8_t *base, str
|
|
|
338
402
|
model->norm.whitespace = (const se_range_t *)cursor;
|
|
339
403
|
model->norm.whitespace_count = n_ws;
|
|
340
404
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
405
|
+
se_status_t rc = validate_norm_map(model->norm.lower, model->norm.lower_count, "lower", err);
|
|
406
|
+
if (rc != SE_OK)
|
|
407
|
+
return rc;
|
|
408
|
+
rc = validate_norm_map(model->norm.nfd, model->norm.nfd_count, "nfd", err);
|
|
409
|
+
if (rc != SE_OK)
|
|
410
|
+
return rc;
|
|
411
|
+
rc = validate_norm_ranges(model->norm.mn, model->norm.mn_count, "mn", err);
|
|
412
|
+
if (rc != SE_OK)
|
|
413
|
+
return rc;
|
|
414
|
+
rc = validate_norm_ranges(model->norm.punct, model->norm.punct_count, "punct", err);
|
|
415
|
+
if (rc != SE_OK)
|
|
416
|
+
return rc;
|
|
417
|
+
rc = validate_norm_ranges(model->norm.control, model->norm.control_count, "control", err);
|
|
418
|
+
if (rc != SE_OK)
|
|
419
|
+
return rc;
|
|
420
|
+
return validate_norm_ranges(model->norm.whitespace, model->norm.whitespace_count, "whitespace",
|
|
421
|
+
err);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
static se_status_t validate_vocab_hash(const se_model_t *model, struct section vocab_strings,
|
|
425
|
+
se_error_t *err) {
|
|
426
|
+
const se_meta_t *m = &model->meta;
|
|
427
|
+
size_t bitset_size = ((size_t)m->vocab_size + 7u) / 8u;
|
|
428
|
+
size_t seen_bytes = 0;
|
|
429
|
+
uint32_t filled = 0;
|
|
430
|
+
se_status_t rc = SE_OK;
|
|
431
|
+
|
|
432
|
+
if (!se_alloc_bytes(bitset_size, 1u, &seen_bytes)) {
|
|
433
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "vocabulary size is too large");
|
|
434
|
+
return SE_ERR_INVALID_FORMAT;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
uint8_t *seen = (uint8_t *)se_calloc(SE_ALLOC_FORMAT_VALIDATE, 1, seen_bytes);
|
|
438
|
+
if (!seen) {
|
|
439
|
+
se_error_set(err, SE_ERR_OOM, "out of memory while validating vocabulary hash");
|
|
440
|
+
return SE_ERR_OOM;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (uint32_t i = 0; i < m->hash_table_size; i++) {
|
|
444
|
+
const se_vocab_slot_t *slot = &model->vocab_hash[i];
|
|
445
|
+
if (slot->token_id == SE_SLOT_EMPTY)
|
|
446
|
+
continue;
|
|
447
|
+
if (slot->token_id >= m->vocab_size) {
|
|
448
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u has token id out of range", i);
|
|
449
|
+
rc = SE_ERR_INVALID_FORMAT;
|
|
450
|
+
goto done;
|
|
373
451
|
}
|
|
452
|
+
if ((uint64_t)slot->str_off + slot->str_len > vocab_strings.size) {
|
|
453
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u points outside the string blob", i);
|
|
454
|
+
rc = SE_ERR_INVALID_FORMAT;
|
|
455
|
+
goto done;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
uint8_t mask = (uint8_t)(1u << (slot->token_id & 7u));
|
|
459
|
+
uint8_t *byte = &seen[slot->token_id >> 3];
|
|
460
|
+
if (*byte & mask) {
|
|
461
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT,
|
|
462
|
+
"token id %u appears more than once in hash table", slot->token_id);
|
|
463
|
+
rc = SE_ERR_INVALID_FORMAT;
|
|
464
|
+
goto done;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
*byte |= mask;
|
|
468
|
+
filled++;
|
|
374
469
|
}
|
|
375
470
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
471
|
+
if (filled != m->vocab_size) {
|
|
472
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "hash table has %u filled slots, expected %u",
|
|
473
|
+
filled, m->vocab_size);
|
|
474
|
+
rc = SE_ERR_INVALID_FORMAT;
|
|
475
|
+
goto done;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
uint32_t unk_lookup = 0;
|
|
479
|
+
if (!se_vocab_lookup(model, (const uint8_t *)"[UNK]", 5, &unk_lookup) ||
|
|
480
|
+
unk_lookup != m->unk_id) {
|
|
481
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT,
|
|
482
|
+
"[UNK] is not reachable through the vocabulary hash");
|
|
483
|
+
rc = SE_ERR_INVALID_FORMAT;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
done:
|
|
487
|
+
se_free(seen);
|
|
488
|
+
return rc;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static se_status_t validate_hash_probe_limits(const se_model_t *model, se_error_t *err) {
|
|
492
|
+
const se_meta_t *m = &model->meta;
|
|
493
|
+
if (m->max_probe == 0)
|
|
494
|
+
return SE_OK;
|
|
495
|
+
|
|
496
|
+
uint32_t mask = m->hash_table_size - 1u;
|
|
497
|
+
for (uint32_t i = 0; i < m->hash_table_size; i++) {
|
|
498
|
+
const se_vocab_slot_t *slot = &model->vocab_hash[i];
|
|
499
|
+
if (slot->token_id == SE_SLOT_EMPTY)
|
|
500
|
+
continue;
|
|
501
|
+
|
|
502
|
+
uint32_t pos = slot->hash & mask;
|
|
503
|
+
uint32_t probes = 1;
|
|
504
|
+
while (pos != i) {
|
|
505
|
+
probes++;
|
|
506
|
+
pos = (pos + 1u) & mask;
|
|
507
|
+
if (probes > m->max_probe) {
|
|
508
|
+
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u exceeds recorded max probe", i);
|
|
398
509
|
return SE_ERR_INVALID_FORMAT;
|
|
399
510
|
}
|
|
400
|
-
prev_hi = range->hi;
|
|
401
511
|
}
|
|
402
512
|
}
|
|
403
513
|
|
|
@@ -408,6 +518,12 @@ static se_status_t validate(se_model_t *model, se_error_t *err) {
|
|
|
408
518
|
const uint8_t *base = (const uint8_t *)model->map_base;
|
|
409
519
|
const uint64_t file_size = (uint64_t)model->map_size;
|
|
410
520
|
|
|
521
|
+
if (!se_host_is_little_endian()) {
|
|
522
|
+
se_error_set(err, SE_ERR_UNSUPPORTED_VERSION,
|
|
523
|
+
"static_embeddings .semb files require a little-endian host");
|
|
524
|
+
return SE_ERR_UNSUPPORTED_VERSION;
|
|
525
|
+
}
|
|
526
|
+
|
|
411
527
|
if (file_size < SE_HEADER_SIZE) {
|
|
412
528
|
se_error_set(err, SE_ERR_INVALID_FORMAT, "file shorter than header");
|
|
413
529
|
return SE_ERR_INVALID_FORMAT;
|
|
@@ -553,8 +669,7 @@ static se_status_t validate(se_model_t *model, se_error_t *err) {
|
|
|
553
669
|
|
|
554
670
|
const struct section sections[] = {vocab_strings, vocab_hash, embeddings, norm,
|
|
555
671
|
provenance, root_trie, cont_trie};
|
|
556
|
-
se_status_t overlap_status =
|
|
557
|
-
validate_no_overlaps(sections, sizeof(sections) / sizeof(sections[0]), err);
|
|
672
|
+
se_status_t overlap_status = validate_no_overlaps(sections, SE_ARRAY_LEN(sections), err);
|
|
558
673
|
if (overlap_status != SE_OK)
|
|
559
674
|
return overlap_status;
|
|
560
675
|
|
|
@@ -585,74 +700,13 @@ static se_status_t validate(se_model_t *model, se_error_t *err) {
|
|
|
585
700
|
model->provenance = provenance.size ? (const char *)(base + provenance.off) : NULL;
|
|
586
701
|
model->provenance_size = (size_t)provenance.size;
|
|
587
702
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
se_error_set(err, SE_ERR_OOM, "out of memory while validating vocabulary hash");
|
|
592
|
-
return SE_ERR_OOM;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
uint32_t filled = 0;
|
|
596
|
-
for (uint32_t i = 0; i < m->hash_table_size; i++) {
|
|
597
|
-
const se_vocab_slot_t *slot = &model->vocab_hash[i];
|
|
598
|
-
if (slot->token_id == SE_SLOT_EMPTY)
|
|
599
|
-
continue;
|
|
600
|
-
if (slot->token_id >= m->vocab_size) {
|
|
601
|
-
free(seen);
|
|
602
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u has token id out of range", i);
|
|
603
|
-
return SE_ERR_INVALID_FORMAT;
|
|
604
|
-
}
|
|
605
|
-
if ((uint64_t)slot->str_off + slot->str_len > vocab_strings.size) {
|
|
606
|
-
free(seen);
|
|
607
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u points outside the string blob", i);
|
|
608
|
-
return SE_ERR_INVALID_FORMAT;
|
|
609
|
-
}
|
|
610
|
-
uint8_t mask = (uint8_t)(1u << (slot->token_id & 7u));
|
|
611
|
-
uint8_t *byte = &seen[slot->token_id >> 3];
|
|
612
|
-
if (*byte & mask) {
|
|
613
|
-
free(seen);
|
|
614
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT,
|
|
615
|
-
"token id %u appears more than once in hash table", slot->token_id);
|
|
616
|
-
return SE_ERR_INVALID_FORMAT;
|
|
617
|
-
}
|
|
618
|
-
*byte |= mask;
|
|
619
|
-
filled++;
|
|
620
|
-
}
|
|
621
|
-
free(seen);
|
|
622
|
-
|
|
623
|
-
if (filled != m->vocab_size) {
|
|
624
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT, "hash table has %u filled slots, expected %u",
|
|
625
|
-
filled, m->vocab_size);
|
|
626
|
-
return SE_ERR_INVALID_FORMAT;
|
|
627
|
-
}
|
|
703
|
+
se_status_t hash_status = validate_vocab_hash(model, vocab_strings, err);
|
|
704
|
+
if (hash_status != SE_OK)
|
|
705
|
+
return hash_status;
|
|
628
706
|
|
|
629
|
-
|
|
630
|
-
if (
|
|
631
|
-
|
|
632
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT,
|
|
633
|
-
"[UNK] is not reachable through the vocabulary hash");
|
|
634
|
-
return SE_ERR_INVALID_FORMAT;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (m->max_probe > 0) {
|
|
638
|
-
uint32_t mask = m->hash_table_size - 1u;
|
|
639
|
-
for (uint32_t i = 0; i < m->hash_table_size; i++) {
|
|
640
|
-
const se_vocab_slot_t *slot = &model->vocab_hash[i];
|
|
641
|
-
if (slot->token_id == SE_SLOT_EMPTY)
|
|
642
|
-
continue;
|
|
643
|
-
uint32_t pos = slot->hash & mask;
|
|
644
|
-
uint32_t probes = 1;
|
|
645
|
-
while (pos != i) {
|
|
646
|
-
probes++;
|
|
647
|
-
pos = (pos + 1u) & mask;
|
|
648
|
-
if (probes > m->max_probe) {
|
|
649
|
-
se_error_set(err, SE_ERR_INVALID_FORMAT, "slot %u exceeds recorded max probe",
|
|
650
|
-
i);
|
|
651
|
-
return SE_ERR_INVALID_FORMAT;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
707
|
+
se_status_t probe_status = validate_hash_probe_limits(model, err);
|
|
708
|
+
if (probe_status != SE_OK)
|
|
709
|
+
return probe_status;
|
|
656
710
|
|
|
657
711
|
se_status_t trie_status =
|
|
658
712
|
parse_trie(&model->root_trie, base, root_trie, m->vocab_size, "root", err);
|
|
@@ -701,35 +755,65 @@ se_status_t se_model_open(se_model_t *model, const char *path, se_error_t *err)
|
|
|
701
755
|
model->map_size = (size_t)st.st_size;
|
|
702
756
|
model->mapped = 1;
|
|
703
757
|
#else
|
|
704
|
-
|
|
705
|
-
|
|
758
|
+
HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
759
|
+
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
760
|
+
if (file == INVALID_HANDLE_VALUE) {
|
|
706
761
|
se_error_set(err, SE_ERR_IO, "cannot open %s", path);
|
|
707
762
|
return SE_ERR_IO;
|
|
708
763
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
se_error_set(err, SE_ERR_IO, "empty file %s", path);
|
|
764
|
+
|
|
765
|
+
LARGE_INTEGER file_size;
|
|
766
|
+
if (!GetFileSizeEx(file, &file_size) || file_size.QuadPart <= 0) {
|
|
767
|
+
CloseHandle(file);
|
|
768
|
+
se_error_set(err, SE_ERR_IO, "cannot stat %s", path);
|
|
715
769
|
return SE_ERR_IO;
|
|
716
770
|
}
|
|
717
|
-
|
|
718
|
-
if (
|
|
719
|
-
|
|
720
|
-
se_error_set(err,
|
|
721
|
-
return SE_ERR_OOM;
|
|
722
|
-
}
|
|
723
|
-
if (fread(buf, 1, (size_t)size, f) != (size_t)size) {
|
|
724
|
-
free(buf);
|
|
725
|
-
fclose(f);
|
|
726
|
-
se_error_set(err, SE_ERR_IO, "short read on %s", path);
|
|
771
|
+
|
|
772
|
+
if ((uint64_t)file_size.QuadPart > (uint64_t)SIZE_MAX) {
|
|
773
|
+
CloseHandle(file);
|
|
774
|
+
se_error_set(err, SE_ERR_IO, "file is too large to map safely");
|
|
727
775
|
return SE_ERR_IO;
|
|
728
776
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
777
|
+
|
|
778
|
+
void *view = NULL;
|
|
779
|
+
HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
780
|
+
if (mapping) {
|
|
781
|
+
view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
|
782
|
+
CloseHandle(mapping);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (view) {
|
|
786
|
+
CloseHandle(file);
|
|
787
|
+
model->map_base = view;
|
|
788
|
+
model->map_size = (size_t)file_size.QuadPart;
|
|
789
|
+
model->mapped = 1;
|
|
790
|
+
} else {
|
|
791
|
+
size_t size = (size_t)file_size.QuadPart;
|
|
792
|
+
void *buf = se_malloc(SE_ALLOC_MODEL_FILE, size);
|
|
793
|
+
if (!buf) {
|
|
794
|
+
CloseHandle(file);
|
|
795
|
+
se_error_set(err, SE_ERR_OOM, "out of memory");
|
|
796
|
+
return SE_ERR_OOM;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
size_t got = 0;
|
|
800
|
+
while (got < size) {
|
|
801
|
+
DWORD chunk = (DWORD)((size - got) > 0x10000000u ? 0x10000000u : (size - got));
|
|
802
|
+
DWORD read = 0;
|
|
803
|
+
if (!ReadFile(file, (uint8_t *)buf + got, chunk, &read, NULL) || read == 0) {
|
|
804
|
+
se_free(buf);
|
|
805
|
+
CloseHandle(file);
|
|
806
|
+
se_error_set(err, SE_ERR_IO, "short read on %s", path);
|
|
807
|
+
return SE_ERR_IO;
|
|
808
|
+
}
|
|
809
|
+
got += read;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
CloseHandle(file);
|
|
813
|
+
model->map_base = buf;
|
|
814
|
+
model->map_size = size;
|
|
815
|
+
model->mapped = 0;
|
|
816
|
+
}
|
|
733
817
|
#endif
|
|
734
818
|
|
|
735
819
|
se_status_t rc = validate(model, err);
|
|
@@ -747,9 +831,12 @@ void se_model_close(se_model_t *model) {
|
|
|
747
831
|
if (model->mapped)
|
|
748
832
|
munmap(model->map_base, model->map_size);
|
|
749
833
|
else
|
|
750
|
-
|
|
834
|
+
se_free(model->map_base);
|
|
751
835
|
#else
|
|
752
|
-
|
|
836
|
+
if (model->mapped)
|
|
837
|
+
UnmapViewOfFile(model->map_base);
|
|
838
|
+
else
|
|
839
|
+
se_free(model->map_base);
|
|
753
840
|
#endif
|
|
754
841
|
memset(model, 0, sizeof(*model));
|
|
755
842
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <stddef.h>
|
|
5
5
|
#include <stdint.h>
|
|
6
6
|
#include <signal.h>
|
|
7
|
+
#include <limits.h>
|
|
7
8
|
|
|
8
9
|
#define SE_MAGIC "SEMBv1\0\0"
|
|
9
10
|
#define SE_MAGIC_LEN 8
|
|
@@ -26,6 +27,22 @@
|
|
|
26
27
|
#define SE_EMPTY_RAISE 1u
|
|
27
28
|
|
|
28
29
|
#define SE_SLOT_EMPTY 0xFFFFFFFFu
|
|
30
|
+
#define SE_SIZE_MAX ((size_t)-1)
|
|
31
|
+
|
|
32
|
+
#ifndef SE_ENABLE_ALLOC_STATS
|
|
33
|
+
#define SE_ENABLE_ALLOC_STATS 0
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
#define SE_ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
|
37
|
+
|
|
38
|
+
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
|
39
|
+
#define SE_STATIC_ASSERT(expr, name) _Static_assert((expr), #name)
|
|
40
|
+
#else
|
|
41
|
+
#define SE_STATIC_ASSERT_JOIN_INNER(a, b) a##b
|
|
42
|
+
#define SE_STATIC_ASSERT_JOIN(a, b) SE_STATIC_ASSERT_JOIN_INNER(a, b)
|
|
43
|
+
#define SE_STATIC_ASSERT(expr, name) \
|
|
44
|
+
typedef char SE_STATIC_ASSERT_JOIN(se_static_assertion_, name)[(expr) ? 1 : -1]
|
|
45
|
+
#endif
|
|
29
46
|
|
|
30
47
|
enum {
|
|
31
48
|
SE_OFF_MAGIC = 0,
|
|
@@ -199,6 +216,110 @@ typedef struct {
|
|
|
199
216
|
char message[256];
|
|
200
217
|
} se_error_t;
|
|
201
218
|
|
|
219
|
+
typedef enum { SE_VECTOR_FORMAT_F32 = 1, SE_VECTOR_FORMAT_F16 = 2 } se_vector_format_t;
|
|
220
|
+
|
|
221
|
+
typedef enum {
|
|
222
|
+
SE_F16_BACKEND_LUT = 0,
|
|
223
|
+
SE_F16_BACKEND_NEON_FP16 = 1,
|
|
224
|
+
SE_F16_BACKEND_F16C = 2
|
|
225
|
+
} se_f16_backend_t;
|
|
226
|
+
|
|
227
|
+
typedef enum {
|
|
228
|
+
SE_ALLOC_UNKNOWN = 0,
|
|
229
|
+
SE_ALLOC_SCRATCH,
|
|
230
|
+
SE_ALLOC_BATCH_INPUT,
|
|
231
|
+
SE_ALLOC_BATCH_OUTPUT,
|
|
232
|
+
SE_ALLOC_BATCH_STATS,
|
|
233
|
+
SE_ALLOC_BATCH_INDEX,
|
|
234
|
+
SE_ALLOC_TOKEN_IDS,
|
|
235
|
+
SE_ALLOC_TOPK_QUERY,
|
|
236
|
+
SE_ALLOC_TOPK_BEST,
|
|
237
|
+
SE_ALLOC_TOPK_MATRIX_COPY,
|
|
238
|
+
SE_ALLOC_FORMAT_VALIDATE,
|
|
239
|
+
SE_ALLOC_MODEL_FILE,
|
|
240
|
+
SE_ALLOC_CATEGORY_COUNT
|
|
241
|
+
} se_alloc_category_t;
|
|
242
|
+
|
|
243
|
+
typedef struct {
|
|
244
|
+
size_t current_bytes;
|
|
245
|
+
size_t peak_bytes;
|
|
246
|
+
size_t total_allocated_bytes;
|
|
247
|
+
size_t total_freed_bytes;
|
|
248
|
+
size_t alloc_count;
|
|
249
|
+
size_t realloc_count;
|
|
250
|
+
size_t free_count;
|
|
251
|
+
} se_alloc_stats_t;
|
|
252
|
+
|
|
253
|
+
typedef struct {
|
|
254
|
+
const float *q;
|
|
255
|
+
const void *m;
|
|
256
|
+
se_vector_format_t format;
|
|
257
|
+
size_t dim;
|
|
258
|
+
size_t rows;
|
|
259
|
+
long k;
|
|
260
|
+
size_t *best_idx;
|
|
261
|
+
float *best_score;
|
|
262
|
+
int cosine;
|
|
263
|
+
float inv_query_norm;
|
|
264
|
+
volatile sig_atomic_t cancelled;
|
|
265
|
+
} se_topk_job_t;
|
|
266
|
+
|
|
267
|
+
SE_STATIC_ASSERT(sizeof(se_vocab_slot_t) == 16, vocab_slot_size);
|
|
268
|
+
SE_STATIC_ASSERT(sizeof(se_trie_node_t) == 16, trie_node_size);
|
|
269
|
+
SE_STATIC_ASSERT(sizeof(se_trie_edge_t) == 8, trie_edge_size);
|
|
270
|
+
SE_STATIC_ASSERT(sizeof(se_map_entry_t) == 24, map_entry_size);
|
|
271
|
+
SE_STATIC_ASSERT(sizeof(se_range_t) == 8, range_size);
|
|
272
|
+
|
|
273
|
+
static inline int se_checked_add_size(size_t a, size_t b, size_t *out) {
|
|
274
|
+
if (a > SE_SIZE_MAX - b)
|
|
275
|
+
return 0;
|
|
276
|
+
*out = a + b;
|
|
277
|
+
return 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static inline int se_checked_mul_size(size_t a, size_t b, size_t *out) {
|
|
281
|
+
if (a != 0 && b > SE_SIZE_MAX / a)
|
|
282
|
+
return 0;
|
|
283
|
+
*out = a * b;
|
|
284
|
+
return 1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static inline int se_array_bytes(size_t count, size_t elem_size, size_t *out) {
|
|
288
|
+
return se_checked_mul_size(count, elem_size, out);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
static inline size_t se_alloc_count(size_t count) {
|
|
292
|
+
return count ? count : 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static inline int se_alloc_bytes(size_t count, size_t elem_size, size_t *out) {
|
|
296
|
+
return se_checked_mul_size(se_alloc_count(count), elem_size, out);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
static inline int se_next_capacity(size_t current, size_t need, size_t min_capacity, size_t *out) {
|
|
300
|
+
size_t next = current ? current : min_capacity;
|
|
301
|
+
|
|
302
|
+
if (next == 0)
|
|
303
|
+
next = 1;
|
|
304
|
+
while (next < need) {
|
|
305
|
+
if (next > SE_SIZE_MAX / 2)
|
|
306
|
+
return 0;
|
|
307
|
+
next *= 2;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
*out = next;
|
|
311
|
+
return 1;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static inline int se_size_fits_long(size_t n) {
|
|
315
|
+
return n <= (size_t)LONG_MAX;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
static inline int se_host_is_little_endian(void) {
|
|
319
|
+
const uint16_t one = 1;
|
|
320
|
+
return *((const uint8_t *)(const void *)&one) == 1;
|
|
321
|
+
}
|
|
322
|
+
|
|
202
323
|
#if defined(__GNUC__) || defined(__clang__)
|
|
203
324
|
#define SE_PRINTF_FORMAT(fmt_index, first_arg) __attribute__((format(printf, fmt_index, first_arg)))
|
|
204
325
|
#else
|
|
@@ -208,6 +329,28 @@ typedef struct {
|
|
|
208
329
|
void se_error_clear(se_error_t *err);
|
|
209
330
|
void se_error_set(se_error_t *err, se_status_t status, const char *fmt, ...) SE_PRINTF_FORMAT(3, 4);
|
|
210
331
|
|
|
332
|
+
const char *se_alloc_category_name(se_alloc_category_t category);
|
|
333
|
+
void se_alloc_stats_reset(void);
|
|
334
|
+
void se_alloc_stats_snapshot(se_alloc_stats_t out[SE_ALLOC_CATEGORY_COUNT]);
|
|
335
|
+
|
|
336
|
+
#if SE_ENABLE_ALLOC_STATS
|
|
337
|
+
void *se_alloc_stats_malloc(se_alloc_category_t category, size_t bytes);
|
|
338
|
+
void *se_alloc_stats_calloc(se_alloc_category_t category, size_t count, size_t elem_size);
|
|
339
|
+
void *se_alloc_stats_realloc(se_alloc_category_t category, void *ptr, size_t bytes);
|
|
340
|
+
void se_alloc_stats_free(void *ptr);
|
|
341
|
+
|
|
342
|
+
#define se_malloc(category, bytes) se_alloc_stats_malloc((category), (bytes))
|
|
343
|
+
#define se_calloc(category, count, elem_size) \
|
|
344
|
+
se_alloc_stats_calloc((category), (count), (elem_size))
|
|
345
|
+
#define se_realloc(category, ptr, bytes) se_alloc_stats_realloc((category), (ptr), (bytes))
|
|
346
|
+
#define se_free(ptr) se_alloc_stats_free((ptr))
|
|
347
|
+
#else
|
|
348
|
+
#define se_malloc(category, bytes) malloc((bytes))
|
|
349
|
+
#define se_calloc(category, count, elem_size) calloc((count), (elem_size))
|
|
350
|
+
#define se_realloc(category, ptr, bytes) realloc((ptr), (bytes))
|
|
351
|
+
#define se_free(ptr) free((ptr))
|
|
352
|
+
#endif
|
|
353
|
+
|
|
211
354
|
se_status_t se_model_open(se_model_t *model, const char *path, se_error_t *err);
|
|
212
355
|
void se_model_close(se_model_t *model);
|
|
213
356
|
size_t se_model_memsize(const se_model_t *model);
|
|
@@ -263,4 +406,20 @@ se_status_t se_embed_ids(const se_model_t *model, se_scratch_t *scratch, const u
|
|
|
263
406
|
void se_l2_normalize(float *vec, uint32_t dim);
|
|
264
407
|
size_t se_model_warmup(const se_model_t *model);
|
|
265
408
|
|
|
409
|
+
size_t se_vector_format_element_bytes(se_vector_format_t format);
|
|
410
|
+
uint16_t se_float_to_f16_bits(float value);
|
|
411
|
+
float se_f16_bits_to_float(uint16_t half);
|
|
412
|
+
void se_write_f16le(uint8_t *dst, float value);
|
|
413
|
+
float se_read_f16le(const uint8_t *src);
|
|
414
|
+
void se_encode_f16_from_floats(uint8_t *dst, const float *src, size_t count);
|
|
415
|
+
void se_decode_f16_to_floats(float *dst, const uint8_t *src, size_t count);
|
|
416
|
+
void se_select_f16_backend(void);
|
|
417
|
+
se_f16_backend_t se_current_f16_backend(void);
|
|
418
|
+
|
|
419
|
+
float se_dot_product_f32(const float *q, const float *row, size_t dim);
|
|
420
|
+
float se_dot_and_row_sq_f32(const float *q, const float *row, size_t dim, float *row_sq_out);
|
|
421
|
+
float se_dot_product_f16(const float *q, const uint8_t *row, size_t dim);
|
|
422
|
+
float se_dot_and_row_sq_f16(const float *q, const uint8_t *row, size_t dim, float *row_sq_out);
|
|
423
|
+
void *se_topk_execute(void *arg);
|
|
424
|
+
|
|
266
425
|
#endif
|