Rubyracer(Ruby的V8绑定)执行速度非常慢

因此,我在eventmachine中有一个TCP服务器,而therubyracer用作预先挂起服务器操作(如filter或扩展)的方法。 当服务器没有接收到大量数据时,这一切都很有吸引力,但是当它被淹没时(有时需要它)它变得非常慢。

所以,我做了一个小基准来看看rubyracer与Ruby相比有多慢,当我看到结果时我感到震惊

user system total real V8: 0.060000 0.000000 0.060000 ( 0.059903) Ruby: 0.000000 0.000000 0.000000 ( 0.000524) 

我不介意它是否很慢,说实话,但我不希望它锁定我的整个服务器,直到它完成处理数据。 使用EM::defer并不是一个真正的选择(我试过它,但它有时会产生大量的线程,具体取决于洪水的密集程度)。 我无法绕过洪水,因为我没有设计协议,客户端要求它们就像那样(尽管很可怕)。

基准代码:

 require 'v8' require 'benchmark' class User def initialize @name = "smack" @sex = "female" @age = rand(100) @health = rand(100) @level = rand(100) @colour = rand(14) end attr_accessor :name, :sex, :age, :health, :level, :colour end # Create context and the function context = V8::Context.new code = "obj = { __incybincy__: function() { user.name + '' + '' + '' user.sex + '' + '' + '' user.age + '' + '' + '' user.health + '' + '' + '' user.level + '' + '' + '' user.colour + '' + '' + '' } }" context.eval(code) # Insert the user into the context user = User.new context["user"] = user # Benchmark n = 100 Benchmark.bm do |x| x.report("V8: ") do n.times do context['obj'].__incybincy__ end end x.report("Ruby: ") do n.times do user.name + "" + "" user.sex + "" + "" user.age.to_s + "" + "" user.health.to_s + "" + "" user.level.to_s + "" + "" user.colour.to_s + "" + "" end end end 

编辑

问题:有没有办法消除由therubyracer引起的瓶颈? 通过其他方式将JavaScript实现到Ruby中是可以接受的。


2012年3月7日更新

因此,我设法优化代码,因为我认为造成瓶颈的原因是Ruby JS通信,每次执行[native code]时都会发生这种情况,这一直是因为ruby使用getter和setter方法对于类,或者直接在语言之间传递对象。

  user system total real V8-optimized: 0.050000 0.000000 0.050000 ( 0.049733) V8-normal: 0.870000 0.050000 0.920000 ( 0.885439) Ruby: 0.010000 0.000000 0.010000 ( 0.015064) #where n is 1000 

因此,我通过在JS端缓存来减少Ruby和JS之间的调用次数,但这并没有像我希望的那样优化它,因为至少有一个对象必须传递给函数: Hash或者至少一个JSON String ,我甚至走了一个通过Fixnum的长度 – 这让我感叹FML – 这不是一个很大的改进,而不是传递一个字符串(如果有的话)。

我仍然希望有一个比我更好更快的解决方案。

问题是默认情况下,Ruby Racer将字符串从Ruby 复制到V8,反之亦然。

在您的基准测试中,访问这6个字符串属性将导致至少6个memcpy()操作,这些操作必须分配新内存并逐字节地遍历字符串的长度以将其移动到新位置。 将它与Ruby方面进行比较,它基本上是一个无操作(字符串对象只包含一个已经分配和设置的指针),难怪它的速度要慢得多。

您可以更改此行为以通过引用而不是按值传递字符串。

 class Wrapper attr_reader :object def inititialize(object) @object = object end end cxt['aString'] = Wrapper.new('not copied') 

当然,如果你想在javascript中访问字符串,你必须最终支付副本。 您可以将此包装器技术用于Nums,数组和Hashes,默认情况下所有这些都将复制到JavaScript。

有关更多详细信息,请参阅https://github.com/cowboyd/therubyracer/wiki/Converting-ruby-object-to-javascript 。

V8确实支持外部管理字符串的概念,这允许您在Ruby中分配char * ,然后使用其来自V8的地址。 但是,此function目前在The Ruby Racer中不可用。

Interesting Posts