Timeit显示常规python比numpy更快?

时间:2018-07-10 00:13:33

标签: python performance numpy timeit

我正在为游戏编写一段代码,该代码使用它们在游戏中的坐标位置来计算屏幕上所有对象之间的距离。最初,我打算使用基本的Python和列表来执行此操作,但是由于需要计算的距离数将随着对象数的增加而成倍增加,因此我认为使用numpy可以更快。 / p>

我对numpy不太熟悉,并且我一直在尝试使用它的基本代码。我写了一些时间来编码相同的函数在numpy和常规Python中完成一次计算所需的时间,而numpy似乎比常规的要花费更多的时间。 python。

功能非常简单。它以1.1开头,然后递增200,000次,在最后一个值上加上0.1,然后找到新值的平方根。这不是我在游戏代码中实际要做的,而是要从位置坐标中找到总距离矢量。这只是我一起进行的快速测试。我已经读过here,在NumPy中数组的初始化花费了更多时间,因此我将numpy和python数组的初始化都移到了它们的函数之外,但是Python仍然比numpy

这是一段代码:

#!/usr/bin/python3

import numpy
from timeit import timeit
#from time import process_time as timer
import math

thing = numpy.array([1.1,0.0], dtype='float')
thing2 = [1.1,0.0]

def NPFunc():

    for x in range(1,200000):
        thing[0] += 0.1
        thing[1] = numpy.sqrt(thing[0])


    print(thing)
    return None


def PyFunc():

    for x in range(1,200000):
        thing2[0] += 0.1
        thing2[1] = math.sqrt(thing2[0])

    print(thing2)
    return None


print(timeit(NPFunc, number=1))
print(timeit(PyFunc, number=1))

它给出了这个结果,表明普通的Python快了3倍:

[ 20000.99999999    141.42489173]
0.2917748889885843

[20000.99999998944, 141.42489172698504]
0.10341173503547907

我做错什么了吗,这个计算是否是如此简单,以至于不是numpy的良好测试?

2 个答案:

答案 0 :(得分:5)

  

我做错什么了吗,这个计算是否是如此简单,以至于它不是对NumPy的良好测试?

计算并不是很简单,但是您没有利用NumPy的任何优势。

NumPy的主要好处是向量化:您可以一次性对数组的每个元素进行操作,并且任何需要的循环都发生在NumPy内经过严格优化的C(或Fortran或C ++或其他)循环中,而不是缓慢的通用Python迭代。

但是您只访问一个值,因此在C中没有循环可以完成。

最重要的是,由于数组中的值存储为“本机”值,因此NumPy函数不需要解开框,将原始C double从Python float中拉出,然后按照所有Python数学函数所必须的方法,将它们重新装在新的Python float中。

但是您也不这样做。实际上,您正在 double 起作用:将值作为float从数组中拉出(装箱),然后将其传递给函数(必须取消装箱)它,然后重新装箱以返回结果),然后将其存储回数组中(再次将其装箱)。

同时,由于np.sqrt设计用于数组,因此它必须首先检查传递的类型,并确定是否需要遍历数组或取消装箱并重新装箱单个值等等,而math.sqrt仅取一个值。当您在200000个元素的数组上调用np.sqrt时,该类型开关的增加成本可以忽略不计,但是每次通过内循环执行此操作时,情况就不同了。


所以,这不是不公平的测试。

您已经证明,使用NumPy一次提取一个值,一次对它们进行操作,然后一次将它们存储回数组中比不使用NumPy慢。

但是,如果将其与实际利用NumPy进行比较(例如,通过创建一个200000浮点数的数组,然后在该数组上调用np.sqrt与循环遍历并在每个数组上调用math.sqrt一个-您将证明使用NumPy的目的比不使用NumPy的要快

答案 1 :(得分:3)

您正在比较错误

a_list = np.arange(0,20000,0.1)
timeit(lambda:np.sqrt(a_list),number=1)