Ruby Map / Уменьшить функцию обязательно эффективны? - PullRequest
3 голосов
/ 07 октября 2011
b1 = Time.now
puts (1..100000).inject(0) { |x, y| x + y }
a1 = Time.now
puts "Time for inject: #{a1 - b1}"

b2 = Time.now
sum = 0
(1..100000).each do |value|
    sum += value
end
puts sum
a2 = Time.now
puts "Time for each: #{a2 - b2}"

Приведенный выше код Ruby сравнивает два способа суммирования целых чисел. К моему удивлению, более элегантный подход инъекция или уменьшение превосходит другой. Почему это так? Почему люди пытаются использовать неэффективные инъекции или сокращения ? Просто потому что это элегантно?

PS: Спасибо за все вдохновляющие ответы. Я хотел спросить, что происходит за кулисами, что приводит к различиям.

Ответы [ 6 ]

6 голосов
/ 07 октября 2011

Я бы пошел с небольшой математикой в ​​этом случае:

require "benchmark"

N = 5_000_000

Benchmark.bmbm do |bm|
  bm.report "inject 1" do
    (1..N).inject(0) { |x, y| x + y }
  end

  bm.report "inject 2" do
    (1..N).inject(:+)
  end

  bm.report "each" do
    sum = 0
    (1..N).each do |value|
      sum += value
    end
  end

  bm.report "sum of finite arithmetic progression" do
    ((1 + N) * N) / 2
  end
end

И результат:

% ruby sum.rb
Rehearsal ------------------------------------------------------------------------
inject 1                               0.500000   0.000000   0.500000 (  0.507497)
inject 2                               0.320000   0.000000   0.320000 (  0.322675)
each                                   0.370000   0.000000   0.370000 (  0.380504)
sum of finite arithmetic progression   0.000000   0.000000   0.000000 (  0.000005)
--------------------------------------------------------------- total: 1.190000sec

                                           user     system      total        real
inject 1                               0.500000   0.000000   0.500000 (  0.507697)
inject 2                               0.320000   0.000000   0.320000 (  0.322323)
each                                   0.370000   0.000000   0.370000 (  0.380307)
sum of finite arithmetic progression   0.000000   0.000000   0.000000 (  0.000004)
% 

Лучшая математика всегда быстрее:)

5 голосов
/ 07 октября 2011

Да, читаемость кода важнее микрооптимизаций. Разница едва заметна, даже если взять сумму миллионов элементов. Кроме того, оба метода O(n), поэтому ни один из них не будет значительно превосходить другой по мере увеличения количества элементов.

Как уже отмечали другие, inject(:+) еще немного быстрее. Даже если это не так, выберите тот, который проще для глаз, и не беспокойтесь о крошечных различиях в производительности. Вероятно, это не будет узким местом в вашем приложении.

require "benchmark"

N = 5_000_000

Benchmark.bmbm do |bm|
  bm.report "inject 1" do
    (1..N).inject(0) { |x, y| x + y }
  end

  bm.report "inject 2" do
    (1..N).inject(:+)
  end

  bm.report "each" do
    sum = 0
    (1..N).each do |value|
      sum += value
    end
  end
end

Результаты:

               user     system      total        real
inject 1   0.610000   0.000000   0.610000 (  0.613080)
inject 2   0.370000   0.000000   0.370000 (  0.370892)
each       0.570000   0.000000   0.570000 (  0.568266)
3 голосов
/ 07 октября 2011

@ сумасшедший прав.Я рекомендую вам использовать модуль тестирования в следующий раз, как:

#!/usr/bin/env ruby

require "benchmark"

Benchmark.bm do |x|
  x.report { (1..10000000).inject(:+) }
  x.report { sum = 0; (1..10000000).each { |value| sum += value } }
end
3 голосов
/ 07 октября 2011

Попробуйте вместо этого:

puts (1..100000).inject(:+)

Лично я иду на элегантность, если одна строка ввода может заменить 3 строки каждая до тех пор, пока она не станет грязной, я бы пошла с инъекцией.

2 голосов
/ 07 октября 2011

Интересно отметить, что большинство или все предыдущие ответы, вероятно, предполагали последнюю основную версию ruby ​​(1.9).В 1.8.7 эта разница более выражена:

$ ruby -v
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10.6.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03
$ ruby bench.rb 

Rehearsal ------------------------------------------------------------------------
inject 1                               3.910000   0.010000   3.920000 (  3.932388)
inject 2                               0.660000   0.000000   0.660000 (  0.662330)
each                                   1.120000   0.010000   1.130000 (  1.126276)
sum of finite arithmetic progression   0.000000   0.000000   0.000000 (  0.000009)
--------------------------------------------------------------- total: 5.710000sec

                                           user     system      total        real
inject 1                               3.930000   0.010000   3.940000 (  3.956084)
inject 2                               0.680000   0.000000   0.680000 (  0.685073)
each                                   1.110000   0.000000   1.110000 (  1.109675)
sum of finite arithmetic progression   0.000000   0.000000   0.000000 (  0.000009)

Абсолютно согласен с тем, что читаемость и поддержка важнее.

1 голос
/ 07 октября 2011

Ruby в основном не о выступлениях.Если вы хотите выступления, для этого есть другие языки.

Ruby - это удовольствие писать элегантный код:)

...