Skip to content

Instantly share code, notes, and snippets.

@keymastervn
Last active May 16, 2021 10:01
Show Gist options
  • Save keymastervn/16d2e08d1c62d172dd830a3634e7a8cf to your computer and use it in GitHub Desktop.
Save keymastervn/16d2e08d1c62d172dd830a3634e7a8cf to your computer and use it in GitHub Desktop.
Testing if rubocop `smart_path` is efficient in large number of offenses
require 'benchmark/ips'
def dir_no_cache
Dir.pwd
end
def dir_cache
@dir_cache ||= Dir.pwd
end
Benchmark.ips do |x|
x.report('dir_no_cache') { send :dir_no_cache }
x.report('dir_cache') { send :dir_cache }
x.compare!
end
module RuboCop
# Common methods and behaviors for dealing with paths.
module PathUtil
module_function
def relative_path(path, base_dir = Dir.pwd)
# Optimization for the common case where path begins with the base
# dir. Just cut off the first part.
if path.start_with?(base_dir)
base_dir_length = base_dir.length
result_length = path.length - base_dir_length - 1
return path[base_dir_length + 1, result_length]
end
path_name = Pathname.new(File.expand_path(path))
begin
path_name.relative_path_from(Pathname.new(base_dir)).to_s
rescue ArgumentError
path
end
end
puts '------------------------------------8<----------------------------------'
puts 'original'
def smart_path(path)
# Ideally, we calculate this relative to the project root.
base_dir = Dir.pwd
if path.start_with? base_dir
relative_path(path, base_dir)
else
path
end
end
puts '------------------------------------8<----------------------------------'
puts 'patched'
PWD = Dir.pwd
def smart_path_new(path)
# Ideally, we calculate this relative to the project root.
base_dir = PWD
if path.start_with? base_dir
relative_path(path, base_dir)
else
path
end
end
puts '------------------------------------8<----------------------------------'
def match_path?(pattern, path)
case pattern
when String
File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB) ||
hidden_file_in_not_hidden_dir?(pattern, path)
when Regexp
begin
pattern.match?(path)
rescue ArgumentError => e
return false if e.message.start_with?('invalid byte sequence')
raise e
end
end
end
# Returns true for an absolute Unix or Windows path.
def absolute?(path)
%r{\A([A-Z]:)?/}i.match?(path)
end
def hidden_file_in_not_hidden_dir?(pattern, path)
hidden_file?(path) &&
File.fnmatch?(
pattern, path,
File::FNM_PATHNAME | File::FNM_EXTGLOB | File::FNM_DOTMATCH
) &&
!hidden_dir?(path)
end
def hidden_file?(path)
maybe_hidden_file?(path) && File.basename(path).start_with?('.')
end
# Loose check to reduce memory allocations
def maybe_hidden_file?(path)
separator_index = path.rindex(File::SEPARATOR)
return false unless separator_index
dot_index = path.index('.', separator_index + 1)
dot_index == separator_index + 1
end
def hidden_dir?(path)
File.dirname(path).split(File::SEPARATOR).any? { |dir| dir.start_with?('.') }
end
end
end
class A
include RuboCop::PathUtil
def initialize(file_path)
@file_path = file_path
end
def bm_smart_path
smart_path(@file_path)
end
def bm_smart_path_new
smart_path_new(@file_path)
end
end
# assume that you put this benchmark file in /tmp as well, because this is the common case
sample_file_in_tmp_dir = '/tmp/powerlog/today.txt'
# not common
sample_out_tmp_dir = '/Users/datle-eh/ossprojects'
first_sampling = A.new(sample_file_in_tmp_dir)
second_sampling = A.new(sample_out_tmp_dir)
Benchmark.ips do |x|
x.time = 10
x.warmup = 2
x.report('rubocop_smart_path') { first_sampling.bm_smart_path }
x.report('rubocop_smart_path_new') { first_sampling.bm_smart_path_new }
x.report('rubocop_smart_path_2nd') { second_sampling.bm_smart_path }
x.report('rubocop_smart_path_new_2nd') { second_sampling.bm_smart_path_new }
x.compare!
end
# ➜ /tmp ruby -v bm_dir_with_cache.rb
# ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin19]
# Warming up --------------------------------------
# dir_no_cache 7.089k i/100ms
# dir_cache 1.039M i/100ms
# Calculating -------------------------------------
# dir_no_cache 74.091k (± 1.9%) i/s - 375.717k in 5.072876s
# dir_cache 10.367M (± 1.0%) i/s - 51.960M in 5.012283s
# Comparison:
# dir_cache: 10367481.7 i/s
# dir_no_cache: 74090.9 i/s - 139.93x (± 0.00) slower
# ------------------------------------8<----------------------------------
# original
# ------------------------------------8<----------------------------------
# patched
# ------------------------------------8<----------------------------------
# Warming up --------------------------------------
# rubocop_smart_path 7.302k i/100ms
# rubocop_smart_path_new
# 929.127k i/100ms
# rubocop_smart_path_2nd
# 7.394k i/100ms
# rubocop_smart_path_new_2nd
# 891.144k i/100ms
# Calculating -------------------------------------
# rubocop_smart_path 73.170k (± 2.3%) i/s - 737.502k in 10.084911s
# rubocop_smart_path_new
# 9.277M (± 1.1%) i/s - 92.913M in 10.016738s
# rubocop_smart_path_2nd
# 70.741k (± 5.2%) i/s - 709.824k in 10.063347s
# rubocop_smart_path_new_2nd
# 8.871M (± 1.2%) i/s - 89.114M in 10.046973s
# Comparison:
# rubocop_smart_path_new: 9276859.4 i/s
# rubocop_smart_path_new_2nd: 8871142.6 i/s - 1.05x (± 0.00) slower
# rubocop_smart_path: 73170.3 i/s - 126.78x (± 0.00) slower
# rubocop_smart_path_2nd: 70741.0 i/s - 131.14x (± 0.00) slower
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment