2015-09-17 23 views
9

próby rozwiązania podstawowego problemu algorytmu w języku Ruby i testowanie wydajności.Wykonanie pętli dla każdej z pętli podczas

Tylko w przypadku, algorytm ma znaleźć najmniejszą dodatnią liczbę, która jest podzielna przez wszystkie numery od 1 do 20. Oto kod:

def remainder(number) # with while 
    divisor = 2 
    while divisor < 21 
    return false unless number % divisor == 0 
    divisor += 1 
    end 
    true 
end 

def remainder(number) # with each 
    (2..20).each do |divisor| 
    return false unless number % divisor == 0 
    end 
    true 
end 

number = 180_000_000 
while number < 10_000_000_000 
    if remainder number 
    puts "#{number}" 
    break 
    end 
    number += 1 
end 

Na moim komputerze, ze podczas gdy wersja Ruby zajmuje około 10 sekund, a każda z wersji zajmuje od 70 do 80 sekund. Kod robi dokładnie to samo, daje ten sam rezultat. Skąd taka różnica w wydajności?

Odpowiedz

5

Wydaje się, że koszt dodają:

  1. Tworzenie wyliczający dla zakresu przedmiotu (2..20)
  2. wywołania z bloku w each

OTO odniesienia

require 'benchmark' 

c = 100_000 
Benchmark.bm(7) do |x| 
    x.report("range - 1 :") { c.times { (2..20) } } 
    x.report("range - 2 :") { c.times { (2..20).each } } 
    x.report("range - 3 :") { c.times { (2..20).each { |x| x } } } 
end 

Przykładowa wydajność powyższego to:

   user  system  total  real 
range - 1 : 0.000000 0.000000 0.000000 ( 0.006004) 
range - 2 : 0.031000 0.000000 0.031000 ( 0.026017) 
range - 3 : 0.125000 0.000000 0.125000 ( 0.122081) 
[Finished in 0.4s] 

Jak widać, utworzenie obiektu klasy nie jest problemem, ale stworzenie wyliczający za to dodatkowego czasu, a przechodząc do tego bloku iteratora i wykonanie kodu, dodaje dalsze koszty.

W porównaniu do tego, implementacja pętli while wykonuje operacje podstawowe. Dlatego jest szybszy.

Uwaga for pętla wykona tak źle jak each, ponieważ jest mniej więcej odpowiednikiem each realizacji

+3

Warto również zauważyć, że porównujesz te małe koszty w stosunku do kosztu 'return false, chyba że liczba% divisor == 0', która w ogóle nie jest dużo kodu Ruby. Błędem byłoby zabranie wiadomości, że w Ruby powinieneś unikać tworzenia instancji w szeregach lub używać metod blokowych do struktury kodu. Tylko w rzadkich przypadkach zauważysz dużą różnicę czasu –

1

each to sposób, i to realizowane użyciu while w C z użyciem for pętlę, która (w C) robi to samo ty while pętla robi. Wewnętrznie musi posiadać licznik, zainicjować go do 2 i zwiększyć go do 20 - to samo, co twoja wersja while musi zrobić. Ale wersja each ma również narzut na tworzenie funkcji (blok), wysyłając ją do metody each i wywołując ją w każdej iteracji pętli for wewnątrz implementacji metody each. Wszystko to wymaga dodatkowej pracy z komputera, który sprawia, że ​​kod jest wolniejszy.

+1

a 'each' wersji tworzy i zbiera śmieci' (2..20) '' zakres number' times ... – spickermann

+1

#each w zakresie jest zaimplementowany z pętlą 'for' - http://ruby-doc.org/core-2.2.3/Range.html#method-i-each – Anthony

+0

@NeilSlater - Myślałem o tym samym, ale nie "podczas gdy coś; end' i 'while something do; koniec równoważny? – Anthony

Powiązane problemy