Ruby if语句优化重构最佳实践
我在这里遇到了一个非常常见的重构情况,在浏览了一些博客之后,我仍然没有得到任何令人满意的评论;所以在这里问一个问题Ruby if语句优化重构最佳实践,ruby,performance,optimization,refactoring,ruby-hash,Ruby,Performance,Optimization,Refactoring,Ruby Hash,我在这里遇到了一个非常常见的重构情况,在浏览了一些博客之后,我仍然没有得到任何令人满意的评论;所以在这里问一个问题 h = { a: 'a', b: 'b' } new_hash = {} new_hash[:a] = h[:a].upcase if h[:a].present? 据我的朋友说,这段代码可以通过以下方式进行重构以提高性能 a = h[:a] new_hash[:a] = a.upcase if a.present? 乍一看,它看起来有点优化。但是,这是一种会带来很大不
h = {
a: 'a',
b: 'b'
}
new_hash = {}
new_hash[:a] = h[:a].upcase if h[:a].present?
据我的朋友说,这段代码可以通过以下方式进行重构以提高性能
a = h[:a]
new_hash[:a] = a.upcase if a.present?
乍一看,它看起来有点优化。但是,这是一种会带来很大不同的东西,还是一种过度优化?应该选择哪种风格
寻求专家建议:)
使用基准n=1000更新
user system total real
hash lookup 0.000000 0.000000 0.000000 ( 0.000014)
new var 0.000000 0.000000 0.000000 ( 0.000005)
AND op 0.000000 0.000000 0.000000 ( 0.000018)
try 0.000000 0.000000 0.000000 ( 0.000046)
Calculating -------------------------------------
hash lookup 40.000 memsize ( 40.000 retained)
1.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
new var 0.000 memsize ( 0.000 retained)
0.000 objects ( 0.000 retained)
0.000 strings ( 0.000 retained)
AND op 40.000 memsize ( 40.000 retained)
1.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
try 200.000 memsize ( 40.000 retained)
5.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
使用gem基准内存更新内存基准
user system total real
hash lookup 0.000000 0.000000 0.000000 ( 0.000014)
new var 0.000000 0.000000 0.000000 ( 0.000005)
AND op 0.000000 0.000000 0.000000 ( 0.000018)
try 0.000000 0.000000 0.000000 ( 0.000046)
Calculating -------------------------------------
hash lookup 40.000 memsize ( 40.000 retained)
1.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
new var 0.000 memsize ( 0.000 retained)
0.000 objects ( 0.000 retained)
0.000 strings ( 0.000 retained)
AND op 40.000 memsize ( 40.000 retained)
1.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
try 200.000 memsize ( 40.000 retained)
5.000 objects ( 1.000 retained)
1.000 strings ( 1.000 retained)
优化是不一样的,有内存优化,性能优化,还有可读性和代码的结构 性能:对速度和性能几乎没有任何影响,因为哈希是在O(1)中访问的。尝试使用
benchmark
看看自己几乎没有什么不同
内存:你朋友的代码不如你的优化,因为他初始化了另一个对象a
,而你的没有
可读性和风格:乍一看,您朋友的代码似乎行数更少,更具描述性。但是请记住,您可能需要对散列中的每个键/值执行此操作,因此您可能需要有
a
,b
,并且它会随着散列的进行而继续(当散列继续进行时,当然最好在散列上迭代)。这里没有太多要看的内容。优化穿着不同的鞋,有内存优化,性能优化,还有可读性和代码的结构
性能:对速度和性能几乎没有任何影响,因为哈希是在O(1)中访问的。尝试使用benchmark
看看自己几乎没有什么不同
内存:你朋友的代码不如你的优化,因为他初始化了另一个对象a
,而你的没有
可读性和风格:乍一看,您朋友的代码似乎行数更少,更具描述性。但是请记住,您可能需要对散列中的每个键/值执行此操作,因此您可能需要有
a
,b
,并且它会随着散列的进行而继续(当散列继续进行时,当然最好在散列上迭代)。根据您的具体情况,这里没有太多内容可看,rails方法(如present?
)可能会很脏,并且肯定会影响性能。如果您只关心nil
检查,而不关心空Array
或空String
之类的事情,那么使用纯ruby方法将“快得多”(引号是为了强调性能在这个基本示例中完全无关紧要的事实)
因为我们在做基准测试
设置
h = {
a: 'a',
b: 'b'
}
class Object
def present?
!blank?
end
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
end
def hash_lookup(h)
new_hash = {}
new_hash[:a] = h[:a].upcase if h[:a].present?
new_hash
end
def new_var(h)
new_hash = {}
a = h[:a]
new_hash[:a] = a.upcase if a.present?
new_hash
end
def hash_lookup_w_safe_nav(h)
new_hash = {}
new_hash[:a] = h[:a]&.upcase
new_hash
end
def hash_lookup_wo_rails(h)
new_hash = {}
new_hash[:a] = h[:a].upcase if h[:a]
new_hash
end
def new_var_wo_rails(h)
new_hash = {}
a = h[:a]
new_hash[:a] = a.upcase if a
new_hash
end
基准
N = [1_000,10_000,100_000]
require 'benchmark'
N.each do |n|
puts "OVER #{n} ITERATIONS"
Benchmark.bm do |x|
x.report(:new_var) { n.times {new_var(h)}}
x.report(:hash_lookup) { n.times {hash_lookup(h)}}
x.report(:hash_lookup_w_safe_nav) { n.times {hash_lookup_w_safe_nav(h)}}
x.report(:hash_lookup_wo_rails) { n.times {hash_lookup_wo_rails(h)}}
x.report(:new_var_wo_rails) { n.times {new_var_wo_rails(h)}}
end
end
输出
OVER 1000 ITERATIONS
user system total real
new_var 0.001075 0.000159 0.001234 ( 0.001231)
hash_lookup 0.002441 0.000000 0.002441 ( 0.002505)
hash_lookup_w_safe_nav 0.001077 0.000000 0.001077 ( 0.001077)
hash_lookup_wo_rails 0.001100 0.000000 0.001100 ( 0.001145)
new_var_wo_rails 0.001015 0.000000 0.001015 ( 0.001016)
OVER 10000 ITERATIONS
user system total real
new_var 0.010321 0.000000 0.010321 ( 0.010329)
hash_lookup 0.010104 0.000015 0.010119 ( 0.010123)
hash_lookup_w_safe_nav 0.007211 0.000000 0.007211 ( 0.007213)
hash_lookup_wo_rails 0.007508 0.000000 0.007508 ( 0.017302)
new_var_wo_rails 0.008186 0.000026 0.008212 ( 0.016679)
OVER 100000 ITERATIONS
user system total real
new_var 0.099400 0.000249 0.099649 ( 0.192481)
hash_lookup 0.101419 0.000009 0.101428 ( 0.199788)
hash_lookup_w_safe_nav 0.078156 0.000010 0.078166 ( 0.140796)
hash_lookup_wo_rails 0.078743 0.000000 0.078743 ( 0.166815)
new_var_wo_rails 0.073271 0.000000 0.073271 ( 0.125869)
根据您的具体情况,rails方法(如
present?
)可能不干净,并且肯定会影响性能。如果您只关心nil
检查,而不关心空Array
或空String
之类的事情,那么使用纯ruby方法将“快得多”(引号是为了强调性能在这个基本示例中完全无关紧要的事实)
因为我们在做基准测试
设置
h = {
a: 'a',
b: 'b'
}
class Object
def present?
!blank?
end
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
end
def hash_lookup(h)
new_hash = {}
new_hash[:a] = h[:a].upcase if h[:a].present?
new_hash
end
def new_var(h)
new_hash = {}
a = h[:a]
new_hash[:a] = a.upcase if a.present?
new_hash
end
def hash_lookup_w_safe_nav(h)
new_hash = {}
new_hash[:a] = h[:a]&.upcase
new_hash
end
def hash_lookup_wo_rails(h)
new_hash = {}
new_hash[:a] = h[:a].upcase if h[:a]
new_hash
end
def new_var_wo_rails(h)
new_hash = {}
a = h[:a]
new_hash[:a] = a.upcase if a
new_hash
end
基准
N = [1_000,10_000,100_000]
require 'benchmark'
N.each do |n|
puts "OVER #{n} ITERATIONS"
Benchmark.bm do |x|
x.report(:new_var) { n.times {new_var(h)}}
x.report(:hash_lookup) { n.times {hash_lookup(h)}}
x.report(:hash_lookup_w_safe_nav) { n.times {hash_lookup_w_safe_nav(h)}}
x.report(:hash_lookup_wo_rails) { n.times {hash_lookup_wo_rails(h)}}
x.report(:new_var_wo_rails) { n.times {new_var_wo_rails(h)}}
end
end
输出
OVER 1000 ITERATIONS
user system total real
new_var 0.001075 0.000159 0.001234 ( 0.001231)
hash_lookup 0.002441 0.000000 0.002441 ( 0.002505)
hash_lookup_w_safe_nav 0.001077 0.000000 0.001077 ( 0.001077)
hash_lookup_wo_rails 0.001100 0.000000 0.001100 ( 0.001145)
new_var_wo_rails 0.001015 0.000000 0.001015 ( 0.001016)
OVER 10000 ITERATIONS
user system total real
new_var 0.010321 0.000000 0.010321 ( 0.010329)
hash_lookup 0.010104 0.000015 0.010119 ( 0.010123)
hash_lookup_w_safe_nav 0.007211 0.000000 0.007211 ( 0.007213)
hash_lookup_wo_rails 0.007508 0.000000 0.007508 ( 0.017302)
new_var_wo_rails 0.008186 0.000026 0.008212 ( 0.016679)
OVER 100000 ITERATIONS
user system total real
new_var 0.099400 0.000249 0.099649 ( 0.192481)
hash_lookup 0.101419 0.000009 0.101428 ( 0.199788)
hash_lookup_w_safe_nav 0.078156 0.000010 0.078166 ( 0.140796)
hash_lookup_wo_rails 0.078743 0.000000 0.078743 ( 0.166815)
new_var_wo_rails 0.073271 0.000000 0.073271 ( 0.125869)
取决于你打算做什么。多了解一些情况肯定会有帮助。如果你对变异
h
没问题,你可以只做h[:a]&&h[:a].upcase
或h[:a]。尝试(:upcase!)
用第二种方法回答有关性能和优化的问题,最好围绕它进行一些基准测试。您正在通过存储在变量中来补偿哈希查找。@kiddorailsupcase
或upcase
两者的作用与我使用另一个散列存储其值的方式相同。还尝试使用try
方法进行基准测试。他们不会做与upcase相同的事情
可以改变h
中的值,也可以返回nil
。e、 g.'A'.upcase!#=>如果直接更改h[:a]
,并且通过关联h
,则无需使用nil
@kiddorails'点新的\u散列。这种方法在技术上会更有效,尽管是无限的。@engineersmnkynew\u hash
对我来说很重要。这取决于你打算做什么。多了解一些情况肯定会有帮助。如果你对变异h
没问题,你可以只做h[:a]&&h[:a].upcase
或h[:a]。尝试(:upcase!)
用第二种方法回答有关性能和优化的问题,最好围绕它进行一些基准测试。您正在通过存储在变量中来补偿哈希查找。@kiddorailsupcase
或upcase
两者的作用与我使用另一个散列存储其值的方式相同。还尝试使用try
方法进行基准测试。他们不会做与upcase相同的事情
可以改变h
中的值,也可以返回nil
。e、 g.'A'.upcase!#=>如果直接更改h[:a]
,并且通过关联h
,则无需使用nil
@kiddorails'点新的\u散列。这种方法在技术上会更有效,尽管是无限的。@engineersmnkynew\u hash
在我的例子中很重要。我已经用1000个计数的基准更新了这个问题。分配新变量似乎太快了。内存基准测试也是如此