为什么 Ruby 方法调用特别慢(与其他语言相比)?
我正在尝试了解 Ruby 性能,并遇到了 this SO thread ,其中一个答案提到“方法调用是 Ruby 中最常见的操作之一,速度特别慢。”
另一个线程提到“它对方法进行“后期查找”,以实现灵活性。这它还必须记住每个上下文的名称才能进行评估,因此它的帧和方法调用速度较慢。”
有人可以更详细地解释为什么 Ruby 方法调用特别慢,并详细说明第二个线程吗?我不完全确定什么是延迟查找或为什么它很慢,而且我不知道每个上下文的名称意味着什么或它与框架和方法调用有何关系。
我(可能不正确)的理解是,由于可以在运行时添加或修改方法,Ruby解释器永远无法“记住”如何运行特定方法,因此它必须在程序运行时每次都查找该方法,而这就是方法调用缓慢的意思。但更正和更多技术性的解释会很棒。
I'm trying to read about Ruby performance, and came across this SO thread, where one of the answers mentions that "method calls, one of the most common operations in Ruby, are particularly slow."
Another thread mentions that "It does "late lookup" for methods, to allow for flexibility. This slows it down quite a bit. It also has to remember names per context to allow for eval, so its frames and method calls are slower."
Can someone explain in more detail why Ruby method calls are particularly slow, and elaborate on the second thread? I'm not totally sure what late lookup is or why it's slow, and I don't know what names per context means or how it relates to frames and method calls.
My (possibly incorrect) understanding is that since methods can be added or modified at runtime, the Ruby interpreter can never "remember" how to run a particular method, so it has to lookup the method every time while the program is running, and this is what is meant by method calls being slow. But corrections and more technical explanations would be great.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
编译语言通常具有快速的方法分派,因为调用代码知道类的 vtable 的索引,vtable 是方法指针的数组。经过几次指针取消引用后,调用代码就可以直接跳转到方法中。编译器创建 vtable,并将源代码中的每个方法名称替换为 vtable 中方法的数字索引。
Ruby 等动态语言的方法调度通常很慢,因为调用代码有方法的名称,而不是指针(也不是包含指针的数组的索引)。调用代码必须向对象询问其类,然后必须询问该类是否具有该名称的方法,如果没有,则继续沿着祖先链向上询问每个祖先是否具有该名称的方法(这就是编译器在编译语言中所做的事情,这就是编译速度慢而方法分派速度快的原因)。动态语言必须执行数十到数百条机器指令来搜索对象的类以及对象的所有祖先类以查找该方法,而不是仅花费一些机器指令来调用方法的一些指针取消引用。每个类都有一个名称的哈希表 ->方法,但具有字符串键的哈希表比具有整数索引的数组慢一个数量级。
当然,有一些方法可以优化动态语言中的方法调度。在 Ruby 中,这就是 JRuby、Rubinius 和 IronRuby 正在做的事情。但这是另一个问题的主题。
Compiled languages often have fast method dispatch because the calling code knows an index into the class' vtable, which is an array of method pointers. After just a few pointer dereferences, the calling code can jump right into the method. The compiler create the vtable, and replaces every method name in the source code with the numerical index of the method in the vtable.
Dynamic languages such as Ruby often have slow method dispatch because the calling code has a name for the method, not a pointer (nor an index into an array containing the pointers). The calling code has to ask the object for its class, then has to ask the class if it has a method by that name, and if not, go on up the chain of ancestors asking each ancestor if it has a method by that name (this is what the compiler does in a compiled language, which is why the compiling is slow and the method dispatch is fast). Rather than a few pointer dereferences costing just a few machine instructions to invoke a method, a dynamic language must execute dozens to hundreds of machine instructions to search the object's class and all the object's ancestor classes for the method. Each class has a HashTable of names -> methods, but HashTables with string keys are an order of magnitude slower than arrays with integer indexes.
There are ways to optimize method dispatch in dynamic langauges, of course. In Ruby, that's what JRuby, Rubinius, and IronRuby are working on. But that's a subject for another question.