Skip to content

Commit 87d8d9c

Browse files
committed
Add fast_transcoder_entry_table, rename other transcoder managed tbl
We want to avoid taking the VM lock at all on transcoding operations like `String#encode`, so now we have another managed table so we can avoid going through a two-level table that requires a VM lock. I think this is justified because looking up transcoder entries is done fairly often. Ractors are now quite fast when using `String#encode`.
1 parent ffa0c0d commit 87d8d9c

1 file changed

Lines changed: 55 additions & 73 deletions

File tree

transcode.c

Lines changed: 55 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "id.h"
2929

3030
#define ENABLE_ECONV_NEWLINE_OPTION 1
31+
#define SRC_ENC_TO_DST_ENC_KEY_SIZE 128
3132

3233
/* VALUE rb_cEncoding = rb_define_class("Encoding", rb_cObject); */
3334
static VALUE rb_eUndefinedConversionError;
@@ -64,7 +65,8 @@ static VALUE sym_finished;
6465
static VALUE sym_after_output;
6566
static VALUE sym_incomplete_input;
6667

67-
static VALUE fast_transcoder_table;
68+
static VALUE fast_transcoder_path_table;
69+
static VALUE fast_transcoder_entry_table;
6870

6971
static unsigned char *
7072
allocate_converted_string(const char *sname, const char *dname,
@@ -208,11 +210,26 @@ rb_free_transcoder_table(void)
208210
st_free_table(transcoder_table);
209211
}
210212

213+
static void
214+
gen_src_to_dst_encodings_key(const char key_buf[SRC_ENC_TO_DST_ENC_KEY_SIZE], const char *sname, const char *dname)
215+
{
216+
char *p = (char*)key_buf;
217+
size_t slen = strlen(sname);
218+
memcpy(p, sname, slen);
219+
p += slen;
220+
memcpy(p, ":", 1);
221+
p += 1;
222+
size_t dlen = strlen(dname);
223+
RUBY_ASSERT(slen + dlen < SRC_ENC_TO_DST_ENC_KEY_SIZE);
224+
memcpy(p, dname, dlen);
225+
}
226+
211227
static transcoder_entry_t *
212228
make_transcoder_entry(const char *sname, const char *dname)
213229
{
214230
st_data_t val;
215231
st_table *table2;
232+
transcoder_entry_t *entry = NULL;
216233

217234
RB_VM_LOCKING() {
218235
if (!st_lookup(transcoder_table, (st_data_t)sname, &val)) {
@@ -221,23 +238,39 @@ make_transcoder_entry(const char *sname, const char *dname)
221238
}
222239
table2 = (st_table *)val;
223240
if (!st_lookup(table2, (st_data_t)dname, &val)) {
224-
transcoder_entry_t *entry = ALLOC(transcoder_entry_t);
241+
entry = ALLOC(transcoder_entry_t);
225242
entry->sname = sname;
226243
entry->dname = dname;
227244
entry->lib = NULL;
228245
entry->transcoder = NULL;
229246
val = (st_data_t)entry;
230247
st_add_direct(table2, (st_data_t)dname, val);
248+
} else {
249+
entry = (transcoder_entry_t*)val;
231250
}
232251
}
233-
return (transcoder_entry_t *)val;
252+
char key_buf[SRC_ENC_TO_DST_ENC_KEY_SIZE] = { 0 };
253+
gen_src_to_dst_encodings_key(key_buf, sname, dname);
254+
VALUE tbl = fast_transcoder_entry_table;
255+
VALUE new_tbl = rb_managed_id_table_dup(tbl);
256+
rb_managed_id_table_insert(new_tbl, rb_intern(key_buf), (VALUE)entry);
257+
RUBY_ATOMIC_VALUE_SET(fast_transcoder_entry_table, new_tbl); // TODO: use CAS
258+
return entry;
234259
}
235260

