Skip to content

Instantly share code, notes, and snippets.

@SamSaffron
Created April 27, 2016 22:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SamSaffron/d1a9cc8e141e7415e06306369fdedfe5 to your computer and use it in GitHub Desktop.
Save SamSaffron/d1a9cc8e141e7415e06306369fdedfe5 to your computer and use it in GitHub Desktop.
require 'benchmark/ips'
LONG_STRING = " this is a longer test
this is a longer test
this is a longer test
this is a longer test
this is a longer test"
class String
def original_blank?
/\A[[:space:]]*\z/ === self
end
def original_blank_with_empty?
empty? || (/\A[[:space:]]*\z/ === self)
end
def new_blank?
empty? || !(/[[:^space:]]/ === self)
end
end
Benchmark.ips do |x|
x.report('original blank no internal loop') do
/\A[[:space:]]*\z/ === LONG_STRING
end
x.report('original blank with empty no internal loop') do
LONG_STRING.empty? || (/\A[[:space:]]*\z/ === LONG_STRING)
end
x.report('new blank with empty no internal loop') do
LONG_STRING.empty? || !(/[[:^space:]]/ === LONG_STRING)
end
x.report('original blank times loop') do |t|
t.times do
/\A[[:space:]]*\z/ === LONG_STRING
end
end
x.report('original blank with empty times loop') do |t|
t.times do
LONG_STRING.empty? || (/\A[[:space:]]*\z/ === LONG_STRING)
end
end
x.report('new blank with empty times loop') do |t|
t.times do
LONG_STRING.empty? || !(/[[:^space:]]/ === LONG_STRING)
end
end
x.report('original blank while loop') do |t|
i = 0
while i < t
/\A[[:space:]]*\z/ === LONG_STRING
i += 1
end
end
x.report('original blank with empty while loop') do |t|
i = 0
while i < t
LONG_STRING.empty? || (/\A[[:space:]]*\z/ === LONG_STRING)
i += 1
end
end
x.report('new blank with empty while loop') do |t|
i = 0
while i < t
LONG_STRING.empty? || !(/[[:^space:]]/ === LONG_STRING)
i += 1
end
end
x.report('patched string original blank while loop') do |t|
i = 0
while i < t
LONG_STRING.original_blank?
i += 1
end
end
x.report('patched string original blank with empty while loop') do |t|
i = 0
while i < t
LONG_STRING.original_blank_with_empty?
i += 1
end
end
x.report('patched string new blank with while loop') do |t|
i = 0
while i < t
LONG_STRING.new_blank?
i += 1
end
end
x.compare!
end
@SamSaffron
Copy link
Author

SamSaffron commented Apr 27, 2016

Results:

-------------------------------------------------
original blank no internal loop
                          3.364M (± 2.2%) i/s -     16.822M
original blank with empty no internal loop
                          3.221M (± 2.2%) i/s -     16.112M
new blank with empty no internal loop
                          3.711M (± 3.0%) i/s -     18.558M
original blank times loop
                          3.809M (± 2.0%) i/s -     19.124M
original blank with empty times loop
                          3.656M (± 2.5%) i/s -     18.356M
new blank with empty times loop
                          4.293M (± 3.3%) i/s -     21.446M
original blank while loop
                          4.197M (± 2.3%) i/s -     21.109M
original blank with empty while loop
                          4.042M (± 3.9%) i/s -     20.273M
new blank with empty while loop
                          4.846M (± 2.0%) i/s -     24.284M
patched string original blank while loop
                          3.773M (± 2.1%) i/s -     18.954M
patched string original blank with empty while loop
                          3.645M (± 2.9%) i/s -     18.363M
patched string new blank with while loop
                          1.985M (± 5.0%) i/s -      9.971M

Comparison:
new blank with empty while loop:  4846442.9 i/s
new blank with empty times loop:  4293406.6 i/s - 1.13x slower
original blank while loop:  4197128.9 i/s - 1.15x slower
original blank with empty while loop:  4042239.5 i/s - 1.20x slower
original blank times loop:  3808940.4 i/s - 1.27x slower
patched string original blank while loop:  3773147.8 i/s - 1.28x slower
new blank with empty no internal loop:  3711448.5 i/s - 1.31x slower
original blank with empty times loop:  3656175.9 i/s - 1.33x slower
patched string original blank with empty while loop:  3645082.5 i/s - 1.33x slower
original blank no internal loop:  3363782.7 i/s - 1.44x slower
original blank with empty no internal loop:  3220765.0 i/s - 1.50x slower
patched string new blank with while loop:  1984885.6 i/s - 2.44x slower

@SamSaffron
Copy link
Author

So:

Fastest is code is:

  x.report('new blank with empty while loop') do |t|
    i = 0
    while i < t
      LONG_STRING.empty? || !(/[[:^space:]]/ === LONG_STRING)
      i += 1
    end
  end

BUT this is the slowest!

class String
  def original_blank?
    /\A[[:space:]]*\z/ === self
  end

  def original_blank_with_empty?
    empty? || (/\A[[:space:]]*\z/ === self)
  end

  def new_blank?
    empty? || !(/[[:^space:]]/ === self)
  end

end

 x.report('patched string new blank with while loop') do |t|
    i = 0
    while i < t
      LONG_STRING.new_blank?
      i += 1
    end
  end

@SamSaffron
Copy link
Author

reason appears to be that new regex allocates a lot more bytes than old regex

https://gist.github.com/SamSaffron/f73fd0395e050e927d1a3137373eeaee

@schneems
Copy link

It looks like the memory use is larger because a character is matched and a MatchData object is created, if you make the string empty LONG_STRING = " " then the old regex will generate and return a match which will allocate a MatchData. It will then be larger if you evaluate with MemoryProfiler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment