Last active
May 16, 2021 10:01
-
-
Save keymastervn/16d2e08d1c62d172dd830a3634e7a8cf to your computer and use it in GitHub Desktop.
Testing if rubocop `smart_path` is efficient in large number of offenses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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