如何使用 Pythons timeit 计时代码段来测试性能?

2025-01-06 08:32:00
admin
原创
139
摘要:问题描述:我有一个 python 脚本,它运行正常,但我需要写入执行时间。我在 Google 上搜索过我应该使用什么timeit,但似乎无法让它工作。我的 Python 脚本如下所示:import sys import getopt import timeit import random import os ...

问题描述:

我有一个 python 脚本,它运行正常,但我需要写入执行时间。我在 Google 上搜索过我应该使用什么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
" % 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(它不平均多次运行),但它很简单。

time.time()(在 Windows 和 Linux 中)和time.clock()(在 Linux 中)对于快速函数来说不够精确(您得到总计 = 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 中,正如 Corey 在评论中所述,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)

从 3.5 开始,该globals参数可以直接timeit与接受参数的函数一起使用:

timeit.timeit('foobar(x,y)', number=1000, globals = globals())

您最好指定运行次数,因为默认值一百万次对于您的用例来说可能太高(导致在此代码上花费大量时间;-)。

解决方案 4:

专注于一件具体的事情。磁盘 I/O 很慢,所以如果您要调整的只是数据库查询,我会将其从测试中剔除。

如果您需要计时数据库执行时间,请寻找数据库工具,例如询问查询计划,并注意性能不仅因确切查询和索引而异,还因数据负载(您存储了多少数据)而异。

也就是说,您可以简单地将代码放入函数中并使用以下命令运行该函数timeit.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)

将从脚本中获取全局变量function_to_repeatvar1var2在每次重复时将它们传递给函数。

解决方案 5:

这是 steven 答案的简单包装。此功能不执行重复运行/平均,只是让您不必到处重复计时代码 :)

'''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))

解决方案 6:

如何使用以下方法对函数进行计时timeit

import timeit

def time_this():
    return 'a' + 'b'

timeit.timeit(time_this, number=1000)

它返回运行 1000 次所花费的时间(以秒为单位)time_this()

解决方案 7:

测试套件没有尝试使用导入的timeit,因此很难判断其意图。尽管如此,这是一个规范的答案,因此完整的示例timeit似乎是有序的,详细说明了Martijn 的答案。

的文档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}
n = {n}, {trials} trials
{'-' * 30}
")

    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}
{'-' * 30}
")

您可以轻松添加所需的函数和参数。使用非纯函数时请谨慎,并注意状态。

示例输出:

$ 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
------------------------------

解决方案 8:

另一个简单的 timeit 示例:

def your_function_to_test():
   # do some stuff...

time_to_run_100_times = timeit.timeit(your_function_to_test, number=100)

解决方案 9:

我看到这个问题已经得到解答了,但我还是想发表一下我的看法。

我也遇到过类似的情况,我必须测试几种方法的执行时间,因此编写了一个小脚本,该脚本在其中编写的所有函数上调用 timeit。

该脚本也可以在此处作为 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();"


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")

解决方案 10:

如果要测量性能,建议使用它time.perf_counter_ns(),因为它具有纳秒精度。一个简单的例子:

import time
from time import perf_counter_ns

def convert_nanoseconds_to_seconds(nanoseconds: int) -> float:
    return nanoseconds / 1_000_000_000

start = perf_counter_ns()

def my_function():
    # Insert your code here
    time.sleep(1)

end = perf_counter_ns()

print(f"Function took {convert_nanoseconds_to_seconds(end - start):.2f} seconds to execute.")

它在终端中的显示方式:

Function took 1.23 seconds to execute.

函数装饰器

但是,使用函数装饰器会更优雅@measure_performance,因为每次调用函数时都会触发计时器,并且函数完成后时钟会自动停止。例如:

import time
from time import perf_counter_ns

def convert_nanoseconds_to_seconds(nanoseconds: int) -> float:
    return nanoseconds / 1_000_000_000

def measure_performance(function):
    def wrapper(*args, **kwargs):
        start = perf_counter_ns()
        result = function(*args, **kwargs)
        end = perf_counter_ns()
        print(f"Function {function.__name__} took {convert_nanoseconds_to_seconds(end - start):.2f} seconds to execute.")
        return result
    return wrapper

@measure_performance
def my_function():
    # Insert your code here
    time.sleep(1)

my_function()

Python 的带计时器的函数装饰器

另一个选择是使用现有的包。坦白说,我是Timer for Python的作者。这是一个轻量级的包,可以轻松测量时间和性能。它也基于time.perf_counter_ns(),并且已经有一个内置的函数装饰器,名为@function_timer()

from timer import function_timer

@function_timer()
def my_function():
    # Insert your code here
    time.sleep(1)

my_function()

它在终端中的显示方式:

Elapsed time: 1.23 seconds for thread MY_FUNCTION

如果要自定义默认输出,可以设置不同的线程名称或小数。例如:

Elapsed time: 0.12345 seconds for thread CUSTOM
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用