Язык программирования Python.
Профайлер.
Для определения мест в программе, на выполнение которых уходит значительная часть времени, обычно применяется профайлер.
Модуль 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 значений. Необходимо отметить, что на результаты может влиять загруженность компьютера, на котором проводятся испытания.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.