{"action":"created","issue":{"url":"https://api.github.com/repos/rspec/rspec-rails/issues/1308","labels_url":"https://api.github.com/repos/rspec/rspec-rails/issues/1308/labels{/name}","comments_url":"https://api.github.com/repos/rspec/rspec-rails/issues/1308/comments","events_url":"https://api.github.com/repos/rspec/rspec-rails/issues/1308/events","html_url":"https://github.com/rspec/rspec-rails/pull/1308","id":57595747,"number":1308,"title":"Replace excess &block in arguments to yield construction","user":{"login":"davydovanton","id":1147484,"avatar_url":"https://avatars.githubusercontent.com/u/1147484?v=3","gravatar_id":"","url":"https://api.github.com/users/davydovanton","html_url":"https://github.com/davydovanton","followers_url":"https://api.github.com/users/davydovanton/followers","following_url":"https://api.github.com/users/davydovanton/following{/other_user}","gists_url":"https://api.github.com/users/davydovanton/gists{/gist_id}","starred_url":"https://api.github.com/users/davydovanton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davydovanton/subscriptions","organizations_url":"https://api.github.com/users/davydovanton/orgs","repos_url":"https://api.github.com/users/davydovanton/repos","events_url":"https://api.github.com/users/davydovanton/events{/privacy}","received_events_url":"https://api.github.com/users/davydovanton/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-02-13T12:58:25Z","updated_at":"2015-02-15T19:01:03Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/rspec/rspec-rails/pulls/1308","html_url":"https://github.com/rspec/rspec-rails/pull/1308","diff_url":"https://github.com/rspec/rspec-rails/pull/1308.diff","patch_url":"https://github.com/rspec/rspec-rails/pull/1308.patch"},"body":""},"comment":{"url":"https://api.github.com/repos/rspec/rspec-rails/issues/comments/74431155","html_url":"https://github.com/rspec/rspec-rails/pull/1308#issuecomment-74431155","issue_url":"https://api.github.com/repos/rspec/rspec-rails/issues/1308","id":74431155,"user":{"login":"cupakromer","id":1524496,"avatar_url":"https://avatars.githubusercontent.com/u/1524496?v=3","gravatar_id":"","url":"https://api.github.com/users/cupakromer","html_url":"https://github.com/cupakromer","followers_url":"https://api.github.com/users/cupakromer/followers","following_url":"https://api.github.com/users/cupakromer/following{/other_user}","gists_url":"https://api.github.com/users/cupakromer/gists{/gist_id}","starred_url":"https://api.github.com/users/cupakromer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cupakromer/subscriptions","organizations_url":"https://api.github.com/users/cupakromer/orgs","repos_url":"https://api.github.com/users/cupakromer/repos","events_url":"https://api.github.com/users/cupakromer/events{/privacy}","received_events_url":"https://api.github.com/users/cupakromer/received_events","type":"User","site_admin":false},"created_at":"2015-02-15T19:01:03Z","updated_at":"2015-02-15T19:01:03Z","body":"Thanks for the extra benchmark. As I was reviewing it I realized that it's not actually measuring the change since the block isn't actually invoked on the call to `route`. However, that got me thinking at which point I remembered, rspec-core has already done [many benchmarks about this](https://github.com/rspec/rspec-core/tree/master/benchmarks):\r\n\r\n- [call vs yield](https://github.com/rspec/rspec-core/blob/master/benchmarks/call_v_yield.rb)\r\n- [capture block vs yield](https://github.com/rspec/rspec-core/blob/master/benchmarks/capture_block_vs_yield.rb)\r\n- [flat map vs inject](https://github.com/rspec/rspec-core/blob/master/benchmarks/flat_map_vs_inject.rb)\r\n\r\nThe results are very interesting:\r\n\r\n> This benchmark demonstrates that capturing a block (e.g. `&block`) has\r\n> a high constant cost, taking about 5x longer than a single `yield`\r\n> (even if the block is never used!).\r\n> \r\n> However, fowarding a captured block can be faster than using `yield`\r\n> if the block is used many times (the breakeven point is at about 20-25\r\n> invocations), so it appears that he per-invocation cost of `yield`\r\n> is higher than that of a captured-and-forwarded block.\r\n> \r\n> Note that there is no circumstance where using `block.call` is faster.\r\n> \r\n> See also `flat_map_vs_inject.rb`, which appears to contradict these\r\n> results a little bit.\r\n>\r\n> -- https://github.com/rspec/rspec-core/blob/master/benchmarks/capture_block_vs_yield.rb#L83-L95\r\n\r\nand\r\n\r\n> Surprisingly, `flat_map(&block)` appears to be faster than\r\n> `flat_map { yield }` in spite of the fact that our array here\r\n> is smaller than the break-even point of 20-25 measured in the\r\n> `capture_block_vs_yield.rb` benchmark. In fact, the forwaded-block\r\n> version remains faster in my benchmarks here no matter how small\r\n> I shrink the `words` array. I'm not sure why!\r\n>\r\n> -- https://github.com/rspec/rspec-core/blob/master/benchmarks/flat_map_vs_inject.rb#L37-L42\r\n\r\nGiven this information and how `before` is implemented I wrote this modified benchmark:\r\n\r\n```ruby\r\nrequire 'benchmark/ips'\r\n\r\ndef before_n_times(n, &block)\r\n  n.times { instance_exec(&block) }\r\nend\r\n\r\ndef yield_n_times(n)\r\n  before_n_times(n) { yield }\r\nend\r\n\r\ndef capture_block_and_yield_n_times(n, &block)\r\n  before_n_times(n) { yield }\r\nend\r\n\r\ndef capture_block_and_call_n_times(n, &block)\r\n  before_n_times(n) { block.call }\r\nend\r\n\r\n[10, 25, 50, 100, 1000, 10000].each do |count|\r\n  puts \"\\n\\nInvoking the block #{count} times\\n\"\r\n\r\n  Benchmark.ips do |x|\r\n    x.report(\"Yield #{count} times                  \") do\r\n      yield_n_times(count) { }\r\n    end\r\n\r\n    x.report(\"Capture block and yield #{count} times\") do\r\n      capture_block_and_yield_n_times(count) { }\r\n    end\r\n\r\n    x.report(\"Capture block and call #{count} times \") do\r\n      capture_block_and_call_n_times(count) { }\r\n    end\r\n  end\r\nend\r\n```\r\n\r\nMy results using Ruby 2.2.0:\r\n\r\n```\r\nInvoking the block 10 times\r\nCalculating -------------------------------------\r\nYield 10 times                  \r\n                        13.127k i/100ms\r\nCapture block and yield 10 times\r\n                        12.975k i/100ms\r\nCapture block and call 10 times \r\n                        11.524k i/100ms\r\n-------------------------------------------------\r\nYield 10 times                  \r\n                        165.030k (± 5.7%) i/s -    827.001k\r\nCapture block and yield 10 times\r\n                        163.866k (± 5.9%) i/s -    817.425k\r\nCapture block and call 10 times \r\n                        137.892k (± 7.3%) i/s -    691.440k\r\n\r\n\r\nInvoking the block 25 times\r\nCalculating -------------------------------------\r\nYield 25 times                  \r\n                         7.305k i/100ms\r\nCapture block and yield 25 times\r\n                         7.314k i/100ms\r\nCapture block and call 25 times \r\n                         6.047k i/100ms\r\n-------------------------------------------------\r\nYield 25 times                  \r\n                         84.167k (± 5.6%) i/s -    423.690k\r\nCapture block and yield 25 times\r\n                         82.110k (± 6.4%) i/s -    409.584k\r\nCapture block and call 25 times \r\n                         67.144k (± 6.2%) i/s -    338.632k\r\n\r\n\r\nInvoking the block 50 times\r\nCalculating -------------------------------------\r\nYield 50 times                  \r\n                         4.211k i/100ms\r\nCapture block and yield 50 times\r\n                         4.181k i/100ms\r\nCapture block and call 50 times \r\n                         3.410k i/100ms\r\n-------------------------------------------------\r\nYield 50 times                  \r\n                         45.223k (± 5.0%) i/s -    227.394k\r\nCapture block and yield 50 times\r\n                         45.253k (± 4.9%) i/s -    225.774k\r\nCapture block and call 50 times \r\n                         36.181k (± 5.7%) i/s -    180.730k\r\n\r\n\r\nInvoking the block 100 times\r\nCalculating -------------------------------------\r\nYield 100 times                  \r\n                         2.356k i/100ms\r\nCapture block and yield 100 times\r\n                         2.305k i/100ms\r\nCapture block and call 100 times \r\n                         1.842k i/100ms\r\n-------------------------------------------------\r\nYield 100 times                  \r\n                         23.677k (± 7.1%) i/s -    117.800k\r\nCapture block and yield 100 times\r\n                         24.039k (± 4.7%) i/s -    122.165k\r\nCapture block and call 100 times \r\n                         18.888k (± 6.6%) i/s -     95.784k\r\n\r\n\r\nInvoking the block 1000 times\r\nCalculating -------------------------------------\r\nYield 1000 times                  \r\n                       244.000  i/100ms\r\nCapture block and yield 1000 times\r\n                       245.000  i/100ms\r\nCapture block and call 1000 times \r\n                       192.000  i/100ms\r\n-------------------------------------------------\r\nYield 1000 times                  \r\n                          2.540k (± 4.3%) i/s -     12.688k\r\nCapture block and yield 1000 times\r\n                          2.499k (± 5.6%) i/s -     12.495k\r\nCapture block and call 1000 times \r\n                          1.975k (± 5.1%) i/s -      9.984k\r\n\r\n\r\nInvoking the block 10000 times\r\nCalculating -------------------------------------\r\nYield 10000 times                  \r\n                        24.000  i/100ms\r\nCapture block and yield 10000 times\r\n                        24.000  i/100ms\r\nCapture block and call 10000 times \r\n                        19.000  i/100ms\r\n-------------------------------------------------\r\nYield 10000 times                  \r\n                        232.923  (±15.5%) i/s -      1.128k\r\nCapture block and yield 10000 times\r\n                        212.504  (±21.6%) i/s -    936.000 \r\nCapture block and call 10000 times \r\n                        184.090  (±10.3%) i/s -    912.000 \r\n```\r\n\r\nThis seems to show that the error margin is enough to negate any benefit from capturing the block initially. It also shows that not capturing the block is still faster at low rates of calling. If this holds for your system, I think this PR is good as is and we won't need to capture the block in the `route` method, but still use `yield`."}}