236261
static transcoder_entry_t *
237262
get_transcoder_entry(const char *sname, const char *dname)
238263
{
239264
st_data_t val = 0;
240265
st_table *table2;
266+
char key_buf[SRC_ENC_TO_DST_ENC_KEY_SIZE] = { 0 };
267+
gen_src_to_dst_encodings_key(key_buf, sname, dname);
268+
VALUE entry_val;
269+
// TODO: once we CAS in `make_transcoder_entry`, we no longer need to check regular transcoder_table after
270+
if (rb_managed_id_table_lookup(fast_transcoder_entry_table, rb_intern(key_buf), &entry_val)) {
271+
return (transcoder_entry_t*)entry_val;
272+
}
273+
241274
RB_VM_LOCKING() {
242275
if (st_lookup(transcoder_table, (st_data_t)sname, &val)) {
243276
table2 = (st_table *)val;
@@ -1032,8 +1065,12 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10321065
int num_trans;
10331066
rb_econv_t *ec;
10341067

1035-
if (*sname && (!senc || !senc->max_enc_len)) rb_enc_find_index(sname); // loads encoding if not already loaded
1036-
if (*dname && (!denc || !denc->max_enc_len)) rb_enc_find_index(dname); // loads encoding if not already loaded
1068+
if (*sname && (!senc || !senc->max_enc_len)) {
1069+
rb_enc_find_index(sname); // loads encoding
1070+
}
1071+
if (*dname && (!denc || !denc->max_enc_len)) {
1072+
rb_enc_find_index(dname); // loads encoding
1073+
}
10371074

10381075
if (*sname == '\0' && *dname == '\0') {
10391076
num_trans = 0;
@@ -1044,17 +1081,11 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10441081
struct trans_open_t toarg;
10451082
toarg.entries = NULL;
10461083
toarg.num_additional = 0;
1047-
char buf[128] = { 0 }; // encoding namelen max is currently 63 bytes, so this is enough
1048-
char *p = buf;
1049-
size_t slen = strlen(sname);
1050-
memcpy(p, sname, slen);
1051-
p += slen;
1052-
memcpy(p, ":", 1);
1053-
p += 1;
1054-
memcpy(p, dname, strlen(dname));
1055-
ID src_to_dest_id = rb_intern(buf);
1084+
char key_buf[SRC_ENC_TO_DST_ENC_KEY_SIZE] = { 0 };
1085+
gen_src_to_dst_encodings_key(key_buf, sname, dname);
1086+
ID src_to_dest_id = rb_intern(key_buf);
10561087
VALUE managed_val;
1057-
VALUE tbl = RUBY_ATOMIC_VALUE_LOAD(fast_transcoder_table);
1088+
VALUE tbl = RUBY_ATOMIC_VALUE_LOAD(fast_transcoder_path_table);
10581089
if (rb_managed_id_table_lookup(tbl, src_to_dest_id, &managed_val)) {
10591090
entries = (transcoder_entry_t **)managed_val;
10601091
} else {
@@ -1067,9 +1098,9 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10671098
// No need for CAS loop if it's not most recent `fast_transcoder_table`, some values
10681099
// can be lost. It will just go through the slow path next time for the lost src/dst encoding
10691100
// pairs
1070-
VALUE new_transcoder_tbl = rb_managed_id_table_dup(tbl);
1071-
rb_managed_id_table_insert(new_transcoder_tbl, src_to_dest_id, (VALUE)entries);
1072-
RUBY_ATOMIC_VALUE_SET(fast_transcoder_table, new_transcoder_tbl);
1101+
VALUE new_tbl = rb_managed_id_table_dup(tbl);
1102+
rb_managed_id_table_insert(new_tbl, src_to_dest_id, (VALUE)entries);
1103+
RUBY_ATOMIC_VALUE_SET(fast_transcoder_path_table, new_tbl);
10731104
}
10741105
}
10751106

@@ -1159,30 +1190,7 @@ rb_econv_open_enc(rb_encoding *senc, const char *sname, rb_encoding *denc, const
11591190
rb_econv_t *
11601191
rb_econv_open(const char *sname, const char *dname, int ecflags)
11611192
{
1162-
rb_econv_t *ec;
1163-
int num_decorators;
1164-
const char *decorators[MAX_ECFLAGS_DECORATORS];
1165-
int i;
1166-
1167-
num_decorators = decorator_names(ecflags, decorators);
1168-
if (num_decorators == -1)
1169-
return NULL;
1170-
1171-
ec = rb_econv_open0(NULL, sname, NULL, dname, ecflags & ECONV_ERROR_HANDLER_MASK);
1172-
if (ec) {
1173-
for (i = 0; i < num_decorators; i++) {
1174-
if (rb_econv_decorate_at_last(ec, decorators[i]) == -1) {
1175-
rb_econv_close(ec);
1176-
ec = NULL;
1177-
break;
1178-
}
1179-
}
1180-
}
1181-
1182-
if (ec) {
1183-
ec->flags |= ecflags & ~ECONV_ERROR_HANDLER_MASK;
1184-
}
1185-
return ec; // can be NULL
1193+
return rb_econv_open_enc(NULL, sname, NULL, dname, ecflags);
11861194
}
11871195

11881196
static int
@@ -2755,35 +2763,7 @@ rb_econv_open_opts_enc(rb_encoding *senc, const char *source_encoding, rb_encodi
27552763
rb_econv_t *
27562764
rb_econv_open_opts(const char *source_encoding, const char *destination_encoding, int ecflags, VALUE opthash)
27572765
{
2758-
rb_econv_t *ec;
2759-
VALUE replacement;
2760-
2761-
if (NIL_P(opthash)) {
2762-
replacement = Qnil;
2763-
}
2764-
else {
2765-
if (!RB_TYPE_P(opthash, T_HASH) || !OBJ_FROZEN(opthash))
2766-
rb_bug("rb_econv_open_opts called with invalid opthash");
2767-
replacement = rb_hash_aref(opthash, sym_replace);
2768-
}
2769-
2770-
ec = rb_econv_open(source_encoding, destination_encoding, ecflags);
2771-
if (ec) {
2772-
if (!NIL_P(replacement)) {
2773-
int ret;
2774-
rb_encoding *enc = rb_enc_get(replacement);
2775-
2776-
ret = rb_econv_set_replacement(ec,
2777-
(const unsigned char *)RSTRING_PTR(replacement),
2778-
RSTRING_LEN(replacement),
2779-
rb_enc_name(enc));
2780-
if (ret == -1) {
2781-
rb_econv_close(ec);
2782-
ec = NULL;
2783-
}
2784-
}
2785-
}
2786-
return ec; // can be NULL
2766+
return rb_econv_open_opts_enc(NULL, source_encoding, NULL, destination_encoding, ecflags, opthash);
27872767
}
27882768

27892769
static int
@@ -4568,8 +4548,10 @@ void
45684548
Init_transcode(void)
45694549
{
45704550
transcoder_table = st_init_strcasetable();
4571-
fast_transcoder_table = rb_managed_id_table_new(8); // NOTE: size is arbitrarily chosen
4572-
rb_gc_register_address(&fast_transcoder_table);
4551+
fast_transcoder_path_table = rb_managed_id_table_new(8); // NOTE: size is arbitrarily chosen
4552+
rb_gc_register_address(&fast_transcoder_path_table);
4553+
fast_transcoder_entry_table = rb_managed_id_table_new(8);
4554+
rb_gc_register_address(&fast_transcoder_entry_table);
45734555

45744556
id_destination_encoding = rb_intern_const("destination_encoding");
45754557
id_destination_encoding_name = rb_intern_const("destination_encoding_name");

0 commit comments

Comments
 (0)