17 апреля 2013 г.

Хронометраж в Python -e

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

Так вот чтоб все это можно было сделать мы будем использовать модуль Time, который уже есть в инструкциях Python -a.

Для того чтоб все это работало мы создадим и сохраним на долго, утилиту timer.


# Файл mytimer.py

import time
reps = 1000
repslist = range(reps)

def timer(func, *pargs, **kargs):
    start = time.clock()
    for i in repslist:
        ret = func(*pargs, **kargs)
    elapsed = time.clock() - start
    return (elapsed, ret) 

Эта утилита способна вызывать любую функцию с любым количеством аргументов. Она фиксирует начальное время, вызывает функцию фиксированное число раз и вычитает время начала из времени конца.

  • Модуль time из стандартной библиотеки языка Python позволяет получить текущее  время  с  точностью,  зависящей  от  платформы.  В  Windows,  как утверждается, этот  модуль  позволяет получить  время  с точностью до  микросекунды и, следовательно, обеспечивает высокую точность. 
  • Вызов функции range был вынесен за пределы цикла измерения времени, благодаря чему время конструирования диапазона не накладывается на получаемые результаты в Python 2.6. В Python 3.0 функция range возвращает итератор, поэтому в версии 3.0 данный шаг можно считать излишним (хотя он и не мешает).
  • Счетчик reps  оформлен как глобальная переменная, благодаря чему  она 
может изменяться импортирующим модулем при необходимости: mytimer. 
reps = N.

Далее будет подробней...
Теперь можно приступать к измерению производительности инструментов итераций. Запустите следующий сценарий – он использует модуль mytimer, который мы только что написали, для измерения относительной скорости выполнения различных приемов конструирования списков, изученных нами:
# Файл timeseqs.py
import sys, mytimer    # Импортирует функцию timer 
reps = 10000
repslist = range(reps) # Вызов функции range вынесен за пределы цикла в 2.6
def forLoop():
    res = []
    for x in repslist:
        res.append(abs(x))
    return res
def listComp():
    return [abs(x) for x in repslist]
def mapCall():
    return list(map(abs, repslist))    # Вызов list необходим только в 3.0
def genExpr():
    return list(abs(x) for x in repslist) # Функция list вынуждает 
                                          # вернуть сразу все результаты
def genFunc():
    def gen():
        for x in repslist:
            yield abs(x)
    return list(gen())
print(sys.version)
for test in (forLoop, listComp, mapCall, genExpr, genFunc):
    elapsed, result = mytimer.timer(test)
    print (‘-’ * 33)
    print (‘%-9s: %.5f => [%s...%s]’ %
            (test.__name__, elapsed, result[0], result[-1]))

Этот сценарий тестирует все пять альтернативных способов создания списков и, как видно из листинга, выполняет по 10 миллионов итераций каждым из способов, то есть каждый из тестов создает список из 10 000 элементов 1 000 раз.

Результаты:
C:\misc> c:\python30\python timeseqs.py
3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)]
---------------------------------
forLoop  : 2.64441 => [0...9999]
---------------------------------
listComp : 1.60110 => [0...9999]
---------------------------------
mapCall  : 1.41977 => [0...9999]
---------------------------------
genExpr  : 2.21758 => [0...9999]
---------------------------------
genFunc  : 2.18696 => [0...9999]

Вывод - функция map оказалась быстрее.
-----------------------------------------------------------------
Следующий пост расскажет про альтернативные способы реализации...скоро

З.Ы. 3 часа ночи, спать пора бы итти

3 комментария:

  1. Можно еще реализовать в виде декоратора и писать в лог, тогда можно профайлить функции простым добавлением @profile перед их объявлением, например

    ОтветитьУдалить
  2. Создал два файла, как в примере, скопировал в них содержимое с этой страницы, и вот такая ошибка при запуске https://pp.vk.me/c621822/v621822850/15d14/HkNRrG1T3ew.jpg

    ОтветитьУдалить