Опубликован: 22.12.2005 | Уровень: для всех | Доступ: платный
Лекция 14:

Устройство интерпретатора языка Python

< Лекция 13 || Лекция 14: 12345678

Профайлер

Для определения мест в программе, на выполнение которых уходит значительная часть времени, обычно применяется профайлер.

Модуль profile

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

В качестве примера можно рассмотреть профилирование функции для поиска строк из списка, наиболее похожих на данную. Для того чтобы качественно профилировать функцию difflib.get_close_matches(), нужен большой объем данных. В файле russian.txt собрано 160 тысяч слов русского языка. Следующая программа поможет профилировать функцию difflib.get_close_matches():

import difflib, profile

                def print_close_matches(word):
                  print "\n".join(difflib.get_close_matches(word + "\n", open("russian.txt")))

                profile.run(r'print_close_matches("профайлер")')

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

провайдер

 трайлер

 бройлер

 899769 function calls (877642 primitive calls) in 23.620 CPU seconds

 Ordered by: standard name

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 1    0.000    0.000   23.610   23.610 <string>:1(?)
 1    0.000    0.000   23.610   23.610 T.py:6(print_close_matches)
 1    0.000    0.000    0.000    0.000 difflib.py:147(__init__)
 1    0.000    0.000    0.000    0.000 difflib.py:210(set_seqs)
 159443    1.420    0.000    1.420    0.000 difflib.py:222(set_seq1)
 2    0.000    0.000    0.000    0.000 difflib.py:248(set_seq2)
 2    0.000    0.000    0.000    0.000 difflib.py:293(__chain_b)
 324261    2.240    0.000    2.240    0.000 difflib.py:32(_calculate_ratio)
 28317    1.590    0.000    1.590    0.000 difflib.py:344(find_longest_match)
 6474    0.100    0.000    2.690    0.000 difflib.py:454(get_matching_blocks)
 28317/6190    1.000    0.000    2.590    0.000 difflib.py:480(__helper)
 6474    0.450    0.000    3.480    0.001 difflib.py:595(ratio)
 28686    0.240    0.000    0.240    0.000 difflib.py:617(<lambda>)
 158345    8.690    0.000    9.760    0.000 difflib.py:621(quick_ratio)
 159442    2.950    0.000    4.020    0.000 difflib.py:650(real_quick_ratio)
 1    4.930    4.930   23.610   23.610 difflib.py:662(get_close_matches)
 1    0.010    0.010   23.620   23.620 profile:0(print_close_matches("профайлер"))
 0    0.000             0.000          profile:0(profiler)

Здесь колонки таблицы показывают следующие значения: ncalls - количество вызовов (функции), tottime - время выполнения кода функции (не включая времени выполнения вызываемых из нее функций), percall - то же время, в пересчете на один вызов, cumtime - суммарное время выполнения функции (и всех вызываемых из нее функций), filename - имя файла, lineno - номер строки в файле, function - имя функции (если эти параметры известны).

Из приведенной статистики следует, что наибольшие усилия по оптимизации кода необходимо приложить в функциях quick_ratio() (на нее потрачено 8,69 секунд), get_close_matches() (4,93 секунд), затем можно заняться real_quick_ratio() (2,95 секунд) и _calculate_ratio() (секунд).

Это лишь самый простой вариант использования профайлера: модуль profile (и связанный с ним pstats ) позволяет получать и обрабатывать статистику: их применение описано в документации.

Модуль timeit

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

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

from timeit import Timer

                t = Timer("""
                res = ""
                for k in range(1000000,1010000):
                  res += str(k)
                """)
                print t.timeit(200)

                t = Timer("""
                res = []
                for k in range(1000000,1010000):
                  res.append(str(k))
                res = ",".join(res)
                """)
                print t.timeit(200)

                t = Timer("""
                res = ",".join([str(k) for k in range(1000000,1010000)])
                """)
                print t.timeit(200)

Разные версии Python дадут различные результаты прогонов:

# Python 2.3
                77.6665899754
                10.1372740269
                9.07727599144

                # Python 2.4
                9.26631307602
                9.8416929245
                7.36629199982

В старых версиях Python рекомендуемым способом конкатенации большого количества строк являлось накопление их в списке с последующим применением функции join() (кстати, инкрементная конкатенация почти в восемь раз медленнее этого приема). Начиная с версии 2.4, инкрементная конкатенация была оптимизирована и теперь имеет даже лучший результат, чем версия со списками (которая вдобавок требует больше памяти). Но чемпионом все-таки является работа со списковым включением, поэтому свертывание циклов в списковое включение позволяет повысить эффективность кода.

Если требуются более точные результаты, рекомендуется использовать метод repeat(n, k) - он позволяет вызывать timeit(k) n раз, возвращая список из n значений. Необходимо отметить, что на результаты может влиять загруженность компьютера, на котором проводятся испытания.

< Лекция 13 || Лекция 14: 12345678
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?

 

Павел Ялганов
Павел Ялганов

Скажите экзамен тоже будет ввиде теста? или там будет какое то практическое интересное задание?

Мария Кравцова
Мария Кравцова
Россия, Сочи, РГПУ им. А.И.Герцена, 1997
Екатерина Архангельская
Екатерина Архангельская
Россия, СПбГУАП