Использовать `timeit` Python из программы, но работает так же, как командная строка?

Например, документация говорит:

Однако обратите внимание, что timeit автоматически определяет количество повторений только при использовании интерфейса командной строки.

Есть ли способ вызвать его из скрипта Python и автоматически определить количество повторений, при этом возвращается только самый короткий номер?


person endolith    schedule 28.09.2013    source источник
comment
Timeit теперь имеет функцию autorange.   -  person Mad Physicist    schedule 16.10.2019


Ответы (2)


Когда вы вызываете timeit из командной строки следующим образом:

python -mtimeit -s'import test' 'test.foo()'

Модуль timeit вызывается как скрипт. В частности, вызывается функция main:

if __name__ == "__main__":
    sys.exit(main())

Если вы посмотрите на исходный код, вы обратите внимание, что функция main может принимать аргумент args:

def main(args=None):    
    if args is None:
        args = sys.argv[1:]

Таким образом, действительно возможно запустить timeit из программы с точно таким же поведением, как при запуске из CLI. Просто укажите свой собственный args вместо того, чтобы установить его на sys.argv[1:]:

import timeit
import shlex

def foo():
    total = 0
    for i in range(10000):
        total += i**3
    return total

timeit.main(args=shlex.split("""-s'from __main__ import foo' 'foo()'"""))

напечатает что-то вроде

100 loops, best of 3: 7.9 msec per loop

К сожалению, main выводит на консоль вместо того, чтобы возвращать время цикла. Поэтому, если вы хотите программно использовать результат, возможно, проще всего будет начать с копирования функцию main, а затем изменить ее — изменить код печати, чтобы он вместо этого возвращал usec.


Пример от OP: если вы поместите это в utils_timeit.py:

import timeit
def timeit_auto(stmt="pass", setup="pass", repeat=3):
    """
    http://stackoverflow.com/q/19062202/190597 (endolith)
    Imitate default behavior when timeit is run as a script.

    Runs enough loops so that total execution time is greater than 0.2 sec,
    and then repeats that 3 times and keeps the lowest value.

    Returns the number of loops and the time for each loop in microseconds
    """
    t = timeit.Timer(stmt, setup)

    # determine number so that 0.2 <= total time < 2.0
    for i in range(1, 10):
        number = 10**i
        x = t.timeit(number) # seconds
        if x >= 0.2:
            break
    r = t.repeat(repeat, number)
    best = min(r)
    usec = best * 1e6 / number
    return number, usec

вы можете использовать его в таких сценариях:

import timeit
import utils_timeit as UT

def foo():
    total = 0
    for i in range(10000):
        total += i**3
    return total

num, timing = UT.timeit_auto(setup='from __main__ import foo', stmt='foo()')
print(num, timing)
person unutbu    schedule 28.09.2013
comment
Да, это выглядит хорошо, за исключением того, что задача этой функции не должна заключаться в том, чтобы вставлять os.curdir в sys.path. Либо PYTHONPATH должен быть правильно настроен, либо возня с sys.path должна быть задачей вызывающего скрипта (а не этой функции). - person unutbu; 30.09.2013
comment
Ваш пример не работает для меня (ImportError: cannot import name foo), но UT.timeit_auto(lambda: foo()) работает, производя результат, аналогичный timeit foo() IPython. - person endolith; 04.10.2013
comment
@endolith: О, но лямбда добавляет некоторые накладные расходы, так что это не идеально. lambda: 5*5 составляет 86 нс, а 5*5 составляет 21 нс. - person endolith; 04.10.2013
comment
@endolith: вы вызываете скрипт из командной строки? например python /path/to/script.py? - person unutbu; 04.10.2013
comment
Нет, я использовал runfile() в Spyder для его запуска. Вы правы, если я запускаю его из командной строки, он работает. Кроме того, я только что понял, что у IPython есть собственный немного другая реализация, поэтому IPython будет выполнять только 1 цикл для очень медленных функций, в то время как минимум с timeit.main() составляет 10 циклов. - person endolith; 04.10.2013
comment
IDE могут путаться с __main__, возможно, поэтому он не работает в Spyder. Многопоточные программы и программы с графическим интерфейсом также могут иметь проблемы при запуске из IDE. Иногда кажется необходимым запустить из CLI. - person unutbu; 04.10.2013

Начиная с Python 3.6, объекты timeit.Timer имеют тег autorange, которая показывает, как number определяется для выполнения из командной строки.

person Mad Physicist    schedule 16.10.2019