我有一个python脚本,它的工作就像它应该,但我需要写执行时间。我在谷歌上搜索过我应该使用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所需的时间。目的是用不同的索引和调优机制测试数据库的更新语句。
测试套件不会尝试使用导入的timeit,因此很难判断其意图是什么。尽管如此,这是一个典型的答案,一个完整的时间例子,似乎是有序的,详细阐述了马丁的答案。
timeit的文档提供了许多值得一看的例子和标志。命令行的基本用法是:
$ python -mtimeit "all(True for _ in range(1000))"
2000 loops, best of 5: 161 usec per loop
$ python -mtimeit "all([True for _ in range(1000)])"
2000 loops, best of 5: 116 usec per loop
运行-h查看所有选项。Python MOTW有一个关于timeit的很棒的部分,展示了如何通过从命令行导入和多行代码字符串运行模块。
在脚本形式中,我通常这样使用它:
import argparse
import copy
import dis
import inspect
import random
import sys
import timeit
def test_slice(L):
L[:]
def test_copy(L):
L.copy()
def test_deepcopy(L):
copy.deepcopy(L)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--n", type=int, default=10 ** 5)
parser.add_argument("--trials", type=int, default=100)
parser.add_argument("--dis", action="store_true")
args = parser.parse_args()
n = args.n
trials = args.trials
namespace = dict(L = random.sample(range(n), k=n))
funcs_to_test = [x for x in locals().values()
if callable(x) and x.__module__ == __name__]
print(f"{'-' * 30}\nn = {n}, {trials} trials\n{'-' * 30}\n")
for func in funcs_to_test:
fname = func.__name__
fargs = ", ".join(inspect.signature(func).parameters)
stmt = f"{fname}({fargs})"
setup = f"from __main__ import {fname}"
time = timeit.timeit(stmt, setup, number=trials, globals=namespace)
print(inspect.getsource(globals().get(fname)))
if args.dis:
dis.dis(globals().get(fname))
print(f"time (s) => {time}\n{'-' * 30}\n")
你可以很容易地放入你需要的函数和参数。使用非纯函数时要谨慎,并注意状态。
样例输出:
$ python benchmark.py --n 10000
------------------------------
n = 10000, 100 trials
------------------------------
def test_slice(L):
L[:]
time (s) => 0.015502399999999972
------------------------------
def test_copy(L):
L.copy()
time (s) => 0.01651419999999998
------------------------------
def test_deepcopy(L):
copy.deepcopy(L)
time (s) => 2.136012
------------------------------
撇开时间不提,您所显示的代码完全不正确:执行100个连接(完全忽略除最后一个连接外的所有连接),然后在执行第一个执行调用时,向它传递一个局部变量query_stmt,该变量仅在执行调用之后才初始化。
首先,让你的代码正确,不要担心时间:即一个函数建立或接收一个连接,并在该连接上执行100或500或任意数量的更新,然后关闭连接。一旦您的代码正常工作,就应该考虑在它上使用timeit !
具体来说,如果你想计时的函数是一个叫做foobar的无参数函数,你可以使用timeit。Timeit(2.6或更高版本——2.5和之前版本更复杂):
timeit.timeit('foobar()', number=1000)
从3.5开始,globals形参使得timeit可以直接用于带形参的函数:
timeit.timeit('foobar(x,y)', number=1000, globals = globals())
您最好指定运行次数,因为对于您的用例来说,默认的100万可能太高了(导致在这段代码中花费大量时间;-)。
您可以在要计时的块前后使用time.time()或time.clock()。
import time
t0 = time.time()
code_block
t1 = time.time()
total = t1-t0
这个方法不像timeit那样精确(它不平均几次运行),但它很简单。
time.time()(在Windows和Linux中)和time.clock()(在Linux中)对于快速函数来说不够精确(你得到total = 0)。在这种情况下,或者如果你想通过几次运行来平均时间,你必须手动多次调用函数(我认为你已经在你的示例代码中这样做了,当你设置它的number参数时,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中,正如Corey在评论中所述,time.clock()具有更高的精度(微秒而不是秒),并且比time.time()更受欢迎。
测试套件不会尝试使用导入的timeit,因此很难判断其意图是什么。尽管如此,这是一个典型的答案,一个完整的时间例子,似乎是有序的,详细阐述了马丁的答案。
timeit的文档提供了许多值得一看的例子和标志。命令行的基本用法是:
$ python -mtimeit "all(True for _ in range(1000))"
2000 loops, best of 5: 161 usec per loop
$ python -mtimeit "all([True for _ in range(1000)])"
2000 loops, best of 5: 116 usec per loop
运行-h查看所有选项。Python MOTW有一个关于timeit的很棒的部分,展示了如何通过从命令行导入和多行代码字符串运行模块。
在脚本形式中,我通常这样使用它:
import argparse
import copy
import dis
import inspect
import random
import sys
import timeit
def test_slice(L):
L[:]
def test_copy(L):
L.copy()
def test_deepcopy(L):
copy.deepcopy(L)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--n", type=int, default=10 ** 5)
parser.add_argument("--trials", type=int, default=100)
parser.add_argument("--dis", action="store_true")
args = parser.parse_args()
n = args.n
trials = args.trials
namespace = dict(L = random.sample(range(n), k=n))
funcs_to_test = [x for x in locals().values()
if callable(x) and x.__module__ == __name__]
print(f"{'-' * 30}\nn = {n}, {trials} trials\n{'-' * 30}\n")
for func in funcs_to_test:
fname = func.__name__
fargs = ", ".join(inspect.signature(func).parameters)
stmt = f"{fname}({fargs})"
setup = f"from __main__ import {fname}"
time = timeit.timeit(stmt, setup, number=trials, globals=namespace)
print(inspect.getsource(globals().get(fname)))
if args.dis:
dis.dis(globals().get(fname))
print(f"time (s) => {time}\n{'-' * 30}\n")
你可以很容易地放入你需要的函数和参数。使用非纯函数时要谨慎,并注意状态。
样例输出:
$ python benchmark.py --n 10000
------------------------------
n = 10000, 100 trials
------------------------------
def test_slice(L):
L[:]
time (s) => 0.015502399999999972
------------------------------
def test_copy(L):
L.copy()
time (s) => 0.01651419999999998
------------------------------
def test_deepcopy(L):
copy.deepcopy(L)
time (s) => 2.136012
------------------------------
专注于一件具体的事情。磁盘I/O很慢,所以如果您要调整的只是数据库查询,我就把它从测试中去掉。
如果您需要计算数据库执行的时间,请寻找数据库工具,例如询问查询计划,并注意性能不仅随确切的查询和您拥有的索引而变化,还随数据负载(您存储了多少数据)而变化。
也就是说,你可以简单地把你的代码放在一个函数中,并使用time .timeit()运行该函数:
def function_to_repeat():
# ...
duration = timeit.timeit(function_to_repeat, number=1000)
这将禁用垃圾收集,重复调用function_to_repeat()函数,并使用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)
将从脚本中抓取全局函数to_repeat, var1和var2,并将它们每次重复传递给函数。