Porównanie prędkości Python vs Julia

10

Próbowałem porównać te dwa fragmenty i zobaczyć, ile iteracji można wykonać w ciągu jednej sekundy. Okazuje się, że Julia osiąga 2,5 miliona iteracji, a Python 4 miliony. Czy Julia nie powinna być szybsza? A może te dwa fragmenty nie są równoważne?

Pyton:

t1 = time.time()
i = 0
while True:
    i += 1
    if time.time() - t1 >= 1:
        break

Julia:

function f()
    i = 0
    t1 = now()
    while true
        i += 1
        if now() - t1 >= Base.Dates.Millisecond(1000)
            break
        end
    end
    return i
end
Michas
źródło
4
Nie jestem pewien, jak działa Julia, ale wygląda na to, że musisz zbudować nowy obiekt dla każdego porównania, podczas gdy Python wykonuje proste porównania liczb całkowitych.
chepner
1
Pamiętaj też, że jest to porównanie prędkości człowieka z biedakiem, prawda? W dzisiejszych czasach możesz sprawić, że Python i Julia będą działać mniej więcej z tą samą prędkością z odpowiednią ilością motywacji (na obu końcach). Jeśli robisz to, aby wybrać jeden z języków, sprawdź, w którym z nich łatwiej jest myśleć. Możesz to zoptymalizować później, kiedy będziesz go naprawdę potrzebować.
norok2
@ norok2 Dotyczy to niektórych kodów, ale nie dotyczy innych. Jeśli jesteś w stanie przekształcić kod Pythona w wywołanie funkcji bibliotecznej napisanej w szybkim języku lub jeśli jest on obsługiwany przez Numba lub coś podobnego, to może, ale w przeciwnym razie Python jest znacznie wolniejszy.
DNF
@DNF Są pewne rzeczy, w których Python jest szybszy, a inne, w których Julia jest szybsza. Jestem pewien, że możesz znaleźć przykłady obu. Nadmierne ignorowanie pełnego obrazu mówi, że Python jest „znacznie” (cokolwiek to może znaczyć) wolniejszy tylko ze względu na stosunkowo drogie jawne zapętlenie i wywołania funkcji. Jasne, jeśli to twój koń roboczy, to może lepiej ci będzie z Julią. Jeśli jednak użyjesz odpowiednich narzędzi, w Pythonie możesz być równie szybki. Czy warto nauczyć się tych narzędzi, czy lepiej nauczyć się innego języka? Trudno powiedzieć.
norok2
1
Używam obu języków i chociaż istnieją „niektóre” przykłady tego, że oba są szybsze, równowaga jest znacznie z jednej strony. Jest to po prostu konsekwencja interpretacji Pythona i wcale nie koncentruje się na wydajności, podczas gdy Julia mocno koncentruje się na wydajności. Właściwie to tak, jakby powiedzieć, że Python jest tak szybki jak C. Byłoby bardzo dziwne, gdyby nie było znaczącej różnicy, i podważyłoby to cel Julii.
DNF

Odpowiedzi:

9

Jest to swego rodzaju dziwne porównanie wydajności, ponieważ zwykle mierzy się czas potrzebny na obliczenie czegoś istotnego, zamiast sprawdzać, ile trywialnych iteracji można wykonać w określonym czasie. Miałem problem z uruchomieniem kodów Python i Julia, więc zmodyfikowałem kod Julia do pracy i po prostu nie uruchomiłem kodu Python. Jak zauważył @chepner w komentarzu, korzystanie now()i porównywanie czasu z DateTimeobiektami jest dość drogie. Funkcja Python time.time()po prostu zwraca wartość zmiennoprzecinkową. Jak się okazuje, istnieje funkcja Julia, time()która robi dokładnie to samo:

julia> time()
1.587648091474481e9

Oto czas oryginalnej f()funkcji (zmodyfikowanej do pracy) w moim systemie:

julia> using Dates

julia> function f()
           i = 0
           t1 = now()
           while true
               i += 1
               if now() - t1 >= Millisecond(1000)
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
4943739

