Running with Ruby

Tag: Performance (page 2 of 5)

Empty? vs blank? vs any? – why you should not use any? to test if there’s anything in the array

I often work with junior programmers and somehow they tend to use syntax like this:

return unless array_data.any?
# or
do something if array_data.any?

And there’s almost nothing wrong with it, as long as you don’t work with huge arrays.

Here’s a difference in speed between empty?, blank? and any? with different array sizes (worse scenario):

worse-scenario

Why any? is so slow with bigger arrays? Well, basically because it iterates through the whole the collection and checks if there’s at least one non-false and non-nil element (when no block given). Of course it will stop with the first non-false value, but we were pessimistic and we assumed that there are no elements like that. Many developers assume that any? answers the: “is there anything in the array” question, when it really answers: “is there at least one non-false and non-nil element in the array”. And this can make huge difference with bigger arrays.

Of course if we decide to have a true-like value in a random place of an array, things look a bit different:

randomMore or less it looks randomly, but you can see a general pattern – the more elements there are, the longer it can get.

Conclusion: Using any? to determine, if there’s anything in the array can have a huge impact on your code performance if you work with bigger arrays. And even if you don’t, having bad habits is not a good idea.

Ruby hash initializing – why do you think you have a hash, but you have an array

We all use hashes and it seems, that there’s nothing special about them. They are as dictionaries in Python, or any other similar structures available for multiple different languages. And that’s more or less true. Although Ruby hashes are really special, because from time to time they really are… arrays.

You won’t see that clearly until you either look into Ruby source code, or you benchmark it. Here is code and and results of my benchmark:

Benchmark

require 'benchmark'

GC.disable

ar = nil

100.times do |steps|
  d = Benchmark.measure {
    100000.times { ar = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 } }
  }

  system("echo '#{ar.count}, #{d.real}' >> #{ar.count}.csv")
end

Benchmark results

hasharray

There’s a huge disproportion between 6 and 7 elements. And that’s the point where a Ruby “hashy array” becomes a real hash. To explain this, I will quote funny-falcon:

Investigation shows, that typical rails application allocates tons of small hashes. Up to 40% of whole allocated hashes never grows bigger than 1 element size.

That’s the primary reason of this patch. Small arrays are faster and use less memory than hashes. If you want to see the code, here’s a pull request with code that optimize arrays up to 6 elements.

Olderposts Newerposts

Copyright © 2017 Running with Ruby

Theme by Anders NorenUp ↑