And*_*imm 4 c ruby optimization ruby-1.8
流行Ruby实现中Symbol#to_proc的相对性能表明在MRI Ruby 1.8.7中,Symbol#to_proc它比基准测试中的替代品慢30%到130%,但在YARV Ruby 1.9.2中并非如此.
为什么会这样?1.8.7的创建者没有Symbol#to_proc使用纯Ruby 编写.
另外,有没有为1.8提供更快的Symbol#to_proc性能的宝石?
(当我使用ruby-prof时,符号#to_proc开始出现,所以我认为我不会过早优化)
to_proc1.8.7中的实现看起来像这样(参见object.c):
static VALUE
sym_to_proc(VALUE sym)
{
return rb_proc_new(sym_call, (VALUE)SYM2ID(sym));
}
Run Code Online (Sandbox Code Playgroud)
而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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果你剥离了所有繁忙的初始化工作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;
}
Run Code Online (Sandbox Code Playgroud)
所以真正的区别在于1.9.2的to_proc缓存生成的Procs,而1.8.7每次调用时都会生成一个全新的Proc to_proc.除非每次迭代都在一个单独的过程中完成,否则这两者之间的性能差异将被任何基准测试放大; 但是,每个进程的一次迭代会掩盖您尝试使用启动成本进行基准测试的内容.
胆量rb_proc_new外观几乎相同(见eval.c为1.8.7或proc.c为1.9.2),但可能1.9.2从任何性能改进略有受益rb_iterate.缓存可能是最大的性能差异.
值得注意的是,符号到散列缓存是固定大小(67个条目,但我不确定67来自哪里,可能与运算符的数量有关,因此通常用于符号到进程的转换):
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
/* ... */
if (aryp[index] == sym) {
Run Code Online (Sandbox Code Playgroud)
如果您使用超过67个符号作为过程或如果您的符号ID重叠(mod 67),那么您将无法获得缓存的全部好处.
Rails和1.9编程风格涉及很多简写:
id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
Run Code Online (Sandbox Code Playgroud)
而不是更长的显式块形式:
ints = strings.collect { |s| s.to_i }
sum = ints.inject(0) { |s,i| s += i }
Run Code Online (Sandbox Code Playgroud)
鉴于(流行的)编程风格,通过缓存查找来交换内存以提高速度是有意义的.
您不太可能从gem获得更快的实现,因为gem必须替换核心Ruby功能的一大块.您可以将1.9.2缓存修补到1.8.7源代码中.
| 归档时间: |
|
| 查看次数: |
503 次 |
| 最近记录: |