Что делает цикл while в Ruby быстрее других? - PullRequest
0 голосов
/ 23 января 2019

Я использую этот код:

require 'benchmark'

LOOPS = 100_000_000

def while_loop
        i = 0
        while i < LOOPS do i += 1 end
end


def times_loop
        i = 0
        LOOPS.times { i += 1 }
end

Benchmark.benchmark do |b|
        b.report('while') { while_loop }
        b.report('times') { times_loop }
end

Вывод (Ruby 2.6.0):

while  2.419529   0.000000   2.419529 (  2.426470)
times  7.225500   0.005673   7.231173 (  7.252794)

Почему while цикл быстрее, чем другие?

1 Ответ

0 голосов
/ 23 января 2019

Одна из причин в том, что times - это блок. И это вводит новую область видимости локальной переменной. И это создает какую-то локальную переменную, смотрите:

RubyVM::InstructionSequence.disasm(method(:times_loop)) возвращает

== disasm: #<ISeq:times_loop@1.rb:23 (23,0)-(26,3)>=====================
== catch table
| catch type: break  st: 0003 ed: 0014 sp: 0000 cont: 0014
== disasm: #<ISeq:block in times_loop@1.rb:25 (25,14)-(25,24)>==========
== catch table
| catch type: redo   st: 0001 ed: 0010 sp: 0000 cont: 0001
| catch type: next   st: 0001 ed: 0010 sp: 0000 cont: 0010
|------------------------------------------------------------------------
0000 nop                                                              (  25)[Bc]
0001 getlocal_OP__WC__1 i[Li]
0003 putobject_OP_INT2FIX_O_1_C_ 
0004 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0007 dup              
0008 setlocal_OP__WC__1 i
0010 leave            [Br]
|------------------------------------------------------------------------
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] i          
0000 putobject_OP_INT2FIX_O_0_C_                                      (  24)[LiCa]
0001 setlocal_OP__WC__0 i
0003 getinlinecache   10, <is:0>                                      (  25)[Li]
0006 getconstant      :LOOPS
0008 setinlinecache   <is:0>
0010 send             <callinfo!mid:times, argc:0>, <callcache>, block in times_loop
0014 leave                                                            (  26)[Re]

В этом случае есть две локальные переменные (setlocal_OP__WC__1 и setlocal_OP__WC__0).

В противоположном while используйте только один, RubyVM::InstructionSequence.disasm(method(:while_loop)) возвращает

== disasm: #<ISeq:while_loop@1.rb:15 (15,0)-(20,3)>=====================
== catch table
| catch type: break  st: 0009 ed: 0032 sp: 0000 cont: 0032
| catch type: next   st: 0009 ed: 0032 sp: 0000 cont: 0006
| catch type: redo   st: 0009 ed: 0032 sp: 0000 cont: 0009
|------------------------------------------------------------------------
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] i          
0000 putobject_OP_INT2FIX_O_0_C_                                      (  16)[LiCa]
0001 setlocal_OP__WC__0 i
0003 jump             17                                              (  17)[Li]
0005 putnil           
0006 pop              
0007 jump             17
0009 getlocal_OP__WC__0 i                                             (  18)[Li]
0011 putobject_OP_INT2FIX_O_1_C_ 
0012 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0015 setlocal_OP__WC__0 i
0017 getlocal_OP__WC__0 i                                             (  17)
0019 getinlinecache   26, <is:0>
0022 getconstant      :LOOPS
0024 setinlinecache   <is:0>
0026 opt_lt           <callinfo!mid:<, argc:1, ARGS_SIMPLE>, <callcache>
0029 branchif         9
0031 putnil           
0032 leave                                                            (  20)[Re]

Я думаю, что причина не единственная.

Но установка / получение новой локальной переменной замедляет операцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...