fastruby / fast-ruby

:dash: Writing Fast Ruby :heart_eyes: -- Collect Common Ruby idioms.
https://github.com/fastruby/fast-ruby
5.67k stars 376 forks source link

!Array#empty? vs Array#any? #49

Closed edbond closed 2 years ago

edbond commented 9 years ago

Difference for 2.1.4 is much bigger.

ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-linux]

Calculating -------------------------------------
       !array.empty?   126.879k i/100ms
          array.any?    89.568k i/100ms
-------------------------------------------------
       !array.empty?      6.329M (± 3.6%) i/s -     31.593M
          array.any?      2.432M (± 1.5%) i/s -     12.181M

Comparison:
       !array.empty?:  6329175.1 i/s
          array.any?:  2432104.3 i/s - 2.60x slower
edbond commented 9 years ago
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]

Calculating -------------------------------------
       !Array#empty?   143.635k i/100ms
          Array#any?   134.023k i/100ms
-------------------------------------------------
       !Array#empty?      7.107M (± 1.9%) i/s -     35.621M
          Array#any?      6.760M (± 1.9%) i/s -     33.908M

Comparison:
       !Array#empty?:  7107124.9 i/s
          Array#any?:  6760372.4 i/s - 1.05x slower
Arcovion commented 9 years ago

The methods aren't equivalent, it's insane to compare them:

ARRAY = Array.new(100_000, false).push(true)

# Calculating -------------------------------------
#        !Array#empty?   206.932k i/100ms
#           Array#any?     2.062k i/100ms
# -------------------------------------------------
#        !Array#empty?      8.893M (± 4.2%) i/s -     44.490M
#           Array#any?     21.150k (± 1.3%) i/s -    107.224k

# Comparison:
#        !Array#empty?:  8893115.8 i/s
#           Array#any?:    21149.7 i/s - 420.48x slower

One checks if an array is empty, the other iterates an enumerable looking for a truthy value (or given a block, any truthy value the block returns).

IceDragon200 commented 9 years ago

Instead of Array#any? why not compare it with array.size == 0, that seems a bit more fair

edbond commented 9 years ago

@IceDragon200 #empty? is the same as #size == 0

empty?

static VALUE
rb_ary_empty_p(VALUE ary)
{
    if (RARRAY_LEN(ary) == 0)
        return Qtrue;
    return Qfalse;
}

size

static VALUE
rb_ary_length(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    return LONG2NUM(len);
}
IceDragon200 commented 9 years ago

@edbond What I was aiming for was the execution overhead in ruby for doing an Array#size + Integer#== vs using Array#empty?. Think about folks coming from JS who are used to [].length for checking if an array is empty

jbodah commented 9 years ago

FWIW, #any? vs ! + #empty? was big news to me and a couple coworkers when we heard it (moreover, the number of copies it makes was a bit eye-opening)

Arcovion commented 9 years ago
require 'benchmark/ips'

ARRAY = [*1..1000]

def fastest
  !ARRAY.empty?
end

def faster
  ARRAY.size != 0
end

def fast
  !ARRAY.size.zero?
end

def slow
  !!ARRAY.size.nonzero?
end

def slower
  ARRAY.any? { true }
end

def slowest
  ARRAY != []
end

p !ARRAY.size.zero?
p !ARRAY.empty?
p ARRAY.size != 0
p !!ARRAY.size.nonzero?
p ARRAY.any? { true }
p ARRAY != []

Benchmark.ips do |bm|
  bm.report('!ary.empty?') { fastest }
  bm.report('ary.size != 0') { faster }
  bm.report('!ary.size.zero?') { fast }
  bm.report('!!ary.size.nonzero?') { slow }
  bm.report('ary.any? { true }') { slower }
  bm.report('ary != []') { slowest }
  bm.compare!
end

On my machine:

true
true
true
true
true
true
Calculating -------------------------------------
         !ary.empty?   196.739k i/100ms
       ary.size != 0   188.360k i/100ms
     !ary.size.zero?   193.306k i/100ms
 !!ary.size.nonzero?   174.423k i/100ms
   ary.any? { true }   169.659k i/100ms
           ary != []   164.195k i/100ms
-------------------------------------------------
         !ary.empty?      8.719M (± 3.5%) i/s -     43.676M
       ary.size != 0      8.615M (± 1.8%) i/s -     43.134M
     !ary.size.zero?      8.127M (± 1.8%) i/s -     40.788M
 !!ary.size.nonzero?      6.521M (± 1.4%) i/s -     32.617M
   ary.any? { true }      5.428M (± 1.5%) i/s -     27.145M
           ary != []      5.423M (± 3.2%) i/s -     27.092M

Comparison:
         !ary.empty?:  8719246.6 i/s
       ary.size != 0:  8614787.7 i/s - 1.01x slower
     !ary.size.zero?:  8126586.6 i/s - 1.07x slower
 !!ary.size.nonzero?:  6520599.1 i/s - 1.34x slower
   ary.any? { true }:  5428154.2 i/s - 1.61x slower
           ary != []:  5423254.4 i/s - 1.61x slower

But I don't think it's worth comparing !ary.empty? to ary.size != 0 as no one would pick the latter. Same goes for ary.any? which is not an obvious choice anyway.

winston commented 8 years ago

Hi. Pardon me as I am helping @JuanitoFatas to clear the PRs.

I would agree with @Arcovion in this case and it seems strange to make this comparison. :kissing_heart: