<statement,setup =“”> timeit设计背后的逻辑是什么?</statement,>

时间:2012-12-22 16:26:50

标签: python debugging timeit

我是Python的新手,并且认为我会在Project Euler上遇到问题,同时要做一些具体的事情。

我想到了定时不同解决方案的想法,看看他们如何相互评价。然而,这个简单的任务对我来说太复杂了。我读到time.clock()调用在unix系统上不够准确(秒分辨率对于现代处理器来说简直是可悲的)。因此,我偶然发现了timeit模块,它似乎是分析任务的首选。

我不得不说我真的不明白为什么他们采用这种反直觉的方式去做。我似乎无法让它工作,而不需要重写/重构我的代码,我觉得非常令人沮丧。

采取下面的代码,永远不要错过任何一个既不漂亮也不特别有效的代码:

import math
import sys
from timeit import Timer

def digitsum(number):
    rem = 0 
    while number > 0:
        rem += number % 10
        number //= 10
    return rem

def prime_form(p):
    if p == 2 or p == 3 or p == 5:
        return True
    elif (p-1) % 6 != 0 and (p+1) % 6 != 0:
        return False
    elif digitsum(p) % 3 == 0: 
        return False
    elif p % 10 == 0 or p % 10 == 5:
        return False
    else:
        return True

def lfactor(n):

    if n <= 3:
        return 1

    limit = int(math.sqrt(n))
    if limit % 2 == 0:
        limit -= 1

    lfac = 1
    for i in range(3,limit+1,2):
        if prime_form(i):
            (div,rem) = divmod(n,i)
            if rem == 0:
                lfac = max(lfac, max(lfactor(div) ,lfactor(i)))

    return lfac if lfac != 1 else n

number = int(sys.argv[1])
t = Timer("""print lfactor(number)""", """import primefacs""")
t.timeit(100)
#print lfactor(number)

如果我想对行print lfactor(number)进行计时,为什么我要经历一堆循环,尝试定义一个设置语句等等。我理解为什么人们会想要调试工具与...正在测试的代码(la单元测试)但是不应该有一种简单而直接的方法来获得大量代码的处理时间而不需要太多麻烦(导入/定义设置等)?我在这里想的是像人们那样做的方式:

long t0 = System.currentTimeInMillis();
// do something
long t = System.currentTimeInMillis() - t0;

..或者甚至更好地使用MATLAB,使用tic / toc命令:

tic
x = A\b;
t(n) = toc;

希望这不是一种咆哮,我真的很想理解“蟒蛇的思维方式”,但说实话,它并不是自然而然的,而不是......

2 个答案:

答案 0 :(得分:3)

简单,声明和设置背后的逻辑是设置不是您想要进行基准测试的代码的一部分。通常一个python模块加载一次,而其中的函数运行不止一个,更多。

pythonic使用方式timeit

$ python -m timeit -h

Tool for measuring execution time of small code snippets.

This module avoids a number of common traps for measuring execution
times.  See also Tim Peters' introduction to the Algorithms chapter in
the Python Cookbook, published by O'Reilly.

Library usage: see the Timer class.

Command line usage:
    python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement]

Options:
  -n/--number N: how many times to execute 'statement' (default: see below)
  -r/--repeat N: how many times to repeat the timer (default 3)
  -s/--setup S: statement to be executed once initially (default 'pass')
  -t/--time: use time.time() (default on Unix)
  -c/--clock: use time.clock() (default on Windows)
  -v/--verbose: print raw timing results; repeat for more digits precision
  -h/--help: print this usage message and exit
  --: separate options from statement, use when statement starts with -
  statement: statement to be timed (default 'pass')

[cut]


$ python -m timeit -s 'from primefacs import lfactor' 'lfactor(42)'

$ # this does not work, primefacs is not binded, ie. not loaded
$ python -m timeit 'primefacts.lfactor(42)'


$ # this does not work too, lfactor is not defined
$ python -m timeit 'lfactor(42)'

$ # this works but the time to import primefacs is benchmarked too
$ # but only the first time is loaded, the successive ones the cache is used. 
$ python -m timeit 'import primefacts; primefacts.lfactor(42)'

正如您所看到的,timeit的工作方式比您想象的更直观。

编辑以添加:

  

我读到在unix上time.clock()调用不够准确   系统(秒分辨率简直是现代的可悲   处理器)。

引用文档:

  

在Unix上,将当前处理器时间作为浮点数返回   用秒表示。精度,实际上是非常的定义   “处理器时间”的含义取决于C函数的含义   同名,但无论如何,这是用于的功能   基准测试Python或计时算法...分辨率是   通常优于1微秒。

继续......

  

我不得不说我真的不明白为什么他们这样做了   反直觉的方式来解决它。我似乎无法让它工作,   无需重写/重构我的代码,我发现它   令人沮丧。

是的,它可能是,但这是文档可以帮助您的情况之一,这里是凤仙花examples的链接。这里有更多gentle introductiontimeit

答案 1 :(得分:2)

在对某个语句进行计时时,您希望仅对该语句计时,而不是设置。设置可能比被测语句慢得多。

请注意,timeit会运行您的语句数千次,以获得合理的平均值。它这样做是为了消除OS调度和其他进程(包括但不限于磁盘缓冲区刷新,cronjob执行,内存交换等)的影响;在比较不同的代码选择时,只有平均时间才有意义。

对于您的情况,只需直接测试lfactor(number),然后使用timeit()函数:

timeit.timeit('lfactor(number)', 'from __main__ import lfactor, number')

设置代码从主脚本中检索lfactor()函数以及从number获取的sys.argv;否则将无法看到功能和数字。

print语句的性能测试中绝对没有没有点,这不是你想要的时间。使用timeit 关于查看调用结果,只需查看运行它所需的时间。由于被测代码运行了数千次,所以你得到的就是数千张(大概)相同的结果。

请注意,通常timeit用于比较短python片段的性能特征;要在更复杂的代码中找到性能瓶颈,请改用profiling

如果您只需要一次运行时间,请使用timeit.default_timer()功能为您的平台获取最准确的计时器:

timer = timeit.default_timer
start = timer()
print lfactor(number)
time_taken = timer() - start