如何缓存Ruby VALUE值以供以后重用?

时间:2017-11-17 13:19:07

标签: c++ ruby caching garbage-collection

我将Ruby 2.2嵌入到C ++应用程序中并尝试优化表单的功能

VALUE toRubyString( const MyObject &o )
{
     const char *utf8 = get_string_rep_of( o );
     return rb_enc_str_new( utf8, strlen( utf8 ), rb_utf8_encoding() );
}

事实证明,在某些用例中,toRubyString支配运行时分析输出。在这些情况下,函数会被频繁调用,但只有几个不同的MyObject值。因此,我的想法是将VALUE值缓存在std::map<MyObject, VALUE>之类,以便我可以重复使用它们,

std::map<MyObject, VALUE> cache;

VALUE toRubyString( const MyObject &o )
{
     std::map<MyObject, VALUE>::const_iterator it = cache.find( o );
     if ( it != cache.end() ) {
         return it->second;
     }
     const char *utf8 = get_string_rep_of( o );
     VALUE v = rb_enc_str_new( utf8, strlen( utf8 ), rb_utf8_encoding() );
     cache[o] = v;
     return v;
}

唉,我注意到通过这个修改,Ruby解释器最终崩溃了,如果我省略return it->second;行,那么崩溃就会消失(即当代码避免重用缓存的条目时)。

我怀疑这与垃圾收集器有关,因为它只发生在几千次调用函数之后,甚至是

rb_gc_mark(v);

调用(在将VALUE添加到缓存之前)没有帮助。有没有人知道我在这里可能缺少什么?

1 个答案:

答案 0 :(得分:1)

rb_global_variable(&cache[o]) 可能会工作。

Apparently,插入地图不应该使指向现有元素的指针无效,所以在技术上可以像这样使用地图值的地址。然后rb_global_variable将使得缓存的值完全从GC中排除。

rb_gc_mark不起作用的原因是因为这只会阻止在 next 集合上清除对象。您通常在定义custom class's mark function时使用它,以便GC可以适当地标记内部引用的对象。