Showing posts with label optimization. Show all posts
Showing posts with label optimization. Show all posts

Wednesday, July 22, 2015

Check if array contains any element of another array

I wanted to check if there were common elements in two separate arrays. Seems simple enough, but I was trying to find a way to do this without looping over both arrays in some kind each jungle:

wizard_of_oz_characters = ['tin man', 'scarecrow', 'lion']
batman_villains = ['scarecrow', 'joker', 'bane']
wizard_of_oz_characters.each do |character|
  batman_villains.each do |villain|
    return false if character == villain
  end
end

I was able to pare it down to something simpler:

wizard_of_oz_characters.any? {|character| batman_villains.include?(character) }

…but at it’s core, it’s still looping through and checking each record in sequence. Not what I was after.

Then I found a StackOverflow answer which had exactly what I wanted:

(wizard_of_oz_characters & batman_villains).empty?

This returns the intersection of the two arrays (['scarecrow']), then asks if it is empty. Perfect! And the syntax is kinda fun too.

But what is it actually doing? One of the comments on the SO answer said this is faster because it’s linear rather than quadratic, and although it has been a few years since high school Geometry, I still know quadratic is much more intensive than linear. However, I wanted to find out more.

Turns out, the & operator is treating the Arrays like Sets, an entirely different data type in Ruby. From the Ruby docs themselves, a Set is a “hybrid of Array’s intuitive inter-operation facilities and Hash’s fast lookup.”

So you get the syntax of an Array with the speed of a Hash. And you get this fun and terse syntax. I’m on board with that.

Wednesday, June 24, 2015

Take control of your rspec test suite, Part 1

Let me tell you a little story about how I took our test suite from 25 minutes run time to just under 3 minutes while still testing all the same functionality.

A large application can generate a very unwieldy test suite. On one project I worked on, the test suite took 45 minutes to run on our CI, and when I tried to run the whole thing locally, I stopped it at the 1 1/2 hour mark when it looked like it still had a ways to go.

I can live with a suite like that for awhile, because I just run the tests that are specific to the work I’m doing. However, there are times when I’m working on something that could potentially affect several different places in the app, and I may not know exactly where those places are, so I have to run the whole suite. Waiting 45 minutes to be able to finish my feature is NOT cool, especially since that will likely have to happened a few times to ensure all tests are fixed.

On that project, I was limited with what I could do on the test suite due to budget constraints. However, on another project I worked on, our test suite took 25 minutes to run, but the project manager saw the financial benefit of trimming this number down and so let me spend some time fixing our tests.

I’ll be writing a few different posts on this topic. Step 1 is the easiest part: gathering data. Often, only a handful of tests take the longest to run, so we want to find out which of our tests are “losers” and try to refactor them into “winners.”

Thankfully, rspec gives us a command to do just that:

rspec --profile
# or rspec -p

This will run the entire suite, then list out the 10 examples that took the longest to run. If you want more than 10, you can specify the number you want as an argument, i.e. rspec -p 25.

You can also use this when running on certain test groups. So, if you wanted to find out the 17 slowest feature specs:

rspec -p 17 spec/features

Now you know the worst offenders. Stay tuned for part 2 where we delve into how to fix them up.