Rubocop: Style/NumericPredicate -> defaults to slower code

Created on 16 Oct 2016  路  5Comments  路  Source: rubocop-hq/rubocop

The default value (predicate) of the Style/NumericPredicate cop, produces slower code.
My opinion is that rubocop should default to the faster method, with the option of having the other layout.

Tested with Benchmark-ips & ruby 2.0, 2.1, 2.2, 2.3, 2.4dev

RuboCop::Cop::Style::NumericPredicate
  Benchmark
Warming up --------------------------------------
                == 0    94.390k i/100ms
               zero?    80.343k i/100ms
Calculating -------------------------------------
                == 0      2.498M (卤 4.7%) i/s -     12.459M in   5.002912s
               zero?      1.804M (卤 8.5%) i/s -      8.918M in   5.010642s

Comparison:
                == 0:  2498260.4 i/s
               zero?:  1803547.9 i/s - 1.39x  slower

    "== 0" is faster
Warming up --------------------------------------
                != 0    64.694k i/100ms
            nonzero?    49.316k i/100ms
Calculating -------------------------------------
                != 0      1.949M (卤18.9%) i/s -      9.187M in   5.051024s
            nonzero?    895.233k (卤26.2%) i/s -      4.093M in   5.066035s

Comparison:
                != 0:  1949177.3 i/s
            nonzero?:   895233.2 i/s - 2.18x  slower

    != 0" is faster
Warming up --------------------------------------
                 > 0    83.342k i/100ms
           positive?    73.005k i/100ms
Calculating -------------------------------------
                 > 0      2.629M (卤 3.3%) i/s -     13.168M in   5.014540s
           positive?      1.567M (卤 1.7%) i/s -      7.885M in   5.032639s

Comparison:
                 > 0:  2629417.0 i/s
           positive?:  1567175.2 i/s - 1.68x  slower

    "> 0" is faster
Warming up --------------------------------------
                 < 0    91.912k i/100ms
           negative?    82.470k i/100ms
Calculating -------------------------------------
                 < 0      2.328M (卤 1.2%) i/s -     11.673M in   5.015670s
           negative?      1.952M (卤 0.5%) i/s -      9.814M in   5.027162s

Comparison:
                 < 0:  2327595.1 i/s
           negative?:  1952235.9 i/s - 1.19x  slower

    "< 0" is faster

Test code example:

def self.slow_zero
  0.zero?
  1.zero?
  -1.zero?
  9558.zero?
  -9558.zero?
  1565156.zero?
  -1565156.zero?
end

def self.fast_zero
  0 == 0
  1 == 0
  -1 == 0
  9558 == 0
  -9558 == 0
  1565156 == 0
  -1565156 == 0
end

Most helpful comment

I would have to disagree with this. This is a style cop, not a performance cop. Robustness and readability (in this case readability is about the same) should be the guiding force for the defaults, not speculative micro-optimizations based on the current Ruby implementation.

Language level optimizations are a nebulous thing, potentially leading to us having different defaults per Ruby version, because of changes to the internals. (We recently removed a performance cop because the performance difference had been nuked somewhere around MRI 2.2.)

If someone does millions of comparisons to zero and, after proper, in vivo benchmarks, find that this has become a bottleneck, they can configure the cop or disable it for this block.

All 5 comments

But is the performance difference significant? It looks like they are both very fast.

The result above has an average from about 62% slower.

But also the least result (1.19x slower) means that roughly i can do 5 times a < 0 and in the same time do 4 times a #negative?

I would have to disagree with this. This is a style cop, not a performance cop. Robustness and readability (in this case readability is about the same) should be the guiding force for the defaults, not speculative micro-optimizations based on the current Ruby implementation.

Language level optimizations are a nebulous thing, potentially leading to us having different defaults per Ruby version, because of changes to the internals. (We recently removed a performance cop because the performance difference had been nuked somewhere around MRI 2.2.)

If someone does millions of comparisons to zero and, after proper, in vivo benchmarks, find that this has become a bottleneck, they can configure the cop or disable it for this block.

I agree with @Drenmi.

One comment I still have: @Drenmi sad "Language level optimizations" -> It is slower on ruby 2.0, 2.1, 2.2, 2.3 and 2.4

https://travis-ci.org/dennisvandehoef/rubocop-speed/builds/167869584

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mikegee picture mikegee  路  3Comments

bbugh picture bbugh  路  3Comments

mlammers picture mlammers  路  3Comments

bbatsov picture bbatsov  路  3Comments

david942j picture david942j  路  3Comments