Как я могу сегментировать код для тестирования производительности с помощью Pythons timeit?
У меня есть скрипт python, который работает так, как должен, но мне нужно написать время выполнения. Я googled, что я должен использовать timeit
но я не могу заставить его работать.
Мой скрипт Python выглядит так:
import sys
import getopt
import timeit
import random
import os
import re
import ibm_db
import time
from string import maketrans
myfile = open("results_update.txt", "a")
for r in range(100):
rannumber = random.randint(0, 100)
update = "update TABLE set val = %i where MyCount >= '2010' and MyCount < '2012' and number = '250'" % rannumber
#print rannumber
conn = ibm_db.pconnect("dsn=myDB","usrname","secretPWD")
for r in range(5):
print "Run %s\n" % r
ibm_db.execute(query_stmt)
query_stmt = ibm_db.prepare(conn, update)
myfile.close()
ibm_db.close(conn)
Мне нужно время, необходимое для выполнения запроса и запись его в файл results_update.txt
. Цель состоит в том, чтобы протестировать инструкцию обновления для моей базы данных с различными индексами и механизмами настройки.
Ответы
Ответ 1
Вы можете использовать time.time()
или time.clock()
до и после блока, который вы хотите использовать.
import time
t0 = time.time()
code_block
t1 = time.time()
total = t1-t0
Этот метод не такой точный, как timeit
(он не timeit
несколько прогонов), но это просто.
time.time()
(в Windows и Linux) и time.clock()
(в Linux) недостаточно точны для быстрых функций (вы получаете total = 0). В этом случае или если вы хотите усреднить время, прошедшее несколькими прогонами, вам нужно вручную вызвать функцию несколько раз (как я думаю, вы уже делаете в своем примере код, а timeit делает это автоматически, когда вы устанавливаете его числовой аргумент)
import time
def myfast():
code
n = 10000
t0 = time.time()
for i in range(n): myfast()
t1 = time.time()
total_n = t1-t0
В Windows, как сказал Кори в комментарии, time.clock()
имеет гораздо более высокую точность (микросекунда вместо второй) и является предпочтительной по сравнению с time.time()
.
Ответ 2
Если вы профилируете свой код и можете использовать IPython, он имеет магическую функцию %timeit
.
%%timeit
работает с ячейками.
In [2]: %timeit cos(3.14)
10000000 loops, best of 3: 160 ns per loop
In [3]: %%timeit
...: cos(3.14)
...: x = 2 + 3
...:
10000000 loops, best of 3: 196 ns per loop
Ответ 3
В отличие от времени, этот код, который вы показываете, просто неверен: вы выполняете 100 подключений (полностью игнорируете все, кроме последнего), а затем, когда вы выполняете первый вызов выполнения, вы передаете ему локальную переменную query_stmt
, которая вы выполняете инициализацию только после выполнения вызова.
Во-первых, сделайте свой код правильным, не беспокоясь о сроках: например, функция, которая производит или получает соединение и выполняет 100 или 500 или любое количество обновлений в этом соединении, затем закрывает соединение. Как только вы правильно работаете над своим кодом, это правильная точка, в которой следует подумать об использовании timeit
на нем!
В частности, если функция, которую вы хотите использовать, не имеет значения foobar
, вы можете использовать timeit.timeit (2.6 или позже - это сложнее в 2.5 и до):
timeit.timeit('foobar()', number=1000)
Лучше указать количество прогонов, потому что по умолчанию миллион может быть высоким для вашего прецедента (что приводит к большому количеству времени в этом коде; -).
Ответ 4
Сосредоточьтесь на одной конкретной вещи. Дисковый ввод/вывод медленный, поэтому я бы выбрал его из теста, если все, что вы собираетесь настроить, - это запрос к базе данных.
И если вам нужно рассчитать время выполнения вашей базы данных, вместо этого ищите инструменты базы данных, такие как запрос плана запроса, и обратите внимание, что производительность зависит не только от конкретного запроса и того, какие у вас индексы, но также и от загрузки данных (сколько данных вы сохранили)
Тем не менее, вы можете просто поместить свой код в функцию и запустить эту функцию с помощью timeit.timeit()
:
def function_to_repeat():
# ...
duration = timeit.timeit(function_to_repeat, number=1000)
Это отключит сборку мусора, повторно вызовет function_to_repeat()
и timeit.default_timer()
время общей продолжительности этих вызовов с помощью timeit.default_timer()
, который является наиболее точным из доступных часов для вашей конкретной платформы.
Вы должны убрать установочный код из повторяющейся функции; Например, сначала вы должны подключиться к базе данных, а затем только к запросам. Используйте аргумент setup
для импорта или создания этих зависимостей и передачи их в вашу функцию:
def function_to_repeat(var1, var2):
# ...
duration = timeit.timeit(
'function_to_repeat(var1, var2)',
'from __main__ import function_to_repeat, var1, var2',
number=1000)
будет брать глобальные переменные function_to_repeat
, var1
и var2
из вашего скрипта и передавать их в функцию при каждом повторении.
Ответ 5
Я вижу, что на этот вопрос уже был дан ответ, но все же хочу добавить 2 цента за то же самое.
Я также столкнулся с похожим сценарием, в котором мне нужно проверить время выполнения для нескольких подходов и, следовательно, записать небольшой script, который вызывает timeit для всех написанных в нем функций.
script также доступен как github gist здесь.
Надеюсь, это поможет вам и другим.
from random import random
import types
def list_without_comprehension():
l = []
for i in xrange(1000):
l.append(int(random()*100 % 100))
return l
def list_with_comprehension():
# 1K random numbers between 0 to 100
l = [int(random()*100 % 100) for _ in xrange(1000)]
return l
# operations on list_without_comprehension
def sort_list_without_comprehension():
list_without_comprehension().sort()
def reverse_sort_list_without_comprehension():
list_without_comprehension().sort(reverse=True)
def sorted_list_without_comprehension():
sorted(list_without_comprehension())
# operations on list_with_comprehension
def sort_list_with_comprehension():
list_with_comprehension().sort()
def reverse_sort_list_with_comprehension():
list_with_comprehension().sort(reverse=True)
def sorted_list_with_comprehension():
sorted(list_with_comprehension())
def main():
objs = globals()
funcs = []
f = open("timeit_demo.sh", "w+")
for objname in objs:
if objname != 'main' and type(objs[objname]) == types.FunctionType:
funcs.append(objname)
funcs.sort()
for func in funcs:
f.write('''echo "Timing: %(funcname)s"
python -m timeit "import timeit_demo; timeit_demo.%(funcname)s();"\n\n
echo "------------------------------------------------------------"
''' % dict(
funcname = func,
)
)
f.close()
if __name__ == "__main__":
main()
from os import system
#Works only for *nix platforms
system("/bin/bash timeit_demo.sh")
#un-comment below for windows
#system("cmd timeit_demo.sh")
Ответ 6
Вот простая оболочка для ответа Стивена. Эта функция не выполняет повторные прогоны/усреднения, просто избавляет вас от необходимости повторять код синхронизации везде :)
'''function which prints the wall time it takes to execute the given command'''
def time_func(func, *args): #*args can take 0 or more
import time
start_time = time.time()
func(*args)
end_time = time.time()
print("it took this long to run: {}".format(end_time-start_time))