Wykonał prawie 5 milionów iteracji, zanim upłynął czas. Tak jak powiedziałem, nie byłem w stanie uruchomić twojego kodu Python w moim systemie bez znaczącego majsterkowania (czego nie zawracałem sobie głowy). Ale oto wersja, f()której używa time()zamiast tego, którą wyobraźni nazywam g():

julia> function g()
           i = 0
           t1 = time()
           while true
               i += 1
               if time() - t1 >= 1
                   break
               end
           end
           return i
       end
g (generic function with 1 method)

julia> g()
36087637

Ta wersja wykonała 36 milionów iteracji. Więc myślę, że Julia jest szybsza w zapętlaniu? Tak! Cóż, właściwie główną pracą w tej pętli są wywołania, time()więc ... Julia szybciej generuje wiele time()połączeń!

Dlaczego czas jest taki dziwny? Jak już powiedziałem, większość faktycznej pracy tutaj to dzwonienie time(). Reszta pętli tak naprawdę nic nie robi. W optymalizowanym języku kompilatora, jeśli kompilator zobaczy pętlę, która nic nie robi, całkowicie ją wyeliminuje. Na przykład:

julia> function h()
           t = 0
           for i = 1:100_000_000
               t += i
           end
           return t
       end
h (generic function with 1 method)

julia> h()
5000000050000000

julia> @time h()
  0.000000 seconds
5000000050000000    

Woah, zero sekund! Jak to możliwe? Spójrzmy na kod LLVM (coś w rodzaju kodu maszynowego, ale dla wyobrażonej maszyny używanej jako reprezentacja pośrednia), która obniża się do:

julia> @code_llvm h()

;  @ REPL[16]:1 within `h'
define i64 @julia_h_293() {
top:
;  @ REPL[16]:6 within `h'
  ret i64 5000000050000000
}

Kompilator widzi pętlę, stwierdza, że ​​wynik jest za każdym razem taki sam, i po prostu zwraca tę stałą wartość zamiast faktycznego wykonania pętli. Co oczywiście zajmuje zero czasu.

Stefan Karpiński
źródło
To BogoMips języków programowania
norok2
1
Tak, ale bogomips są używane do pomiaru procesora, a nie języka programowania. Ale jasne, to coś, co można zmierzyć.
StefanKarpinski
4

Prawdopodobnie chcesz użyć time_nsfunkcji w Julii:

function f()
    i = 0
    t1 = time_ns()
    while true
        i += 1
        if time_ns() - t1 >= 10^9
            break
        end
    end
    return i
end

Na moim komputerze działa 10 razy szybciej niż Python.

Bogumił Kamiński
źródło
4

Cóż, nie tego obserwuję w moim systemie:

Python 3.7.7

Python 3.7.7 (default, Mar 26 2020, 15:48:22) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import time                                                                                                                                                       

In [2]: def f(): 
   ...:     t1 = time.time() 
   ...:     i = 0 
   ...:     while True: 
   ...:         i += 1 
   ...:         if time.time() - t1 >= 1: 
   ...:             return i 
   ...:                                                                                                                                                                   

In [3]: f()                                                                                                                                                               
Out[3]: 4676268


Julia 1.4.0:

julia> using Dates

julia> function f()
           i = 0
           t1 = now()
           while true
               i += 1
               if now() - t1 >= Dates.Millisecond(1000)
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
6339528

ale zauważ, że samo użycie time(tj. porównanie liczb zwykłych) jest jeszcze szybsze:

julia> function f()
           i = 0
           t1 = time()
           while true
               i += 1
               if time() - t1 >= 1
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
24742703
François Févotte
źródło
nie powinieneś używać time.perf_counter_ns()w Pythonie?
norok2
Korzystanie z time.perf_counter_ns nic nie zmienia (przynajmniej w moim systemie) dla tego testu porównawczego. Wydaje mi się, że przy pomiarze różnic czasowych rzędu 1 sekundy dokładność pomiaru czasu nie ma większego znaczenia. Liczy się tylko czas potrzebny do wykonania pomiaru i porównania powstałych obiektów (a także wydajności samych pętli).
François Févotte
W sprawach czas pomiaru Julia - dlatego w moim kodzie użyłem time_nsnie timejak to jest ~ 30% szybszy.
Bogumił Kamiński