Używanie Pythona Timeit do pomiaru czasu kodu

W tym samouczku dowiesz się, jak używać funkcji timeit z modułu timeit Pythona. Dowiesz się, jak mierzyć czas prostych wyrażeń i funkcji w Pythonie.

Chronometraż kodu może pomóc w oszacowaniu czasu wykonania fragmentu kodu, a także zidentyfikować sekcje kodu, które należy zoptymalizować.

Zaczniemy od poznania składni funkcji timeit Pythona. Następnie omówimy przykłady kodu, aby zrozumieć, jak używać go do bloków czasowych kodu i funkcji w module Pythona. Zaczynajmy.

Jak korzystać z funkcji timeit Pythona

Moduł timeit jest częścią standardowej biblioteki Pythona i możesz go zaimportować:

import timeit

Składnia użycia funkcji timeit z modułu timeit jest przedstawiona poniżej:

timeit.timeit(stmt, setup, number)

Tutaj:

  • stmt to fragment kodu, którego czas wykonania ma być mierzony. Możesz określić go jako prosty łańcuch Pythona lub ciąg wielowierszowy albo przekazać nazwę wywoływanego.
  • Jak sama nazwa wskazuje, setup oznacza fragment kodu, który musi zostać uruchomiony tylko raz, często jako warunek wstępny do uruchomienia stmt. Załóżmy na przykład, że obliczasz czas wykonania dla utworzenia tablicy NumPy. W tym przypadku importowanie numpy to kod instalacyjny, a rzeczywiste tworzenie to instrukcja, która ma być mierzona w czasie.
  • Numer parametru określa liczbę uruchomień stmt. Domyślna wartość liczby to 1 milion (1000000), ale możesz również ustawić ten parametr na dowolną inną wybraną wartość.

Teraz, gdy poznaliśmy już składnię funkcji timeit(), zacznijmy kodować kilka przykładów.

Timing Proste wyrażenia Pythona

W tej sekcji spróbujemy zmierzyć czas wykonania prostych wyrażeń Pythona za pomocą timeit.

Uruchom REPL języka Python i uruchom następujące przykłady kodu. Tutaj obliczamy czas wykonania operacji potęgowania i dzielenia piętra dla przebiegów 10000 i 100000.

Zwróć uwagę, że przekazujemy instrukcję do pomiaru czasu jako łańcuch Pythona i używamy średnika do oddzielenia różnych wyrażeń w instrukcji.

>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706

>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764

Uruchamianie timeit Pythona w wierszu poleceń

Możesz także użyć timeit w wierszu poleceń. Oto odpowiednik wywołania funkcji timeit z wiersza poleceń:

$ python-m timeit -n [number] -s [setup] [stmt]
  • python -m timeit oznacza, że ​​uruchamiamy timeit jako główny moduł.
  • n to opcja wiersza polecenia określająca, ile razy kod powinien zostać uruchomiony. Jest to równoważne z argumentem liczba w wywołaniu funkcji timeit().
  • Możesz użyć opcji -s, aby zdefiniować kod instalacyjny.

Tutaj przepisujemy poprzedni przykład, używając odpowiednika z wiersza poleceń:

$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop

W tym przykładzie obliczamy czas wykonania wbudowanej funkcji len(). Inicjalizacja ciągu to kod instalacyjny przekazany przy użyciu opcji s.

$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)'
100000 loops, best of 5: 239 nsec per loop

Zwróć uwagę, że w danych wyjściowych otrzymujemy czas wykonania dla najlepszego z 5 przebiegów. Co to znaczy? Po uruchomieniu timeit w wierszu poleceń opcja repeat r jest ustawiana na domyślną wartość 5. Oznacza to, że wykonanie stmt przez określoną liczbę razy jest powtarzane pięć razy i zwracany jest najlepszy z czasów wykonania.

Analiza metod odwracania łańcuchów przy użyciu timeit

Podczas pracy z łańcuchami Pythona możesz chcieć je odwrócić. Dwa najczęstsze podejścia do odwracania strun są następujące:

  • Korzystanie z krojenia ciągów
  • Korzystanie z funkcji reversed() i metody join().

Odwróć łańcuchy Pythona za pomocą krojenia ciągów

Przyjrzyjmy się, jak działa wycinanie łańcuchów i jak można go użyć do odwrócenia łańcucha w Pythonie. Używając składni jakiś-ciąg[start:stop] zwraca wycinek łańcucha, zaczynając od początku indeksu i kończąc na indeksie stop-1. Weźmy przykład.

Rozważ następujący ciąg „Python”. Łańcuch ma długość 6, a lista indeksów to 0, 1, 2 do 5.

>>> string_1 = 'Python'

Gdy określisz zarówno wartość początkową, jak i końcową, otrzymasz fragment łańcucha rozciągający się od początku do końca-1. Dlatego string_1[1:4] zwraca 'y’.

>>> string_1 = 'Python'
>>> string_1[1:4]
'yth'

Jeśli wartość początkowa nie zostanie określona, ​​używana jest domyślna wartość początkowa zero, a wycinek zaczyna się od indeksu zero i rozciąga się do końca – 1.

Tutaj wartość zatrzymania wynosi 3, więc wycinek zaczyna się od indeksu 0 i przechodzi do indeksu 2.

>>> string_1[:3]
'Pyt'

Jeśli nie uwzględnisz indeksu stop, zobaczysz, że wycinek zaczyna się od indeksu start (1) i rozciąga się do końca łańcucha.

>>> string_1[1:]
'ython'

Zignorowanie zarówno wartości początkowej, jak i końcowej zwraca wycinek całego łańcucha.

>>> string_1[::]
'Python'

