为什么是Symbol#to_proc用Ruby 1.8.7慢? [英] Why is Symbol#to_proc slower in Ruby 1.8.7?
问题描述
<一个href=\"http://blog.gregspurrier.com/articles/relative-performance-of-symbol-to-proc-in-popular-ruby-implementations\"相对=nofollow>在流行的Ruby实现符号#to_proc的相对性能指出,在MRI的Ruby 1.8.7,符号#to_proc
是比其他慢在30%至130%,但其基准的,这不是在YARV红宝石1.9.2的情况
Relative Performance of Symbol#to_proc in Popular Ruby Implementations states that in MRI Ruby 1.8.7, Symbol#to_proc
is slower than the alternative in their benchmark by 30% to 130%, but that this isn't the case in YARV Ruby 1.9.2.
为什么会出现这种情况? 1.8.7的创作者在纯Ruby没有写符号#to_proc
。
Why is this the case? The creators of 1.8.7 didn't write Symbol#to_proc
in pure Ruby.
此外,是否有1.8提供更快的符号#to_proc性能有任何宝石?
Also, are there any gems that provide faster Symbol#to_proc performance for 1.8?
(符号#to_proc开始,当我使用红宝石教授出现,所以我不认为我有罪的premature优化)
(Symbol#to_proc is starting to appear when I use ruby-prof, so I don't think I'm guilty of premature optimization)
推荐答案
的 to_proc
实施1.8.7看起来像这样(见对象。 ç
)
The to_proc
implementation in 1.8.7 looks like this (see 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
,那么留给你(或多或少)这样的:
If you strip away all the busy work of initializing sym_proc_cache
, then you're left with (more or less) this:
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
缓存生成的特效,而1.8.7产生一个全新的你打电话,每一次 to_proc
。这两者之间的性能差异会被任何基准测试你这样做,除非每次迭代是在一个单独的进程完成被放大;然而,每个进程一次迭代会掩盖你想基准的启动成本的。
So the real difference is the 1.9.2's to_proc
caches the generated Procs while 1.8.7 generates a brand new one every single time you call to_proc
. The performance difference between these two will be magnified by any benchmarking you do unless each iteration is done in a separate process; however, one iteration per-process would mask what you're trying to benchmark with the start-up cost.
rb_proc_new
看看pretty的胆量大致相同(见 eval.c
为1.8.7或 proc.c
为1.9.2),但可能1.9.2从 rb_iterate
任何性能改进略微受益。缓存可能是很大的性能差异。
The guts of rb_proc_new
look pretty much the same (see eval.c
for 1.8.7 or proc.c
for 1.9.2) but 1.9.2 might benefit slightly from any performance improvements in rb_iterate
. The caching is probably the big performance difference.
值得注意的是,符号到散列缓存大小是固定的(67项,但我不知道在哪里67从何而来,可能与运营商的数量,这样通常用于符号到转换-proc):
It is worth noting that the symbol-to-hash cache is a fixed size (67 entries but I'm not sure where 67 comes from, probably related to the number of operators and such that are commonly used for symbol-to-proc conversions):
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
/* ... */
if (aryp[index] == sym) {
如果您使用超过67符号特效,或者如果你的符号标识重叠(MOD 67),那么您将无法获得缓存中充分受益。
If you use more than 67 symbols as procs or if your symbol IDs overlap (mod 67) then you won't get the full benefit of the caching.
导轨和1.9编程风格涉及到很多速记的,如:
The Rails and 1.9 programming style involves a lot of shorthands like:
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
而不是明确的长块的形式:
rather than the longer explicit block forms:
ints = strings.collect { |s| s.to_i }
sum = ints.inject(0) { |s,i| s += i }
由于(流行)的编程风格,这是有道理通过缓存查找来换取高速存储器。
Given that (popular) programming style, it makes sense to trade memory for speed by caching the lookup.
你不可能从一个创业板更快地实现以创业板将要更换的核心功能红宝石一大块。你可以打补丁1.9.2缓存到1.8.7的源代码,但。
You're not likely to get a faster implementation from a gem as the gem would have to replace a chunk of the core Ruby functionality. You could patch the 1.9.2 caching into your 1.8.7 source though.
这篇关于为什么是Symbol#to_proc用Ruby 1.8.7慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!