From e85ef212de98c817154ab62ae2d03508c512107e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 11 Jan 2023 10:08:52 +0100 Subject: [PATCH] [ruby/set] Avoid the `block or return` pattern to save Proc allocations Using the block param in a boolean context like this cause it to be allocated. Using it with an `if` or `unless` was optimized in 3.2 (https://github.com/ruby/ruby/pull/6286) but using it with `or` or `and` wasn't. ```ruby def foo(&block) block or return 1 end puts RubyVM::InstructionSequence.of(method(:foo)).disasm == disasm: # (catch: false) local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: 0, kw: -1@-1, kwrest: -1]) [ 1] block@0 0000 getblockparam block@0, 0 ( 12)[LiCa] 0003 dup 0004 branchif 10 0006 pop 0007 putobject_INT2FIX_1_ 0008 leave [Re] 0009 putnil 0010 leave ``` versus ``` def foo(&block) return 1 if block end puts RubyVM::InstructionSequence.of(method(:foo)).disasm == disasm: # (catch: false) local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: 0, kw: -1@-1, kwrest: -1]) [ 1] block@0 0000 getblockparamproxy block@0, 0 ( 16)[LiCa] 0003 branchunless 7 0005 putobject_INT2FIX_1_ 0006 leave ( 17)[Re] 0007 putnil ( 16) 0008 leave ``` https://github.com/ruby/set/commit/e89da977d4 --- lib/set.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/set.rb b/lib/set.rb index df1e68d081..a515dacb0e 100644 --- a/lib/set.rb +++ b/lib/set.rb @@ -507,7 +507,7 @@ class Set # the element as parameter. Returns an enumerator if no block is # given. def each(&block) - block or return enum_for(__method__) { size } + block_given? or return enum_for(__method__) { size } @hash.each_key(&block) self end @@ -582,7 +582,7 @@ class Set # Equivalent to Set#delete_if, but returns nil if no changes were # made. Returns an enumerator if no block is given. def reject!(&block) - block or return enum_for(__method__) { size } + block_given? or return enum_for(__method__) { size } n = size delete_if(&block) self if size != n @@ -591,7 +591,7 @@ class Set # Equivalent to Set#keep_if, but returns nil if no changes were # made. Returns an enumerator if no block is given. def select!(&block) - block or return enum_for(__method__) { size } + block_given? or return enum_for(__method__) { size } n = size keep_if(&block) self if size != n