0

我是 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 unit testing ) 但不应该有一种简单直接的方法来获得一大块代码的处理时间而没有太多麻烦(导入/定义设置等)吗?我在这里的想法类似于这样做的方式:

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

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

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

希望这不会让人觉得这是一种咆哮,我真的在努力理解“pythonian 的思维方式”,但老实说,这并不是自然而然的,根本不是......

4

2 回答 2

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工作方式比您想象的要直观得多。

编辑添加:

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

引用文档:

在 Unix 上,以浮点数形式返回当前处理器时间,以秒为单位。精度,实际上是“处理器时间”含义的定义,取决于同名 C 函数的精度,但无论如何,这是用于对 Python 或计时算法进行基准测试的函数......分辨率通常优于一微秒。

进行中。。

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

是的,可能是这样,但这是文档可以帮助您的情况之一,这里是凤仙花示例的链接。这里比较温和的介绍一下timeit

于 2012-12-22T16:50:21.447 回答
2

为语句计时时,您只想为该语句计时,而不是设置。设置可能比被测语句慢得多。

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

对于您的情况,只需lfactor(number)直接测试,然后使用以下timeit()功能:

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

设置代码检索lfactor()函数,以及numbersys.argv主脚本中获取;否则将不会看到功能和编号。

对语句进行性能测试绝对没有print意义,这不是您要计时的。使用timeit不是查看调用的结果,而是查看运行它所需的时间。由于被测代码运行了数千次,因此您得到的只是(可能)相同结果的数千次打印。

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

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

timer = timeit.default_timer
start = timer()
print lfactor(number)
time_taken = timer() - start
于 2012-12-22T16:45:21.127 回答