Реализация to_proc
в 1.8.7 выглядит следующим образом (см. object.c
):
static VALUE
sym_to_proc(VALUE sym)
{
return rb_proc_new(sym_call, (VALUE)SYM2ID(sym));
}
Принимая во внимание, что реализация 1.9.2 (см. string.c
) выглядит следующим образом:
static VALUE
sym_to_proc(VALUE sym)
{
static VALUE sym_proc_cache = Qfalse;
enum {SYM_PROC_CACHE_SIZE = 67};
VALUE proc;
long id, index;
VALUE *aryp;
if (!sym_proc_cache) {
sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2);
rb_gc_register_mark_object(sym_proc_cache);
rb_ary_store(sym_proc_cache, SYM_PROC_CACHE_SIZE*2 - 1, Qnil);
}
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
aryp = RARRAY_PTR(sym_proc_cache);
if (aryp[index] == sym) {
return aryp[index + 1];
}
else {
proc = rb_proc_new(sym_call, (VALUE)id);
aryp[index] = sym;
aryp[index + 1] = proc;
return proc;
}
}
Если вы отбросите всю занятую работу по инициализации sym_proc_cache
, то у вас останется (более или менее) это:
aryp = RARRAY_PTR(sym_proc_cache);
if (aryp[index] == sym) {
return aryp[index + 1];
}
else {
proc = rb_proc_new(sym_call, (VALUE)id);
aryp[index] = sym;
aryp[index + 1] = proc;
return proc;
}
Таким образом, реальная разница в том, что 1.9.2 to_proc
кэширует сгенерированные Procs, а 1.8.7 генерирует новый каждый раз, когда вы вызываете to_proc
. Разница в производительности между этими двумя показателями будет увеличиваться при любом тестировании, если вы не выполняете каждую итерацию в отдельном процессе; однако одна итерация для каждого процесса маскирует то, что вы пытаетесь сравнить с начальными затратами.
Кишки rb_proc_new
выглядят практически одинаково (см. eval.c
для 1.8.7 или proc.c
для 1.9.2), но 1.9.2 может немного выиграть от любых улучшений производительности в rb_iterate
. Кэширование - это, вероятно, большая разница в производительности.
Стоит отметить, что кэш символа к хешу имеет фиксированный размер (67 записей, но я не уверен, откуда берется 67, вероятно, связанных с количеством операторов и таких, которые обычно используются для преобразования символов в символы). -proc преобразований):
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
/* ... */
if (aryp[index] == sym) {
Если вы используете более 67 символов в качестве проков или если ваши идентификаторы символов перекрываются (мод 67), вы не сможете в полной мере воспользоваться кешированием.
Стиль программирования Rails и 1.9 включает в себя множество сокращений, таких как:
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
вместо более длинных явных блочных форм:
ints = strings.collect { |s| s.to_i }
sum = ints.inject(0) { |s,i| s += i }
Учитывая этот (популярный) стиль программирования, имеет смысл обменять память на скорость, кэшируя поиск.
Вы вряд ли получите более быструю реализацию от гема, поскольку гем должен был бы заменить часть основной функциональности Ruby. Вы можете установить кеширование 1.9.2 в свой источник 1.8.7.