2010-06-15 2 views
1

Какая из этих двух форм инициализации массива лучше в Ruby?Какая из этих инициализаций массива лучше в Ruby?

Метод 1:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

@data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) } 

DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
    @data[day][hour] = 'something' 
    end 
end 

Способ 2:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

@data = {} 

DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
    @data[day] ||= {} 
    @data[day][hour] = 'something' 
    end 
end 

Разница между первым способом и вторым способом, является то, что второй не выделяет память на начальном этапе. Я чувствую, что второй из них немного уступает, когда дело доходит до производительности из-за большого количества копий Array, которые должны произойти.

Однако, в Ruby нет прямой информации о том, что происходит. Итак, если кто-то может объяснить мне, что лучше, было бы здорово!

Благодаря

+1

Вы считаете, что делаете что-то вроде 'time ruby ​​myscript.rb', чтобы узнать, что быстрее? Помните, что старая пословица «мера дважды, разрезанная один раз» из плотницких? При вычислении это «мера один раз, не тратьте время на ваше задание». –

+0

@JUST МОЙ правильный МНЕНИЕ: меня беспокоит не только то, что происходит быстрее, но я также обеспокоен тем, что происходит за кулисами. Смотрите, на C, Java всякий раз, когда я инициализирую массив, я уверен, что его смежное место памяти захвачено в памяти. И если нужна динамически растущая версия, в ней много экземпляров массива. Итак, мне было просто любопытно узнать, какая будет лучшая практика. – bragboy

+1

@Bragaadeesh: Вы должны научиться проскальзывать своего внутреннего уродца, кузнечика. : D Сначала убедитесь, что ваш код является правильным и поддерживаемым. Тогда беспокойтесь о производительности, нанося ущерб ремонтопригодности (но ** никогда не ** корректность!) В ограниченных, хорошо документированных областях только при необходимости.На языке, подобном Ruby, вы можете улучшить производительность алгоритмически (лучший выбор), используя трюки кода (менее оптимальные) или прыгая вниз в реализацию C, если это абсолютно необходимо (последний, отчаянный курорт). Однако сделайте это в конце реализации, но не раньше, чем вы начнете. –

ответ

3

Перед тем, как ответить на вопрос, который вы просили, я собираюсь ответить на этот вопрос, который вы должны были спросить, но не сделал:

Q: Должен ли я сосредоточиться сначала сделать мой код доступным для чтения, или сначала сосредоточиться на производительности?

A: сделать код читаемым и правильно первым, а затем, и только, если есть проблемы с производительностью, начинают беспокоиться о производительности путем измерения, где проблема производительности первого и только , затем, внося изменения в ваш код.

Теперь, чтобы ответить на вопрос, который вы просили, но не должны иметь:

method1.rb:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

10000.times do 

    @data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) } 

    DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
     @data[day][hour] = 'something' 
    end 
    end 

end 

method2.rb:

Результаты мозга умершей теста :

$ time ruby method1.rb 

real 0m1.189s 
user 0m1.140s 
sys 0m0.000s 

$ time ruby method2.rb 

real 0m1.879s 
user 0m1.780s 
sys 0m0.020s 

Мне кажется, что использование пользовательского времени (важный фактор) имеет method1.rb намного быстрее. Вы, конечно, не должны доверять этому эталону и должны сделать свой собственный, отражающий использование вашего фактического кода. Это, однако, то, что вы должны сделать только после, вы определили, какой код является вашим узким местом производительности на самом деле. (Подсказка: 99,44% компьютерных программистов 100% ошибочно, когда они догадываются, где их узкие места без измерения!)

+0

Получил это то, что вы пытались понять. – bragboy

2

Я завернул оба фрагмента кода в отдельные методы и сделал некоторые бенчмаркинг. Вот результаты:

Benchmark.bm(7) do |x| 
    x.report ("method1") { 100000.times { method1 } } 
    x.report ("method2") { 100000.times { method2 } } 
end 

      user  system  total  real 
method1 11.370000 0.010000 11.380000 (11.392233) 
method2 17.920000 0.010000 17.930000 (18.328318) 
+0

Эй, спасибо большое! Я расшифровываю, что первый почти вдвое лучше второго! – bragboy

3

Что случилось с просто

@data = Array.new(7) { Array.new(24) { 'something' }} 

Или, если вы довольны, имеющие объект же везде:

@data = Array.new(7) { Array.new(24, 'something') } 

Это намного быстрее, не то, что было бы важно. Это также гораздо больше читаемо, что является самой важной вещью. В конце концов, цель кода - сообщить намерение другим заинтересованным сторонам, не, общающимся с компьютером.

    user system  total  real 
method1 8.969000 0.000000 8.969000 (9.059570) 
method2 16.547000 0.000000 16.547000 (16.799805) 
method3 6.468000 0.000000 6.468000 (6.616211) 
method4 0.969000 0.015000 0.984000 (1.021484)
Эта последняя строка также показывает еще одну интересную вещь: во время исполнения доминирует время, необходимое для создания строк 7 * 24 * 100000 = 16,8 млн. 'something'.

И, конечно, есть еще одна важная беседа: ваши method1 и method2, которые вы сравниваете друг с другом, делают две совершенно разные вещи! Не имеет смысла сравнивать их друг с другом. method1 создает Array, method2 создает Hash.

Вашего method1 эквивалентен мой первый пример выше:

@data = Array.new(7) { Array.new(24) { 'something' }} 

Хотя method2 является (очень примерно) эквивалентно:

@data = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = 'something' }} 

Ну, за исключением того, что вашегоmethod2 инициализирует весь Hash нетерпеливо, в то время как мой метод выполняет только код инициализации лениво, если нетипичный считывается.

Другими словами, после выполнения кода инициализации, то Hash все еще пуст:

@data # => {} 

Но всякий раз, когда вы пытаетесь получить доступ ключ, она волшебным образом появится:

@data[5][17] # => 'something' 

И он останется там:

@data # => {5 => {17 => 'something'}} 

Поскольку данный код не является фактическим ly инициализировать Hash, это, очевидно, быстрее:

    user system  total  real 
method5 0.266000 0.000000 0.266000 (0.296875)

+0

Hi Jörg !! Я ждал «твоего» ответа. Причина, по которой я что-то помещаю, заключается в том, что я что-то делаю. Позволяет просто сказать, что это не постоянное значение каждый раз. И спасибо за то, что он пролил свет на концепцию Hash vs Array в Ruby. – bragboy

Смежные вопросы