Is iteration slower than linear code? Which one is

2019-09-21 17:32发布

I have a question in my mind from last many days, that while writing a code in ruby, is the linear code is faster and preferable than an iteration?

Let me have an example. There is a block of code for same functionality written in two different ways:

Way 1:

['dog', 'cat', 'tiger'].each do |pet_name|
  puts "I have many pets, one of them is #{pet_name}."
end

Way 2:

puts "I have many pets, one of them is dog."
puts "I have many pets, one of them is cat."
puts "I have many pets, one of them is tiger."

So, I want to know which one is better and preferable? As per my view, I think 2nd one will take less time and memory. But I want to confirm.

6条回答
Summer. ? 凉城
2楼-- · 2019-09-21 17:49

Strictly speaking, yes, there is an overhead involved in iteration. There will be in any language you use (although some use compiler tricks to reduce this). The first version of your code will run a negligible amount quicker due to the cost of iteration, and of building the array you've defined. Also, the string building will probably add another tiny slice of cost.

That being said, it's such a tiny difference you'd have to be very picky to notice, or even care about any of this. It's negligible. Try benchmarking it yourself, you won't notice a significant difference.

I'm not prepared to write 10,000 lines like that for some method, are you? I find that iteration looks a lot cleaner, especially for non-trivial code, and is often preferable in terms of readability and clean code. Not to mention it's more DRY.

查看更多
三岁会撩人
3楼-- · 2019-09-21 17:51

Sequential logic is faster (see the benchmark below the fold), but it almost never matters. The clearer and more maintainable code should always be selected. Only a demonstrated need should cause one to cease optimizing for the programmer and begin optimizing for the machine. By demonstrated, I mean measured--you ran it and found it to be too slow.

The second example violates the DRY (Don't Repeat Yourself) principle and is a minor maintenance problem.


require 'benchmark'

LOOPS = 100000

Benchmark.bm(10) do |x|
  x.report('iteration') do
    LOOPS.times do
      ['dog', 'cat', 'tiger'].each do |pet_name|
        "I have many pets, one of them is #{pet_name}."
      end
    end
  end
  x.report('sequence') do
    LOOPS.times do
      "I have many pets, one of them is dog."
      "I have many pets, one of them is cat."
      "I have many pets, one of them is tiger."
    end
  end
end

# =>                  user     system      total        real
# => iteration    0.200000   0.000000   0.200000 (  0.202054)
# => sequence     0.010000   0.000000   0.010000 (  0.012195)
查看更多
一纸荒年 Trace。
4楼-- · 2019-09-21 18:01

There is always cost for calling a function, creating an array or creating loop.. However this is what programming languages were built for, so to answer your question: yes, the second code would be faster, nanoseconds maybe. But the first code is more general, you never know when you will buy a new pet. It's more useful, maybe someone will give you list of their pets, and you will want to talk about them? Generally speaking well - second code is unnoticeably faster, but first is better and preferable.

查看更多
一夜七次
5楼-- · 2019-09-21 18:02

I realize that your example is very simplified and unlikely to happen in real world, but taking it literally:

The first example will create intermediate objects (strings and array), so you may say that in fact it will take more memory. However these object will be garbage-collected later, so you'll get your memory back. (This would not be the case if you defined array of symbols, as symbols are not garbage-collected).

It is also faster, because it doesn't need to fetch the objects internally from the array during each iteration. But the difference if obviously unnoticeable and should not be taken into account. What should be taken into account is readability here.

If you were a performance freak that you should probably define your methods without parentheses around the arguments as well, because this would result in smaller parse tree created by Ruby interpreter.

# slower
def meth(arg)
end

# faster
def meth arg
end

But considering it a valid reason would be silly of course.

Edit: if you're looking for a good Ruby style guide check this: https://github.com/bbatsov/ruby-style-guide

查看更多
beautiful°
6楼-- · 2019-09-21 18:03

If one of those two options were clearly better and therefore preferable, the language wouldn't provide both options. As always, it depends on the circumstances. Questions you should be asking to decide include

  • which solution is more readable? (For habitual Ruby programmers, the first one is.) The more readable, the better.
  • which solution is faster? (You can only decide this by measuring. Take care to use a realistic example - the time difference for only three animals may not even be measurable reliably.) The faster, the better.
  • how much duplication is introduced by "unrolling" the more idiomatic version? (In this case, not very much - the puts and part of the string literal.) The less duplication you would introduce, the better.

As you see, the answers contradict each other, so you have to understand the context of the decision you're making and weigh the factors correctly to find out which is best overall.

查看更多
萌系小妹纸
7楼-- · 2019-09-21 18:04

In both cases, the time spent running the actual Ruby code is going to be completely dominated by the time it takes to print the text to the screen. Remember: console output is slow. Really slow. Painfully slow.

Since in both cases the code prints the same amount of text (and, in fact, the same text) to the screen, any miniscule performance differences that might or might not exist are going to be lost in the noise.

I think 2nd one will take less time and memory.

Don't think. Look.

Here's a crazy idea: if you want to know which one runs faster, run them and see which one runs faster!

查看更多
登录 后发表回答