static_embeddings 0.1.2 → 0.1.4
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 +161 -0
- data/README.md +13 -6
- 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 +12 -4
- data/docs/BENCHMARKING.md +87 -0
- data/docs/LIMITATIONS.md +93 -0
- data/docs/MODEL_AUDIT.md +27 -13
- data/docs/PERFORMANCE.md +36 -19
- data/ext/static_embeddings/se_embed.c +46 -7
- data/ext/static_embeddings/se_f16.c +43 -7
- data/ext/static_embeddings/se_format.c +83 -5
- data/ext/static_embeddings/se_internal.h +9 -0
- data/ext/static_embeddings/se_tokenizer.c +301 -34
- data/ext/static_embeddings/static_embeddings.c +157 -47
- data/lib/static_embeddings/version.rb +1 -1
- data/static_embeddings.gemspec +2 -0
- metadata +7 -1
|
@@ -274,10 +274,14 @@ static void batch_worker_run(batch_job_t *job, se_scratch_t *scratch) {
|
|
|
274
274
|
|
|
275
275
|
static void *batch_execute(void *arg) {
|
|
276
276
|
batch_job_t *job = (batch_job_t *)arg;
|
|
277
|
-
se_scratch_t scratch;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
se_scratch_t *scratch = se_scratch_acquire(job->model->meta.dim);
|
|
278
|
+
if (!scratch) {
|
|
279
|
+
se_error_set(&job->error, SE_ERR_OOM, "out of memory while sizing scratch buffers");
|
|
280
|
+
job->failed = 1;
|
|
281
|
+
return NULL;
|
|
282
|
+
}
|
|
283
|
+
batch_worker_run(job, scratch);
|
|
284
|
+
se_scratch_release(scratch);
|
|
281
285
|
return NULL;
|
|
282
286
|
}
|
|
283
287
|
|
|
@@ -503,6 +507,51 @@ static VALUE lookup_option(VALUE opts, ID id) {
|
|
|
503
507
|
return rb_hash_lookup2(opts, ID2SYM(id), Qundef);
|
|
504
508
|
}
|
|
505
509
|
|
|
510
|
+
typedef struct {
|
|
511
|
+
const ID *allowed;
|
|
512
|
+
size_t count;
|
|
513
|
+
} keyword_check_t;
|
|
514
|
+
|
|
515
|
+
static int reject_unknown_keyword_i(VALUE key, VALUE value, VALUE arg) {
|
|
516
|
+
const keyword_check_t *check = (const keyword_check_t *)arg;
|
|
517
|
+
(void)value;
|
|
518
|
+
|
|
519
|
+
if (!SYMBOL_P(key))
|
|
520
|
+
rb_raise(rb_eArgError, "keyword must be a Symbol");
|
|
521
|
+
|
|
522
|
+
ID id = SYM2ID(key);
|
|
523
|
+
for (size_t i = 0; i < check->count; i++) {
|
|
524
|
+
if (id == check->allowed[i])
|
|
525
|
+
return ST_CONTINUE;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
VALUE names = rb_ary_new_capa((long)check->count);
|
|
529
|
+
for (size_t i = 0; i < check->count; i++)
|
|
530
|
+
rb_ary_push(names, rb_sprintf(":%s", rb_id2name(check->allowed[i])));
|
|
531
|
+
|
|
532
|
+
const char *name = rb_id2name(id);
|
|
533
|
+
rb_raise(rb_eArgError, "unknown keyword: :%s (accepted: %" PRIsVALUE ")", name ? name : "?",
|
|
534
|
+
rb_ary_join(names, rb_str_new_cstr(", ")));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
static void check_keywords(VALUE opts, const ID *allowed, size_t count) {
|
|
538
|
+
if (NIL_P(opts))
|
|
539
|
+
return;
|
|
540
|
+
if (!RB_TYPE_P(opts, T_HASH))
|
|
541
|
+
rb_raise(rb_eArgError, "keywords must be a Hash");
|
|
542
|
+
|
|
543
|
+
keyword_check_t check;
|
|
544
|
+
check.allowed = allowed;
|
|
545
|
+
check.count = count;
|
|
546
|
+
rb_hash_foreach(opts, reject_unknown_keyword_i, (VALUE)&check);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
#define SE_CHECK_KEYWORDS(opts, ...) \
|
|
550
|
+
do { \
|
|
551
|
+
const ID se_allowed_[] = {__VA_ARGS__}; \
|
|
552
|
+
check_keywords((opts), se_allowed_, sizeof(se_allowed_) / sizeof(se_allowed_[0])); \
|
|
553
|
+
} while (0)
|
|
554
|
+
|
|
506
555
|
static void reject_parallel_threads(VALUE opts) {
|
|
507
556
|
VALUE v = lookup_option(opts, id_threads);
|
|
508
557
|
if (v == Qundef || v == Qnil)
|
|
@@ -867,6 +916,7 @@ static VALUE embed_batch_internal(VALUE self, VALUE texts, VALUE max_tokens_opt,
|
|
|
867
916
|
static VALUE model_embed_batch(int argc, VALUE *argv, VALUE self) {
|
|
868
917
|
VALUE texts, opts;
|
|
869
918
|
rb_scan_args(argc, argv, "1:", &texts, &opts);
|
|
919
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
870
920
|
reject_parallel_threads(opts);
|
|
871
921
|
|
|
872
922
|
VALUE max_tokens = lookup_option(opts, id_max_tokens);
|
|
@@ -882,6 +932,32 @@ static VALUE embed_one_via_batch(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
|
882
932
|
return embed_texts_internal(self, text, 0, 1, max_tokens_opt, format, validation, stats);
|
|
883
933
|
}
|
|
884
934
|
|
|
935
|
+
typedef struct {
|
|
936
|
+
se_scratch_t *scratch;
|
|
937
|
+
const se_model_t *model;
|
|
938
|
+
const uint8_t *input;
|
|
939
|
+
size_t input_len;
|
|
940
|
+
uint32_t max_tokens;
|
|
941
|
+
float *out;
|
|
942
|
+
se_token_stats_t *stats;
|
|
943
|
+
se_error_t err;
|
|
944
|
+
se_status_t rc;
|
|
945
|
+
} embed_one_scratch_job_t;
|
|
946
|
+
|
|
947
|
+
static VALUE embed_one_scratch_body(VALUE arg) {
|
|
948
|
+
embed_one_scratch_job_t *job = (embed_one_scratch_job_t *)(uintptr_t)arg;
|
|
949
|
+
job->rc = se_embed_one(job->model, job->scratch, job->input, job->input_len, job->max_tokens,
|
|
950
|
+
job->out, job->stats, &job->err, NULL);
|
|
951
|
+
return Qnil;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
static VALUE embed_one_scratch_ensure(VALUE arg) {
|
|
955
|
+
embed_one_scratch_job_t *job = (embed_one_scratch_job_t *)(uintptr_t)arg;
|
|
956
|
+
se_scratch_release(job->scratch);
|
|
957
|
+
job->scratch = NULL;
|
|
958
|
+
return Qnil;
|
|
959
|
+
}
|
|
960
|
+
|
|
885
961
|
static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
886
962
|
se_vector_format_t format, se_encoding_validation_t validation,
|
|
887
963
|
se_token_stats_t *stats) {
|
|
@@ -901,25 +977,28 @@ static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
|
901
977
|
rb_enc_associate(result, binary_encoding);
|
|
902
978
|
float *out = (float *)RSTRING_PTR(result);
|
|
903
979
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
980
|
+
se_token_stats_t local_stats;
|
|
981
|
+
embed_one_scratch_job_t job;
|
|
982
|
+
memset(&job, 0, sizeof(job));
|
|
983
|
+
job.model = &w->model;
|
|
984
|
+
job.input = (const uint8_t *)RSTRING_PTR(text);
|
|
985
|
+
job.input_len = (size_t)RSTRING_LEN(text);
|
|
986
|
+
job.max_tokens = resolve_max_tokens(&w->model, max_tokens_opt);
|
|
987
|
+
job.out = out;
|
|
988
|
+
job.stats = stats ? stats : &local_stats;
|
|
989
|
+
se_error_clear(&job.err);
|
|
990
|
+
|
|
991
|
+
job.scratch = se_scratch_acquire(dim);
|
|
992
|
+
if (!job.scratch)
|
|
908
993
|
rb_raise(rb_eNoMemError, "out of memory");
|
|
909
|
-
}
|
|
910
994
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
se_token_stats_t local_stats;
|
|
914
|
-
se_status_t rc =
|
|
915
|
-
se_embed_one(&w->model, &scratch, (const uint8_t *)RSTRING_PTR(text),
|
|
916
|
-
(size_t)RSTRING_LEN(text), resolve_max_tokens(&w->model, max_tokens_opt), out,
|
|
917
|
-
stats ? stats : &local_stats, &err, NULL);
|
|
918
|
-
se_scratch_free(&scratch);
|
|
995
|
+
rb_ensure(embed_one_scratch_body, (VALUE)(uintptr_t)&job, embed_one_scratch_ensure,
|
|
996
|
+
(VALUE)(uintptr_t)&job);
|
|
919
997
|
RB_GC_GUARD(text);
|
|
998
|
+
RB_GC_GUARD(result);
|
|
920
999
|
|
|
921
|
-
if (rc != SE_OK)
|
|
922
|
-
raise_se(&err);
|
|
1000
|
+
if (job.rc != SE_OK)
|
|
1001
|
+
raise_se(&job.err);
|
|
923
1002
|
|
|
924
1003
|
return result;
|
|
925
1004
|
}
|
|
@@ -927,6 +1006,7 @@ static VALUE embed_one_value(VALUE self, VALUE text, VALUE max_tokens_opt,
|
|
|
927
1006
|
static VALUE model_embed(int argc, VALUE *argv, VALUE self) {
|
|
928
1007
|
VALUE text, opts;
|
|
929
1008
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1009
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
930
1010
|
reject_parallel_threads(opts);
|
|
931
1011
|
return embed_one_value(self, text, lookup_option(opts, id_max_tokens),
|
|
932
1012
|
resolve_vector_format(lookup_option(opts, id_format)),
|
|
@@ -937,6 +1017,7 @@ static VALUE model_embed(int argc, VALUE *argv, VALUE self) {
|
|
|
937
1017
|
static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
938
1018
|
VALUE text, opts;
|
|
939
1019
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1020
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_validate_encoding, id_threads);
|
|
940
1021
|
reject_parallel_threads(opts);
|
|
941
1022
|
|
|
942
1023
|
se_token_stats_t stats;
|
|
@@ -953,41 +1034,69 @@ static VALUE model_embed_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
|
953
1034
|
return hash;
|
|
954
1035
|
}
|
|
955
1036
|
|
|
1037
|
+
typedef struct {
|
|
1038
|
+
se_scratch_t *scratch;
|
|
1039
|
+
const se_model_t *model;
|
|
1040
|
+
const uint8_t *input;
|
|
1041
|
+
size_t input_len;
|
|
1042
|
+
uint32_t max_tokens;
|
|
1043
|
+
se_token_stats_t stats;
|
|
1044
|
+
se_error_t err;
|
|
1045
|
+
se_status_t rc;
|
|
1046
|
+
VALUE ids;
|
|
1047
|
+
} tokenize_scratch_job_t;
|
|
1048
|
+
|
|
1049
|
+
static VALUE tokenize_scratch_body(VALUE arg) {
|
|
1050
|
+
tokenize_scratch_job_t *job = (tokenize_scratch_job_t *)(uintptr_t)arg;
|
|
1051
|
+
job->rc = se_tokenize(job->model, job->scratch, job->input, job->input_len, job->max_tokens,
|
|
1052
|
+
&job->stats, &job->err, NULL);
|
|
1053
|
+
if (job->rc != SE_OK)
|
|
1054
|
+
return Qnil;
|
|
1055
|
+
|
|
1056
|
+
job->ids = rb_ary_new_capa((long)job->stats.token_count);
|
|
1057
|
+
for (uint32_t i = 0; i < job->stats.token_count; i++)
|
|
1058
|
+
rb_ary_push(job->ids, UINT2NUM(job->scratch->ids[i]));
|
|
1059
|
+
return Qnil;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
static VALUE tokenize_scratch_ensure(VALUE arg) {
|
|
1063
|
+
tokenize_scratch_job_t *job = (tokenize_scratch_job_t *)(uintptr_t)arg;
|
|
1064
|
+
se_scratch_release(job->scratch);
|
|
1065
|
+
job->scratch = NULL;
|
|
1066
|
+
return Qnil;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
956
1069
|
static VALUE model_tokenize(int argc, VALUE *argv, VALUE self) {
|
|
957
1070
|
VALUE text, opts;
|
|
958
1071
|
rb_scan_args(argc, argv, "1:", &text, &opts);
|
|
1072
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_validate_encoding);
|
|
959
1073
|
|
|
960
1074
|
model_wrapper_t *w = get_model(self);
|
|
961
1075
|
Check_Type(text, T_STRING);
|
|
962
1076
|
check_text_encoding_mode(
|
|
963
1077
|
text, -1, resolve_encoding_validation(lookup_option(opts, id_validate_encoding)));
|
|
964
1078
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1079
|
+
tokenize_scratch_job_t job;
|
|
1080
|
+
memset(&job, 0, sizeof(job));
|
|
1081
|
+
job.model = &w->model;
|
|
1082
|
+
job.input = (const uint8_t *)RSTRING_PTR(text);
|
|
1083
|
+
job.input_len = (size_t)RSTRING_LEN(text);
|
|
1084
|
+
job.max_tokens = resolve_max_tokens(&w->model, lookup_option(opts, id_max_tokens));
|
|
1085
|
+
job.ids = Qnil;
|
|
1086
|
+
se_error_clear(&job.err);
|
|
1087
|
+
|
|
1088
|
+
job.scratch = se_scratch_acquire(w->model.meta.dim);
|
|
1089
|
+
if (!job.scratch)
|
|
971
1090
|
rb_raise(rb_eNoMemError, "out of memory");
|
|
972
|
-
}
|
|
973
1091
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
se_status_t rc = se_tokenize(&w->model, &scratch, (const uint8_t *)RSTRING_PTR(text),
|
|
978
|
-
(size_t)RSTRING_LEN(text), max_tokens, &stats, &err, NULL);
|
|
979
|
-
if (rc != SE_OK) {
|
|
980
|
-
se_scratch_free(&scratch);
|
|
981
|
-
raise_se(&err);
|
|
982
|
-
}
|
|
1092
|
+
rb_ensure(tokenize_scratch_body, (VALUE)(uintptr_t)&job, tokenize_scratch_ensure,
|
|
1093
|
+
(VALUE)(uintptr_t)&job);
|
|
1094
|
+
RB_GC_GUARD(text);
|
|
983
1095
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
rb_ary_push(ids, UINT2NUM(scratch.ids[i]));
|
|
1096
|
+
if (job.rc != SE_OK)
|
|
1097
|
+
raise_se(&job.err);
|
|
987
1098
|
|
|
988
|
-
|
|
989
|
-
RB_GC_GUARD(text);
|
|
990
|
-
return ids;
|
|
1099
|
+
return job.ids;
|
|
991
1100
|
}
|
|
992
1101
|
|
|
993
1102
|
typedef struct {
|
|
@@ -1028,17 +1137,15 @@ static VALUE num2ull_at_value(VALUE arg) {
|
|
|
1028
1137
|
|
|
1029
1138
|
static void *ids_execute(void *arg) {
|
|
1030
1139
|
ids_job_t *job = (ids_job_t *)arg;
|
|
1031
|
-
se_scratch_t scratch;
|
|
1032
|
-
|
|
1033
|
-
if (!se_scratch_reserve(&scratch, job->model->meta.dim)) {
|
|
1140
|
+
se_scratch_t *scratch = se_scratch_acquire(job->model->meta.dim);
|
|
1141
|
+
if (!scratch) {
|
|
1034
1142
|
se_error_set(&job->error, SE_ERR_OOM, "out of memory while sizing scratch buffers");
|
|
1035
|
-
se_scratch_free(&scratch);
|
|
1036
1143
|
return NULL;
|
|
1037
1144
|
}
|
|
1038
1145
|
se_error_clear(&job->error);
|
|
1039
|
-
se_embed_ids(job->model,
|
|
1146
|
+
se_embed_ids(job->model, scratch, job->ids, job->n_ids, job->out, &job->stats, &job->error,
|
|
1040
1147
|
&job->cancelled);
|
|
1041
|
-
|
|
1148
|
+
se_scratch_release(scratch);
|
|
1042
1149
|
return NULL;
|
|
1043
1150
|
}
|
|
1044
1151
|
|
|
@@ -1168,6 +1275,7 @@ static VALUE embed_token_ids_value(VALUE self, VALUE ids_value, VALUE max_tokens
|
|
|
1168
1275
|
static VALUE model_embed_token_ids(int argc, VALUE *argv, VALUE self) {
|
|
1169
1276
|
VALUE ids_value, opts;
|
|
1170
1277
|
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1278
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_threads);
|
|
1171
1279
|
reject_parallel_threads(opts);
|
|
1172
1280
|
return embed_token_ids_value(self, ids_value, lookup_option(opts, id_max_tokens),
|
|
1173
1281
|
resolve_vector_format(lookup_option(opts, id_format)), NULL);
|
|
@@ -1176,6 +1284,7 @@ static VALUE model_embed_token_ids(int argc, VALUE *argv, VALUE self) {
|
|
|
1176
1284
|
static VALUE model_embed_token_ids_with_stats(int argc, VALUE *argv, VALUE self) {
|
|
1177
1285
|
VALUE ids_value, opts;
|
|
1178
1286
|
rb_scan_args(argc, argv, "1:", &ids_value, &opts);
|
|
1287
|
+
SE_CHECK_KEYWORDS(opts, id_max_tokens, id_format, id_threads);
|
|
1179
1288
|
reject_parallel_threads(opts);
|
|
1180
1289
|
|
|
1181
1290
|
se_token_stats_t stats;
|
|
@@ -1315,6 +1424,7 @@ static void topk_check_matrix(VALUE matrix, size_t matrix_bytes, se_vector_forma
|
|
|
1315
1424
|
static VALUE top_k_impl(int argc, VALUE *argv, VALUE self, int cosine) {
|
|
1316
1425
|
VALUE query, matrix, k_val, opts;
|
|
1317
1426
|
rb_scan_args(argc, argv, "3:", &query, &matrix, &k_val, &opts);
|
|
1427
|
+
SE_CHECK_KEYWORDS(opts, id_dim, id_format, id_allow_unfrozen);
|
|
1318
1428
|
(void)self;
|
|
1319
1429
|
|
|
1320
1430
|
Check_Type(query, T_STRING);
|
data/static_embeddings.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: static_embeddings
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
@@ -80,7 +80,13 @@ files:
|
|
|
80
80
|
- CHANGELOG.md
|
|
81
81
|
- LICENSE.txt
|
|
82
82
|
- README.md
|
|
83
|
+
- Rakefile
|
|
84
|
+
- benchmark/_support.rb
|
|
85
|
+
- benchmark/core_paths.rb
|
|
86
|
+
- benchmark/gvl_threshold.rb
|
|
83
87
|
- docs/ARCHITECTURE.md
|
|
88
|
+
- docs/BENCHMARKING.md
|
|
89
|
+
- docs/LIMITATIONS.md
|
|
84
90
|
- docs/MODEL_AUDIT.md
|
|
85
91
|
- docs/PERFORMANCE.md
|
|
86
92
|
- exe/static_embeddings
|