id
等于 1
的元素(id
指的是数组[1
]索引)。我知道我可以在
each
语句内部使用 if
语句,然后检查每个“当前”/“考虑”的元素是否满足 id == 1
。但由于数组中有很多数据,我希望找到另一种更高效的方法来完成相同的事情(避免每次运行 if
)。我该怎么做?
id
等于 1
的元素(id
指的是数组[1
]索引)。each
语句内部使用 if
语句,然后检查每个“当前”/“考虑”的元素是否满足 id == 1
。但由于数组中有很多数据,我希望找到另一种更高效的方法来完成相同的事情(避免每次运行 if
)。我们应该忘记小的效率问题,大约97%的时间都是这样:过早的优化是万恶之源。
现在,你可以做一些像这样的事情:
def f
do_something
end
f 0
for i in 2..n
f i
end
def f
yield 0
for i in 2..@n
yield i
end
end
f do |i|
do_something
end
但是您可能并不想执行这样的操作,如果确实需要执行,那么只有在确定其重要性之后才会执行。
最后,假设这个丑陋的技巧确实使您的服务器运行速度略微提高。这值得吗?
if id == 1
的性能非常好。除非你在后续发现实际的性能问题,否则不必过于担心,然后启动你的分析器。 - Jordan Runningif
语句是一种非常廉价的操作。您可以使用标准基准测试工具进行检查。
require "benchmark"
array = [1] * 100_000
Benchmark.bm do |bm|
bm.report "with if" do
array.each_with_index do |element, i|
next if i == 1
element - 1
end
end
bm.report "without if" do
array.each do |element|
element - 1
end
end
end
结果:
user system total real
with if 0.020000 0.000000 0.020000 ( 0.018115)
without if 0.010000 0.000000 0.010000 ( 0.012248)
在一个包含 100,000 个元素的数组中,这只是大约 0.006 秒的差异。除非它成为瓶颈,否则您不必担心它,而我怀疑它会成为瓶颈。
a = ['a', 'b', 'c']
a.each_with_index.reject {|el,i| i == 1}.each do |el,i|
# do whatever with the element
puts el
end
在编程中,使用 IMHO 而不是自己的显式 if
语句可能是更好的选择。然而我相信,这将导致与使用 if
相同甚至稍微低一些的性能。
如果按照其他人建议进行基准测试后,您知道它所需的时间肯定比您允许的要慢,并且选择是导致缓慢的原因,那么可以通过多种方式轻松修改以删除选择:
a = ['a', 'b', 'c']
n = 1
(a.first(n) + a.drop(n + 1)).each do |el|
# do whatever with the element
puts el
end
不幸的是,我认为这种方法比简单的if语句运行得更慢。我认为可能有潜力提高速度的方法是:
a = ['a', 'b', 'c']
n = 1
((0...n).to_a+((n+1)...a.size).to_a).map{|i| a[i]}.each do |el|
# do whatever with the element
puts el
end
但这样做的可能性很高会更慢。
编辑
基准测试在this gist中。这些结果实际上让我感到惊讶,拒绝选项是迄今为止最慢的选项,其次是范围。在根本不删除元素的情况下表现最好的方法是使用first
和drop
来选择周围的所有元素。
使用无选择作为基线的百分比结果:
with if 146%
with first and drop 104%
without if 100%
显然,这高度取决于您对元素的操作,这是使用Ruby可能执行的最快操作进行测试的。 操作越慢,它们之间的差异就越小。 像往常一样:基准测试,基准测试,基准测试
测试一个真正的for循环可能值得花费五分钟的时间。在Ruby圈子里可能会有所不同,但这并不意味着它从来没有价值。当您调用每个或映射或其他任何方法时,那些方法无论如何都使用for循环。避免绝对化。
这也取决于数组可以变得多大,在某些n下,其中一个可能比另一个更快。在这种情况下,绝对不值得。
如果您不需要特定元素,则可能不需要在数据库中存储该数据行。第1行和其余行之间有什么区别,换句话说,为什么要跳过它?具有id = 1的行始终具有相同的数据吗?如果是这样,将其存储为常量可能更好,并且会使您的问题无关紧要。性能几乎总是会占用更多的内存。
除非Rails 3采用不同的方式,您提取数据并使用id作为查找器键,id = 1将位于元素0中。
不幸的是,Knuth 的话经常被误解,并被用来为那些本应该避免写出的低效代码辩护,这些代码只要程序员足够受过教育并花费 5 秒钟思考就不会存在。当然,如果你不知道代码存在问题或者问题很小,花一周时间尝试加速它也是有问题的,但这更接近 Knuth 所说的内容。性能是计算机科学中最被误解和滥用的概念之一。