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.
@@ -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
- se_scratch_init(&scratch);
279
- batch_worker_run(job, &scratch);
280
- se_scratch_free(&scratch);
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
- se_scratch_t scratch;
905
- se_scratch_init(&scratch);
906
- if (!se_scratch_reserve(&scratch, dim)) {
907
- se_scratch_free(&scratch);
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
- se_error_t err;
912
- se_error_clear(&err);
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
- uint32_t max_tokens = resolve_max_tokens(&w->model, lookup_option(opts, id_max_tokens));
966
-
967
- se_scratch_t scratch;
968
- se_scratch_init(&scratch);
969
- if (!se_scratch_reserve(&scratch, w->model.meta.dim)) {
970
- se_scratch_free(&scratch);
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
- se_token_stats_t stats;
975
- se_error_t err;
976
- se_error_clear(&err);
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
- VALUE ids = rb_ary_new_capa((long)stats.token_count);
985
- for (uint32_t i = 0; i < stats.token_count; i++)
986
- rb_ary_push(ids, UINT2NUM(scratch.ids[i]));
1096
+ if (job.rc != SE_OK)
1097
+ raise_se(&job.err);
987
1098
 
988
- se_scratch_free(&scratch);
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
- se_scratch_init(&scratch);
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, &scratch, job->ids, job->n_ids, job->out, &job->stats, &job->error,
1146
+ se_embed_ids(job->model, scratch, job->ids, job->n_ids, job->out, &job->stats, &job->error,
1040
1147
  &job->cancelled);
1041
- se_scratch_free(&scratch);
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);
@@ -1,3 +1,3 @@
1
1
  module StaticEmbeddings
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -25,8 +25,10 @@ Gem::Specification.new do |spec|
25
25
  "lib/models/*.semb",
26
26
  "exe/*",
27
27
  "ext/**/*.{c,h,rb}",
28
+ "benchmark/**/*.rb",
28
29
  "docs/**/*.md",
29
30
  "tools/*.rb",
31
+ "Rakefile",
30
32
  "static_embeddings.gemspec",
31
33
  "README.md",
32
34
  "CHANGELOG.md",
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.2
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