We’ll start our profiling session by writing a test and a benchmark. Why? Because optimization means making changes to the code, most often to its core part. Sometimes, optimization requires complex architectural changes. And we should be able to make substantial changes without fearing we’ll break things. This is why we need the test. We’ll use the test to make sure we didn’t break anything during optimization. Benchmarking is important because, as you’ll see later, the speedup in the profiler won’t always translate to the same improvement in the real world. So we need to measure our code’s performance before and after optimization to ensure that things got faster.
We’ll embed the test directly into the application and add the --test command-line option to run the test suite. Without the option, the program will run the profiler as before.
With --benchmark option we’ll run our code outside the profiler and measure and print the execution time.
chp4/app.rb | |
| # ... |
| if ARGV[0] == "--test" |
| |
| ARGV.clear |
| require 'minitest/autorun' |
| |
| class AppTest < MiniTest::Unit::TestCase |
| |
| def setup |
| @parsed_data = parse_data(generate_test_data) |
| end |
| |
| def test_parsing |
| assert_equal @parsed_data.length, 50000 |
| assert @parsed_data.all? do |row| |
| row.length == 3 && row[0].class == Fixnum && row[2].class == Date |
| end |
| end |
| |
| def test_find_youngest |
| youngest = find_youngest(@parsed_data) |
| assert @parsed_data.all? { |row| youngest >= row } |
| end |
| |
| end |
| |
| exit(0) |
| elsif ARGV[0] == "--benchmark" |
| |
| require 'benchmark' |
| |
| data = generate_test_data |
| result = Benchmark.realtime do |
| people = parse_data(data) |
| find_youngest(people) |
| end |
| puts "%5.3f" % result |
| |
| exit(0) |
| else |
| data = generate_test_data |
| GC.disable |
| result = RubyProf.profile do |
| people = parse_data(data) |
| find_youngest(people) |
| end |
| end |
| # ... |
The first test_parsing test checks that we got the right number of columns and rows after parsing and that the data types were recognized correctly. The second test_youngest checks that we indeed found the latest birth date from the parsed data.
Let’s run the test and make sure it passes.
| $ bundle exec ruby app.rb --test |
| # Running tests: |
| .. |
| Finished tests in 5.427569s, 0.3685 tests/s, 0.5527 assertions/s. |
| 2 tests, 3 assertions, 0 failures, 0 errors, 0 skips |
Let’s also benchmark the application before optimization.
| $ bundle exec ruby app.rb --benchmark |
| 0.937 |
13.58.5.57