加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

为什么在Ruby 1.8.7中Symbol#to_proc较慢?

发布时间:2020-12-17 04:04:07 所属栏目:百科 来源:网络整理
导读:Relative Performance of Symbol#to_proc in Popular Ruby Implementations指出,在MRI Ruby 1.8.7中,Symbol#to_proc比其基准测试中的替代品慢30%到130%,但YARV Ruby 1.9.2中并非如此. 为什么会这样? 1.8.7的创建者没有在纯Ruby中编写Symbol#to_proc. 另外
Relative Performance of Symbol#to_proc in Popular Ruby Implementations指出,在MRI Ruby 1.8.7中,Symbol#to_proc比其基准测试中的替代品慢30%到130%,但YARV Ruby 1.9.2中并非如此.

为什么会这样? 1.8.7的创建者没有在纯Ruby中编写Symbol#to_proc.

另外,有没有为1.8提供更快的Symbol#to_proc性能的宝石?

(当我使用ruby-prof时,符号#to_proc开始出现,所以我认为我不会过早优化)

解决方法

1.8.7中的to_proc实现看起来像这样(参见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时都会生成一个全新的Proc.除非每次迭代都在一个单独的过程中完成,否则这两者之间的性能差异将被任何基准测试放大;但是,每个进程的一次迭代会掩盖您尝试使用启动成本进行基准测试的内容.

rb_proc_new的内容看起来几乎相同(请参阅eval.c for 1.8.7或proc.c for 1.9.2)但1.9.2可能会从rb_iterate中的任何性能改进中略微受益.缓存可能是最大的性能差异.

值得注意的是,符号到散列缓存是固定大小(67个条目,但我不确定67来自哪里,可能与运算符的数量有关,因此通常用于符号到进程的转换):

id = SYM2ID(sym);
index = (id % SYM_PROC_CACHE_SIZE) << 1;
/* ... */
if (aryp[index] == sym) {

如果您使用超过67个符号作为过程或如果您的符号ID重叠(mod 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 }

鉴于(流行的)编程风格,通过缓存查找来交换内存以提高速度是有意义的.

您不太可能从gem获得更快的实现,因为gem必须替换核心Ruby功能的一大块.您可以将1.9.2缓存修补到1.8.7源代码中.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读