Skip to content

Commit

Permalink
Merge pull request #18 from nashbridges/performance-improvements
Browse files Browse the repository at this point in the history
Avoid creating intermediate arrays when possible, it hurt performance
  • Loading branch information
grosser committed Nov 23, 2016
2 parents 9e3d979 + 5549fcd commit 3450d95
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ gemspec
gem "rspec"
gem "rake"
gem "bump"

group :benchmark do
gem "benchmark-ips"
end
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ PATH
GEM
remote: https://rubygems.org/
specs:
benchmark-ips (2.5.0)
bump (0.5.2)
diff-lcs (1.2.5)
rake (10.4.2)
Expand All @@ -29,6 +30,7 @@ PLATFORMS
ruby

DEPENDENCIES
benchmark-ips
bump
rake
rspec
Expand Down
96 changes: 96 additions & 0 deletions benchmarks/less_arrays.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
require "bundler/setup"
require "benchmark/ips"

require 'unicode_utils/compatibility_decomposition'
require 'unicode_utils/general_category'

module SortAlphabetical
extend self

def sort(set)
set.sort_by do |item|
if block_given?
item = yield(item).to_s
else
item = item.to_s
end
[normalize(item), item] # when both á and a are present, sort them a, á
end
end

def normalize(string)
UnicodeUtils.compatibility_decomposition(string).split(''.freeze).select do |c|
UnicodeUtils.general_category(c) =~ /Letter|Separator|Punctuation|Number/
end.join
end
end

module SortAlphabeticalEachChar
extend self

def sort(set)
set.sort_by do |item|
if block_given?
item = yield(item).to_s
else
item = item.to_s
end
[normalize(item), item] # when both á and a are present, sort them a, á
end
end

def normalize(string)
result = String.new
UnicodeUtils.compatibility_decomposition(string).each_char do |c|
result << c if UnicodeUtils.general_category(c) =~ /Letter|Separator|Punctuation|Number/
end
result
end
end

module SortAlphabeticalEachWithObject
extend self

def sort(set)
set.sort_by do |item|
if block_given?
item = yield(item).to_s
else
item = item.to_s
end
[normalize(item), item] # when both á and a are present, sort them a, á
end
end

def normalize(string)
UnicodeUtils.compatibility_decomposition(string).each_char.each_with_object(String.new) do |c, result|
result << c if UnicodeUtils.general_category(c) =~ /Letter|Separator|Punctuation|Number/
end
end
end

sample = %w[b a á ä o ó x ö í i c]

Benchmark.ips do |x|
x.report("regular") { SortAlphabetical.sort(sample) }
x.report("each_char") { SortAlphabeticalEachChar.sort(sample) }
x.report("each_with_object") { SortAlphabeticalEachWithObject.sort(sample) }

x.compare!
end

=begin
Warming up --------------------------------------
regular 662.000 i/100ms
each_char 1.247k i/100ms
each_with_object 1.070k i/100ms
Calculating -------------------------------------
regular 6.816k (± 3.1%) i/s - 34.424k
each_char 12.762k (± 2.5%) i/s - 64.844k
each_with_object 10.842k (± 2.7%) i/s - 54.570k
Comparison:
each_char: 12762.0 i/s
each_with_object: 10842.4 i/s - 1.18x slower
regular: 6816.2 i/s - 1.87x slower
=end
10 changes: 7 additions & 3 deletions lib/sort_alphabetical.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ def sort(set)
end

def normalize(string)
UnicodeUtils.compatibility_decomposition(string).split(''.freeze).select do |c|
UnicodeUtils.general_category(c) =~ /Letter|Separator|Punctuation|Number/
end.join
result = String.new
UnicodeUtils.compatibility_decomposition(string).each_char do |c|
if UnicodeUtils.general_category(c) =~ /Letter|Separator|Punctuation|Number/
result << c
end
end
result
end
end

0 comments on commit 3450d95

Please sign in to comment.