To show the speed differences in some ruby applications, I wrote a simple ceaser cipher in a few different ways, then timed how long they took to run. I thought the results were interesting enough to post here.
Big thanks to Alan Skorkin for the simple timing code.
The first way creates a string of A-Z
, then add the lower case
letters to it, then split each character out, making an array. This
implementation isn’t a one-line array creation like the second.
The second way uses a string that’s already created with A-Za-z
, and
splits them into an array.
The third way uses a hash of a-z
, then creates the mappings for
A-Za-z
.
The fourth way uses a full hash of A-Za-z
.
Let’s post some graphs!
I removed the outliers from the four trials and made some graphs. First up, the average for 92 trials:
As you can see, it looks like the full hash implementation is the clear winner here.
Next up is the 92 trials plotted out:
Here’s the code if you would like to run your own analysis.
#!/usr/bin/env ruby
# There are lots of ways to do things in ruby. Some are faster than others.
# This ruby script outputs benchmarks in CSV format
# Thanks to Alan Skorkin for the simple timing code
# http://www.skorks.com/2010/03/timing-ruby-code-it-is-easy-with-benchmark/
require "benchmark"
print "HalfArray,FullArray,HalfHash,FullHash\n"
100.times do
# Half array manipulation
def ceaser_cipher(string)
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alphabet += alphabet.downcase
alphabet = alphabet.split(//)
converted_string_array = []
string.split(//).each do |l|
if alphabet.include?(l)
converted_string_array.push(alphabet[(alphabet.find_index(l) - 51).abs].swapcase)
else
converted_string_array.push(l)
end
end
return converted_string_array.join
end
time = Benchmark.realtime do
ceaser_cipher("ABC abc")
end
print (time * 1000)
print ","
# Full array manipulation
def ceaser_cipher(string)
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(//)
converted_string_array = []
string.split(//).each do |l|
if alphabet.include?(l)
converted_string_array.push(alphabet[(alphabet.find_index(l) - 51).abs].swapcase)
else
converted_string_array.push(l)
end
end
return converted_string_array.join
end
time = Benchmark.realtime do
ceaser_cipher("ABC abc")
end
print (time * 1000)
print ","
# Half hash dictionary
def ceaser_cipher(string)
initial_dictionary = Hash[
"a" => "z",
"b" => "y",
"c" => "x",
"d" => "w",
"e" => "v",
"f" => "u",
"g" => "t",
"h" => "s",
"i" => "r",
"j" => "q",
"k" => "p",
"l" => "o",
"m" => "n",
"n" => "m",
"o" => "l",
"p" => "k",
"q" => "j",
"r" => "i",
"s" => "h",
"t" => "g",
"u" => "f",
"v" => "e",
"w" => "d",
"x" => "c",
"y" => "b",
"z" => "a"
]
dictionary = {}
initial_dictionary.each do |original, reversed|
dictionary[original] = reversed
dictionary[original.upcase] = reversed.upcase
end
converted_string_array = []
string.split(//).each do |l|
if dictionary.include?(l)
converted_string_array.push(dictionary[l])
else
converted_string_array.push(l)
end
end
return converted_string_array.join
end
time = Benchmark.realtime do
ceaser_cipher("ABC abc")
end
print (time * 1000)
print ","
# Full hash dictionary
def ceaser_cipher(string)
dictionary = Hash[
"A" => "Z",
"B" => "Y",
"C" => "X",
"D" => "W",
"E" => "V",
"F" => "U",
"G" => "T",
"H" => "S",
"I" => "R",
"J" => "Q",
"K" => "P",
"L" => "O",
"M" => "N",
"N" => "M",
"O" => "L",
"P" => "K",
"Q" => "J",
"R" => "I",
"S" => "H",
"T" => "G",
"U" => "F",
"V" => "E",
"W" => "D",
"X" => "C",
"Y" => "B",
"Z" => "A",
"a" => "z",
"b" => "y",
"c" => "x",
"d" => "w",
"e" => "v",
"f" => "u",
"g" => "t",
"h" => "s",
"i" => "r",
"j" => "q",
"k" => "p",
"l" => "o",
"m" => "n",
"n" => "m",
"o" => "l",
"p" => "k",
"q" => "j",
"r" => "i",
"s" => "h",
"t" => "g",
"u" => "f",
"v" => "e",
"w" => "d",
"x" => "c",
"y" => "b",
"z" => "a"
]
converted_string_array = []
string.split(//).each do |l|
if dictionary.include?(l)
converted_string_array.push(dictionary[l])
else
converted_string_array.push(l)
end
end
return converted_string_array.join
end
time = Benchmark.realtime do
ceaser_cipher("ABC abc")
end
print (time * 1000)
print "\n"
end
I’m not entirely familiar with low low level ruby, so I can’t offer any grand insights, but I thought it was some decent code and a nice set of graphs.