Assert performance with Rspec benchmark
In the world of Ruby development it’s accepted practice to test your code, but only a few people do performance testing. Such tests help to prevent the program work from slowing down while the amount of data grows, and they also help to understand whether the code performance has become slower after the recent code amendments.
To start writing such tests, you need to add gem 'rspec-benchmark'
https://github.com/piotrmurach/rspec-benchmark
require 'rspec-benchmark'
RSpec.configure do |config|
config.include RSpec::Benchmark::Matchers
end
def linear_method(size)
a = []
size.times { a << nil }
end
def quadratic_method(size)
a = []
(size*size).times { a << nil }
end
describe 'Performance' do
context '#linear_method' do
it 'works under 1 ms' do
expect {
linear_method(500_000)
}.to perform_under(1).ms.warmup(2).times.sample(10).times
end
it 'works faster than 1000 ips' do
expect {
linear_method(500_000)
}.to perform_at_least(1000).within(1).warmup(0.2).ips
end
it 'performs linear' do
expect { |n, _i| linear_method(n) }.to perform_linear.in_range(10, 10_000)
end
end
context '#quadratic_method' do
it 'performs power' do
expect { |n, i| quadratic_method(n) }.to perform_power.in_range(10, 10_000)
end
it 'peforms slower than linear' do
expect { quadratic_method(10_000) }.to perform_slower_than { linear_method(10_000) }
end
end
end
I’ve created two methods to check the tests’ work: linear_method(size) и quadratic_method(size).
As you can see in the graph, the amount of data doesn’t affect the complexity of the linear function O(n) unlike the quadratic O(n2).
it 'works under 1 ms' do
expect {
linear_method(10_000)
}.to perform_under(1).ms.warmup(2).times.sample(10).times
end
This test checks that our method is executed in 1 ms with a data volume of 10_000, as each time you run the test, the data might differ slightly, you can set up the test run, for example, 10 times to get averaged data and run the test 2 times to warm up the code.
it 'works faster than 1000 ips' do
expect {
linear_method(500_000)
}.to perform_at_least(1000).within(0.2).warmup(warmup_seconds).ips
end
In addition to checking the execution time you can check for the number of operations per second (ips) and limit the test execution time up to 0.2 seconds.
it 'performs linear' do
expect { |n, _i| linear_method(n) }.to perform_linear.in_range(10, 10_000)
end
Here is the testing of the method for its linearity O(n) in the range of 10-10000.
it 'performs power' do
expect { |n, _i| quadratic_method(n) }.to perform_power.in_range(10, 10_000)
end
Checking exponentiality of the method, in this case it is the quadratic complexity O(n2).
it 'peforms slower than linear' do
expect { quadratic_method(500_000) }.to perform_slower_than { linear_method(500_000) }
end
Also, it’s possible to compare each execution test speed.
I’ve told you only about a small part of the functionality of rspec-benchmark, this gem contains many other matchers and it’s possible to check how many objects were created.
PS: there are tools that help you to profile the code to ensure your assert performance tests always show green, for example, ruby-prof, but we will discuss it some other time.
Happy testing!