The same task, measured

The task

The task is deliberately boring: add up the squares of the numbers from one to five million. The same loop, the same arithmetic, no library differences to argue about. Both programs print the total and the seconds they took.

Ruby

require "benchmark" time = Benchmark.realtime do sum = 0 1.upto(5_000_000) { |i| sum += i * i } puts sum end puts "#{time.round(3)}s"

ruby bench.rb

41666679166667500000
1.051s

Python

import time start = time.perf_counter() total = 0 for i in range(1, 5_000_001): total += i * i print(total) print(f"{time.perf_counter() - start:.3f}s")

python3 bench.py

41666679166667500000
1.806s

The same total, because both add the same integers. Ruby took about 1.05 seconds, Python about 1.81, on the same machine in the same minute.

What the numbers say

A single loop over integers favours CRuby here: the arithmetic and the block are compiled to bytecode and run without the per-iteration object work that CPython does for every integer. Change the task and the result can change with it: string work, regular expressions, JSON and numeric libraries have their own implementations in each language.

Both languages are fast enough for the scripts this course builds, and both have a profiler for the case where they are not: lesson 160 for Ruby, cProfile for Python. The useful habit is the same in both: measure, find the hot function, then change one thing.

What to watch for

Do not move a service between languages because of a micro-benchmark. The cost of a service is usually the database, the network and the deploy, and those do not change with the interpreter.

When you do compare, say what you measured: the version, the machine, the task and the repetition count. A number without those four is an opinion.

Next: Capstone: todo CLI

Article author: Arthur Isaev