Wywołania funkcji w języku Python bardzo powolne

to w zasadzie jest, aby upewnić się, że moja metodologia jest poprawna, ale moje główne pytanie, czy warto sprawdzać na zewnątrz funkcji, czy muszę uzyskać dostęp do funkcji w ogóle. Wiem, wiem, przedwczesna optymalizacja, ale w wielu przypadkach jest to różnica między pomieszczeniem operatora if wewnątrz wywołania funkcji, aby określić, czy chcesz uruchomić resztę kodu, lub umieścić go przed wywołaniem funkcji. Innymi słowy, nie wymaga żadnego wysiłku, aby zrobić to tak czy inaczej. Teraz, wszystkie weryfikacji mieszane między obydwoma, i chciałbym uzyskać to wszystko pięknie i standaryzowany.

głównym powodem, dla którego zapytałem, bo inne odpowiedzi, które widziałem, w zasadzie, o których mowa w timeit, ale to dało mi liczby ujemne, więc przeszedłem na to:

import timeit
import cProfile

def aaaa(idd):
    return idd

def main():
    #start = timeit.timeit()
    for i in range(9999999):
        a = 5
    #end = timeit.timeit()
    #print("1", end - start)

def main2():
    #start = timeit.timeit()
    for i in range(9999999):
        aaaa(5)
    #end = timeit.timeit()
    #print("2", end - start)

cProfile.run('main()', sort='cumulative')
cProfile.run('main2()', sort='cumulative')

i wy

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.310    0.310 {built-in method exec}
        1    0.000    0.000    0.310    0.310 <string>:1(<module>)
        1    0.310    0.310    0.310    0.310 test.py:7(main)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    2.044    2.044 {built-in method exec}
        1    0.000    0.000    2.044    2.044 <string>:1(<module>)
        1    1.522    1.522    2.044    2.044 test.py:14(main2)
  9999999    0.521    0.000    0.521    0.000 test.py:4(aaaa)

dla mnie to pokazuje, że nie wywołanie funkcji .31 sekunda, i wyzwanie trwa 1,52 sekundy, że prawie 5 razy wolniej. Ale, jak już powiedziałem, mam liczby ujemne z timeit, więc chcę się upewnić, że to faktycznie powoli.

również z tego, że zbieram powodem, dla którego wywołania funkcji tak wolni, polega na tym, że python musi sprawdzić, aby upewnić się, że funkcja nadal istnieje, zanim ona będzie w stanie jej uruchomić czy coś jeszcze? Nie ma sposobu, aby po prostu powiedzieć, że to się podoba...załóżmy, że wciąż jest tak, że nie trzeba robić niepotrzebną pracę, która (podobno) spowalnia jego 5x?


ty porównujesz jabłka i gruszki tutaj. Jedna metoda wykonuje proste zastosowanie, z drugiej wywołuje funkcję. Tak, wywołania funkcji dodaj koszty.

musisz usunąć to do minimum dla timeit:

>>> import timeit
>>> timeit.timeit('a = 5')
0.03456282615661621
>>> timeit.timeit('foo()', 'def foo(): a = 5')
0.14389896392822266

teraz wszystko, co zrobiliśmy dodaj wywołanie funkcji (foo robi to samo), więc można zmierzyć dogrywka, która trwa wywołanie funkcji. Nie można powiedzieć, że to prawie 4 razy wolniej, nie, funkcja dzwoń dodaje koszty 0,11 sekundy 1.000.000 iteracji.

jeśli zamiast a = 5 robimy coś, co trwa 0,5 sekundy, aby wykonać milion iteracji, przenoszenie ich w funkcji nie trwa 2 sekundy. Teraz to zajmie 0,61 sekundy, bo koszty ogólne funkcje nie rosną.

wywołanie funkcji musi manipulować stosami, naciskając na niego lokalny ramki, tworząc nową ramkę, a następnie ponownie czyścić go, gdy funkcja powraca.

innymi słowy, przenoszenie operatorów w funkcję dodaje małe koszty, i im więcej operatorów można się poruszać w tę funkcję, tym mniejsze koszty stają się w procentach całkowitej wykonanej pracy. Funkcja nigdy sprawia, że oświadczenia te same w sobie wolniej.

funkcja Pythona-to po prostu obiekt jest zapisywany w zmiennej; można przypisać do innej zmiennej, zastąpić je czymś zupełnie innym, lub je usunąć w każdej chwili. Po wywołaniu funkcji należy najpierw powołują się na imię, pod którym są one przechowywane (foo), a następnie wywołać obiekt funkcji ((arguments)); ten wyszukiwanie musi odbywać się za każdym razem na dynamicznym polskim.

można to zobaczyć bajtów w kodzie, generowanych dla funkcji:

>>> def foo():
...     pass
... 
>>> def bar():
...     return foo()
... 
>>> import dis
>>> dis.dis(bar)
  2           0 LOAD_GLOBAL              0 (foo)
              3 CALL_FUNCTION            0
              6 RETURN_VALUE        

LOAD_GLOBAL opcode szuka nazwa (foo) w globalnej przestrzeni nazw (głównie wyszukiwanie tabele mieszania) i umieszcza wynik w stos. CALL_FUNCTION następnie powoduje, że wszystko, co znajduje się w stosie, zastępując jego wartość zwracana. RETURN_VALUE zwraca wywołanie funkcji znowu, że jest najwyżej położonym w stosie jako wartości zwracanej.