Utwórzmy wycinek z wartością kroku. Ustaw wartości startu, stopu i kroku odpowiednio na 1, 5 i 2. Otrzymujemy wycinek łańcucha zaczynający się od 1 rozciągający się do 4 (z wyłączeniem punktu końcowego 5) zawierający co drugi znak.

>>> string_1[1:5:2]
'yh'

Kiedy użyjesz kroku ujemnego, możesz uzyskać wycinek zaczynający się na końcu łańcucha. Z krokiem ustawionym na -2, string_1[5:2:-2] daje następujący wycinek:

>>> string_1[5:2:-2]
'nh'

Aby uzyskać odwróconą kopię łańcucha, pomijamy wartości początkowe i końcowe i ustawiamy krok na -1, jak pokazano:

>>> string_1[::-1]
'nohtyP'

Podsumowując: ciąg[::-1] zwraca odwróconą kopię ciągu.

Odwracanie ciągów przy użyciu wbudowanych funkcji i metod ciągów

Wbudowana funkcja reversed() w Pythonie zwróci odwrotny iterator nad elementami łańcucha.

>>> string_1 = 'Python'
>>> reversed(string_1)
<reversed object at 0x00BEAF70>

Możesz więc przejść przez odwrotny iterator za pomocą pętli for:

for char in reversed(string_1):
    print(char)

I uzyskaj dostęp do elementów łańcucha w odwrotnej kolejności.

# Output
n
o
h
t
y
P

Następnie możesz wywołać metodę join() w odwrotnym iteratorze ze składnią: .join(reversed(jakiś-łańcuch)).

Poniższy fragment kodu pokazuje kilka przykładów, w których separatorem jest odpowiednio myślnik i spacja.

>>> '-'.join(reversed(string1))
'n-o-h-t-y-P'
>>> ' '.join(reversed(string1))
'n o h t y P'

Tutaj nie chcemy żadnego separatora; więc ustaw separator na pusty ciąg, aby uzyskać odwróconą kopię ciągu:

>>> ''.join(reversed(string1))
'nohtyP'

Użycie .join(reversed(some-string)) zwraca odwróconą kopię ciągu.

Porównanie czasów wykonania za pomocą timeit

Do tej pory poznaliśmy dwa podejścia do odwracania ciągów znaków Pythona. Ale który z nich jest szybszy? Dowiedzmy Się.

W poprzednim przykładzie, w którym mierzyliśmy czas prostych wyrażeń Pythona, nie mieliśmy żadnego kodu instalacyjnego. Tutaj odwracamy łańcuch Pythona. Podczas gdy operacja odwrócenia łańcucha jest wykonywana tyle razy, ile jest określone przez liczbę, kod instalacyjny jest inicjalizacją ciągu, który zostanie wykonany tylko raz.

>>> import timeit
>>> timeit.timeit(stmt="string_1[::-1]", setup = "string_1 = 'Python'", number = 100000)
0.04951830000001678
>>> timeit.timeit(stmt = "''.join(reversed(string_1))", setup = "string_1 = 'Python'", number = 100000)
0.12858760000000302

Przy tej samej liczbie przebiegów odwracania danego łańcucha podejście polegające na dzieleniu łańcucha jest szybsze niż użycie metody join() i funkcji reversed().

Synchronizacja funkcji Pythona za pomocą timeit

W tej sekcji nauczymy się, jak mierzyć czas funkcji Pythona za pomocą funkcji timeit. Biorąc pod uwagę listę ciągów, poniższa funkcja hasDigit zwraca listę ciągów, które mają co najmniej jedną cyfrę.

def hasDigit(somelist):
     str_with_digit = []
     for string in somelist:
         check_char = [char.isdigit() for char in string]
         if any(check_char):
            str_with_digit.append(string)
     return str_with_digit

Teraz chcielibyśmy zmierzyć czas wykonania tej funkcji Pythona hasDigit() za pomocą timeit.

Najpierw zidentyfikujmy instrukcję, która ma być mierzona w czasie (stmt). Jest to wywołanie funkcji hasDigit() z listą łańcuchów jako argumentem. Następnie zdefiniujmy kod instalacyjny. Czy możesz zgadnąć, jaki powinien być kod instalacyjny?

Aby wywołanie funkcji przebiegło pomyślnie, kod instalacyjny powinien zawierać następujące elementy:

  • Definicja funkcji hasDigit()
  • Inicjalizacja listy argumentów ciągów

Zdefiniujmy kod instalacyjny w łańcuchu instalacyjnym, jak pokazano poniżej:

setup = """
def hasDigit(somelist):
    str_with_digit = []
    for string in somelist:
      check_char = [char.isdigit() for char in string]
      if any(check_char):
        str_with_digit.append(string)
    return str_with_digit
thislist=['puffin3','7frost','blue']
     """

Następnie możemy użyć funkcji timeit i uzyskać czas wykonania funkcji hasDigit() dla 100000 przebiegów.

import timeit
timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Output
0.2810094920000097

Wniosek

Nauczyłeś się, jak używać funkcji timeit Pythona do określania czasu w wyrażeniach, funkcjach i innych obiektach wywoływalnych. Może to pomóc w przeprowadzeniu testów porównawczych kodu, porównaniu czasów wykonania różnych implementacji tej samej funkcji i nie tylko.

Przyjrzyjmy się temu, czego nauczyliśmy się w tym samouczku. Możesz użyć funkcji timeit() ze składnią timeit.timeit(stmt=…,setup=…,number=…). Alternatywnie możesz uruchomić timeit w wierszu poleceń, aby określić czas krótkich fragmentów kodu.

W następnym kroku możesz zbadać, jak używać innych pakietów profilowania Pythona, takich jak line-profiler i memprofiler, do profilowania kodu odpowiednio pod kątem czasu i pamięci.

Następnie naucz się obliczać różnicę czasu w Pythonie.