在 Ruby 的方法中声明一个变量和直接使用它在性能上有什么区别吗?

Is there any performance difference between declaring a variable and using it directly in method in Ruby?

如果我要调用函数,可以说打印函数如下:

def print(message)
   //... 
end

有什么区别:

print("Message")

message = "Message"
print(message)

我很好奇,因为 ruby 是一种解释型语言,所以我不知道这是否会对内存管理或其他方面产生影响。

尽管 Ruby 被广泛认为是一种解释性语言,但 Ruby 在该语言的最常见实现中是部分编译的。标准解释器 (YARV) 首先将您的源代码编译成字节码,然后解释该字节码。 (如果您想更深入地了解这个主题,我强烈推荐 Ruby under a Microscope)。

您可以使用 RubyVM::InstructionSequence 查看为特定 Ruby 代码生成的字节码。让我们比较两个示例的字节码:

irb(main):002:0> puts RubyVM::InstructionSequence.compile("print('Message')").dis
asm

== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,16)> (catch: FALSE)
0000 putself                                                          (   1)[Li]
0001 putstring                    "Message"
0003 opt_send_without_block       <callinfo!mid:print, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0006 leave
=> nil


irb(main):003:0> puts RubyVM::InstructionSequence.compile("message = 'Message'; print(message)").disasm
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,35)> (catch: FALSE)
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] message@0
0000 putstring                    "Message"                           (   1)[Li]
0002 setlocal_WC_0                message@0
0004 putself
0005 getlocal_WC_0                message@0
0007 opt_send_without_block       <callinfo!mid:print, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0010 leave
=> nil

正如我们在这里看到的,print('Message') 的字节码指令较少,因此可能 非常快 更快(编写代码时速度不足以牺牲清晰度)。此外,print('Message') 不使用任何局部变量,因此该代码段不需要局部变量 table。

你的message = 'Message'; print(message)例子有更多的字节码指令,因为它需要设置和获取局部变量,随后还需要一个局部变量table(显示为local table (size: 1, ...))。这意味着它会占用更多的内存。

尽管有两种不同的方法,但您总体上仍在创建相同的对象 - 一个字符串 'Message'。因此,对垃圾收集器的影响很小,正如我们从 GC.stat 的值中看到的那样:(a.rb 不使用本地而 b.rb 使用)

$ ruby a.rb                                                                                                        
Message{:count=>12, :heap_allocated_pages=>51, :heap_sorted_length=>62, :heap_allocatable_pages=>11, :heap_available_slots=>20791, :heap_live_slots=>20530, :heap_free_slots=>261, :heap_final_slots=>0, :heap_marked_slots=>15224, :heap_eden_pages=>51, :heap_tomb_pages=>0, :total_allocated_pages=>51, :total_freed_pages=>0, :total_allocated_objects=>68286, :total_freed_objects=>47756, :malloc_increase_bytes=>374152, :malloc_increase_bytes_limit=>16777216, :minor_gc_count=>9, :major_gc_count=>3, :remembered_wb_unprotected_objects=>213, :remembered_wb_unprotected_objects_limit=>424, :old_objects=>14957, :old_objects_limit=>29920, :oldmalloc_increase_bytes=>431824, :oldmalloc_increase_bytes_limit=>16777216}%

$ ruby b.rb                                                                                                        
Message{:count=>12, :heap_allocated_pages=>51, :heap_sorted_length=>62, :heap_allocatable_pages=>11, :heap_available_slots=>20785, :heap_live_slots=>20518, :heap_free_slots=>267, :heap_final_slots=>0, :heap_marked_slots=>15209, :heap_eden_pages=>51, :heap_tomb_pages=>0, :total_allocated_pages=>51, :total_freed_pages=>0, :total_allocated_objects=>68288, :total_freed_objects=>47770, :malloc_increase_bytes=>374488, :malloc_increase_bytes_limit=>16777216, :minor_gc_count=>9, :major_gc_count=>3, :remembered_wb_unprotected_objects=>213, :remembered_wb_unprotected_objects_limit=>424, :old_objects=>14956, :old_objects_limit=>29918, :oldmalloc_increase_bytes=>434104, :oldmalloc_increase_bytes_limit=>16777216}

:count键显示垃圾收集器有运行的次数,你的两个例子都是一样的,所以它并没有真正影响垃圾收集。