Python 奇怪的结果来自时间

Python 奇怪的结果来自时间,python,debugging,time,ipython,Python,Debugging,Time,Ipython,我试图在%的时间内重复IPython的功能,但出于某种奇怪的原因,某些功能的测试结果非常糟糕 伊皮顿: In [11]: from random import shuffle ....: import numpy as np ....: def numpy_seq_el_rank(seq, el): ....: return sum(seq < el) ....: ....: seq = np.array(xrange(10000)) ....:

我试图在%的时间内重复IPython的功能,但出于某种奇怪的原因,某些功能的测试结果非常糟糕

伊皮顿:

In [11]: from random import shuffle
   ....: import numpy as np
   ....: def numpy_seq_el_rank(seq, el):
   ....:     return sum(seq < el)
   ....:
   ....: seq = np.array(xrange(10000))
   ....: shuffle(seq)
   ....:

In [12]: %timeit numpy_seq_el_rank(seq, 10000//2)
10000 loops, best of 3: 46.1 µs per loop
正如您所看到的,在python中,我创建了100个循环,而不是像在ipython中那样创建了10000个循环(结果慢了35000倍),因为这需要很长的时间。有人能解释为什么python的结果如此之慢吗

UPD: 下面是
cProfile.run('my\u timeit(code=np\u code,setup=np\u setup,rep=3,loops=10000)
输出:

4.987秒内调用30650个函数
订购人:标准名称
ncalls tottime percall cumtime percall文件名:lineno(函数)
1    0.000    0.000    4.987    4.987 :1()
1    0.000    0.000    0.000    0.000 :2()
3 0.001 0.000 4.985 1.662:2(内部)
300 0.006 0.000 4.961 0.017:7(数量级)
1 0.000 0.000 4.987 4.987 Lab10.py:47(我的时间)
3 0.019 0.006 0.021 0.007随机。py:277(随机)
1 0.000 0.000 0.002 0.002 timeit.py:121(初始)
3 0.000 0.000 4.985 1.662时间单位:185(时间单位)
1 0.000 0.000 4.985 4.985时间单位:208(重复)
1 0.000 0.000 4.987 4.987时间单位:239(重复)
2 0.000 0.000 0.000 0.000时间单位:90(reindent)
3 0.002 0.001 0.002 0.001{编译}
3 0.000 0.000 0.000 0.000{gc.disable}
3 0.000 0.000 0.000 0.000{gc.enable}
3 0.000 0.000 0.000 0.000{gc.isenabled}
1 0.000 0.000 0.000 0.000{globals}
3 0.000 0.000 0.000 0.000{isinstance}
3 0.000 0.000 0.000 0.000{len}
3 0.000 0.000 0.000 0.000{“列表”对象的“附加”方法}
1 0.000 0.000 0.000 0.000{方法'disable'的''lsprof.Profiler'对象}
299970.001 0.000 0.001 0.000{随机对象的随机方法}
2 0.000 0.000 0.000 0.000{“str”对象的方法“替换”}
1 0.000 0.000 0.000 0.000{min}
3 0.003 0.001 0.003 0.001{numpy.core.multiarray.array}
1 0.000 0.000 0.000 0.000{范围}
300 4.9550.017 4.9550.017{sum}
6 0.000 0.000 0.000 0.000{时间时钟}

在python的一个实现中,此代码运行速度慢的原因可能有很多。一个可能会以不同的方式进行优化,一个可能会预编译某些部分,而另一个则会进行完全解释。找出原因的唯一方法是分析代码

将产生类似于的结果

ncalls tottime percall cumtime percall文件名:lineno(函数)
1 0.000 0.000 0.000 0.000:1(测试)
1    0.000    0.000    0.000    0.000 :1()
1 0.000 0.000 0.000 0.000{方法'disable'的''lsprof.Profiler'对象}
1 0.000 0.000 0.000 0.000{“str”对象的方法“upper”}

它显示了函数调用的时间、调用次数以及调用所用的时间。

一个问题是,您误读了结果
ipython
告诉您,对于总时间最少的10000次迭代,10000次迭代中的每一次都花费了多长时间。
timeit.repeat
模块报告了整个100次迭代所用的时间(同样是三次迭代中最短的一次)。因此,真正的差异是46.1µs/循环(ipython)与16.5 ms/循环(python),仍然是~350x差异的一个因素,但不是35000x

您没有显示
ipython
的评测结果。在您的
ipython
会话中,您是否可能从numpy import sum执行了
,或者从numpy import*
执行了
?如果是这样的话,您将一直在计时
numpy.sum
(它针对
numpy
数组进行了优化,并且会),而您的
python
代码(它以
ipython
没有的方式隔离全局变量)运行正常的
sum
(必须将所有值转换为python
int
并求和)

如果您检查分析输出,几乎所有的工作都是在
sum
中完成的;如果代码的这部分速度提高了几个数量级,那么总时间也会相应减少。这将解释“真正的”差异;在上面链接的测试用例中,这是一个40倍的差异,对于更小的数组(数组越小,
numpy
越不能“炫耀”)和更复杂的值(我相信这里是0和1的总和)


剩下的问题(如果有的话)可能是代码如何被稍微不同的
评估,或者可能是
随机
洗牌的奇怪之处(对于一致性测试,您希望使用一致的种子来播种
随机
,以使“随机性”可重复)但我怀疑这是一个超过百分之几的差异。

您是否尝试过分析代码以查看速度缓慢的原因?@Soviut,没有,我不知道如何分析timeit.repeat函数,因为它对meIs来说太复杂了。你为什么要传递一个字符串代码块,而不是简单地运行你在IPython中做的相同测试?@Soviut:
IPython
's
%timeit
magic也在做字符串求值呢(它们都
compile
it一次,然后计算运行时间,实际上它不是一次又一次地重新编译,也不是提前编译计时)。我不会担心任何差异会带来超过一两微秒的开销。结果告诉你什么?对我来说,它似乎花费了很多时间将
代码块转换为可运行的代码。这是我提到的解释它的事情之一。@Soviut:重读它。字符串成本是co在
cumtime中解开所有在其中完成的事情from timeit import timeit, repeat

def my_timeit(code, setup, rep, loops):
    result = repeat(code, setup=setup, repeat=rep, number=loops)
    return '%d loops, best of %d: %0.9f sec per loop'%(loops, rep, min(result))

np_setup = '''
from random import shuffle
import numpy as np
def numpy_seq_el_rank(seq, el):
    return sum(seq < el)

seq = np.array(xrange(10000))
shuffle(seq)
'''
np_code = 'numpy_seq_el_rank(seq, 10000//2)'

print 'Numpy seq_el_rank:\n\t%s'%my_timeit(code=np_code, setup=np_setup, rep=3, loops=100)
import cProfile

cProfile.run('repeat(code, setup=setup, repeat=rep, number=loops)')