2016-05-31 2 views
1
время

выполнения этого кода составляет около 1 секунды:петля с большим количеством итераций делает выполнения невероятно медленно

start_time = Time.now 

prev = 1 
(1..1000).each do |i| 
    (1..10000).each do |j| 
    result = j * prev 
    result = result + prev 
    result = result - prev 
    result = result/prev 
    prev = j 
    end 
end 

end_time = Time.now 
printf('%f sec', end_time - start_time) 

Но когда я использую одну петлю с 10000000 итераций (вместо 2-х петель, 1000 и 10000 итераций, как написано выше), он становится намного медленнее (около 4,5 секунд):

start_time = Time.now 

prev = 1 
(1..10000000).each do |j| 
    result = j * prev 
    result = result + prev 
    result = result - prev 
    result = result/prev 
    prev = j 
end 

end_time = Time.now 
printf('%f sec', end_time - start_time) 

Почему это происходит? Общий номер итераций остается прежним.

+1

Try исследовать значения 'result' в каждом конкретном случае. Я думаю, что '10_000_000 * 9_999_999' больше, чем' 10_000 * 9_999'. –

+1

У меня есть 1.35sec для первого и 1.24sec для второго кода. Переменная 'result' не растет чрезмерно, потому что она сбрасывается каждый раз. –

+0

Итак, итерация первого кода составляет 1,35 секунды и 1,24 секунды для второго? Странный. Попробуйте выполнить кастинг на более мелкие куски, каждые 20% от общего числа, например. Общая нагрузка должна увеличиваться в равной степени с простой проверкой, поэтому она не должна заметно влиять на тест. –

ответ

2

Второй пример обрабатывает гораздо большие числа, чем первый (как уже отмечал @Sergii K). Возможно, что второй примерный код достигает максимального предела Fixnum в вашей системе. В 32-битной системе maximum signed integer равен 2**(32-1) - 1 = 2147483647, что во втором примере намного меньше максимального значения j * prev (в отличие от максимальных продуктов в первом примере). В такой ситуации Ruby должен преобразовать Fixnums в Bignums, поэтому второй пример кода может быть медленнее первого.

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

Обновление: если число макс Fixnum только 1073741823, так как прокомментировал выше ОП, то это должно означать, что в то время как сама ОС составляет 64 бита, и, возможно, установленный рубин также 64-битный рубин, он по-прежнему использует только 4 байта для хранения номеров Fixnum (вместо 8 в по-настоящему 64-битных рубинах). Максимальное целочисленное значение намного меньше, чем необходимо во втором примере, поэтому ему действительно нужно преобразовать более высокие числа в Bignums, и именно там начинается медленность второго образца.

Вы можете проверить это самостоятельно, если вы сравните:

(2**(0.size * 8 -2) -1).class  # => Fixnum vs: 
(2**(0.size * 8 -2) -1 + 1).class # => should be Bignum 
Смежные вопросы