Рубрики
Без рубрики

timeit – время выполнения небольших фрагментов кода Python.

Автор оригинала: Doug Hellmann.

Цель:

Время выполнения небольших фрагментов кода Python.

Модуль timeit предоставляет простой интерфейс для определения времени выполнения небольших фрагментов кода Python. Он использует зависящую от платформы функцию времени, чтобы обеспечить наиболее точный расчет времени и снижает влияние затрат на запуск или останов на расчет времени за счет многократного выполнения кода.

Содержание модуля

timeit определяет единственный открытый класс, Timer . Конструктор для Timer принимает оператор для синхронизации и оператор “setup” (например, используемый для инициализации переменных). Операторы Python должны быть строками и могут включать встроенные символы новой строки.

Метод timeit () запускает оператор настройки один раз, затем повторно выполняет основной оператор и возвращает количество прошедшего времени. Аргумент timeit () определяет, сколько раз запускать оператор; по умолчанию – 1000000.

Базовый пример

Чтобы проиллюстрировать, как используются различные аргументы Timer , вот простой пример, который печатает идентифицирующее значение при выполнении каждого оператора.

timeit_example.py

import timeit

# using setitem
t  timeit.Timer("print('main statement')", "print('setup')")

print('TIMEIT:')
print(t.timeit(2))

print('REPEAT:')
print(t.repeat(3, 2))

При запуске вывод показывает результаты повторных вызовов print () .

$ python3 timeit_example.py

TIMEIT:
setup
main statement
main statement
1.8429999999944324e-06
REPEAT:
setup
main statement
main statement
setup
main statement
main statement
setup
main statement
main statement
[1.4149999999976681e-06, 1.005999999997842e-06,
1.0179999999984646e-06]

timeit () выполняет оператор установки один раз, затем вызывает основной оператор count раз. Он возвращает одно значение с плавающей запятой, представляющее совокупное количество времени, затраченного на выполнение основного оператора.

Когда используется repeat () , он вызывает timeit () несколько раз (в данном случае 3 раза), и все ответы возвращаются в виде списка.

Сохранение значений в словаре

В этом более сложном примере сравнивается количество времени, которое требуется для заполнения словаря большим количеством значений с использованием различных методов. Во-первых, для настройки Timer необходимо несколько констант. Переменная setup_statement инициализирует список кортежей, содержащих строки и целые числа, которые будут использоваться основными операторами для создания словарей, используя строки в качестве ключей и сохраняя целые числа в качестве связанных значений.

# A few constants
range_size  1000
count  1000
setup_statement  ';'.join([
    "l = [(str(x), x) for x in range(1000)]",
    "d = {}",
])

Служебная функция show_results () предназначена для печати результатов в удобном формате. Метод timeit () возвращает количество времени, необходимое для повторного выполнения оператора. Вывод show_results () преобразует это в количество времени, которое требуется на одну итерацию, а затем дополнительно уменьшает значение до среднего количества времени, необходимого для сохранения одного элемента в словаре.

def show_results(result):
    "Print microseconds per pass and per item."
    global count, range_size
    per_pass  1000000 * (result / count)
    print('{:6.2f} usec/pass'.format(per_pass), end' ')
    per_item  per_pass / range_size
    print('{:6.2f} usec/item'.format(per_item))


print("{} items".format(range_size))
print("{} iterations".format(count))
print()

Чтобы установить базовый уровень, в первой протестированной конфигурации используется __setitem __ () . Все другие варианты позволяют избежать перезаписи значений, уже имеющихся в словаре, поэтому эта простая версия должна быть самой быстрой.

Первый аргумент Timer – это многострочная строка с сохраненными пробелами для обеспечения правильного анализа при запуске. Второй аргумент – это константа, установленная для инициализации списка значений и словаря.

# Using __setitem__ without checking for existing values first
print('__setitem__:', end' ')
t  timeit.Timer(
    textwrap.dedent(
        """
        for s, i in l:
            d[s] = i
        """),
    setup_statement,
)
show_results(t.timeit(numbercount))

В следующем варианте используется setdefault () , чтобы гарантировать, что значения, уже содержащиеся в словаре, не будут перезаписаны.

# Using setdefault
print('setdefault :', end' ')
t  timeit.Timer(
    textwrap.dedent(
        """
        for s, i in l:
            d.setdefault(s, i)
        """),
    setup_statement,
)
show_results(t.timeit(numbercount))

Этот метод добавляет значение, только если возникает исключение KeyError при поиске существующего значения.

# Using exceptions
print('KeyError   :', end' ')
t  timeit.Timer(
    textwrap.dedent(
        """
        for s, i in l:
            try:
                existing = d[s]
            except KeyError:
                d[s] = i
        """),
    setup_statement,
)
show_results(t.timeit(numbercount))

И последний метод использует « in », чтобы определить, есть ли в словаре конкретный ключ.

# Using "in"
print('"not in"   :', end' ')
t  timeit.Timer(
    textwrap.dedent(
        """
        for s, i in l:
            if s not in d:
                d[s] = i
        """),
    setup_statement,
)
show_results(t.timeit(numbercount))

При запуске скрипт выдает следующий результат.

$ python3 timeit_dictionary.py

1000 items
1000 iterations

__setitem__:  62.47 usec/pass   0.06 usec/item
setdefault : 122.70 usec/pass   0.12 usec/item
KeyError   :  60.78 usec/pass   0.06 usec/item
"not in"   :  55.79 usec/pass   0.06 usec/item

Это время для MacMini и будет зависеть от того, какое оборудование используется и какие другие программы запущены в системе. Поэкспериментируйте с переменными range_size и count , поскольку разные комбинации будут давать разные результаты.

Из командной строки

В дополнение к программному интерфейсу timeit предоставляет интерфейс командной строки для тестирования модулей без инструментов.

Чтобы запустить модуль, используйте параметр -m для интерпретатора Python, чтобы найти модуль и рассматривать его как основную программу:

$ python3 -m timeit

Например, чтобы получить помощь:

$ python3 -m timeit -h

Tool for measuring execution time of small code snippets.

This module avoids a number of common traps for measuring execution
times.  See also Tim Peters' introduction to the Algorithms chapter in
the Python Cookbook, published by O'Reilly.

...

Аргумент statement работает в командной строке немного иначе, чем аргумент для Timer . Вместо использования одной длинной строки передайте каждую строку инструкций как отдельный аргумент командной строки. Для отступа строк (например, внутри цикла) вставляйте пробелы в строку, заключая ее в кавычки.

$ python3 -m timeit -s \ \
"for i in range(1000):" \
"  d[str(i)] = i"

1000 loops, best of 5: 332 usec per loop

Также можно определить функцию с более сложным кодом, а затем вызвать функцию из командной строки.

timeit_setitem.py

def test_setitem(range_size1000):
    l  [(str(x), x) for x in range(range_size)]
    d  {}
    for s, i in l:
        d[s]  i

Чтобы запустить тест, передайте код, который импортирует модули и запускает функцию тестирования.

$ python3 -m timeit \
"import timeit_setitem; timeit_setitem.test_setitem()"

1000 loops, best of 5: 376 usec per loop

Смотрите также