Timeit显示常规python比numpy更快?

CMB*_*CMB 0 python performance numpy timeit

我正在为游戏编写一段代码,使用游戏中的坐标位置计算屏幕上所有对象之间的距离.最初我打算使用基本的Python和列表来做到这一点,但由于需要计算的距离数量会随着对象的数量呈指数级增长,我认为这样做可能会更快numpy.

我不是很熟悉numpy,而且我一直在用它来试验基本的代码.我写了一些代码来计算同一个函数numpy在常规Python中完成计算需要多长时间,并且numpy似乎总是花费比常规python更多的时间.

功能很简单.它从1.1开始,然后增加200,000次,将0.1添加到最后一个值,然后找到新值的平方根.这不是我在游戏代码中实际要做的事情,它将涉及从位置坐标中找到总距离矢量; 这只是我拼凑的快速测试.我已经在这里读到,数组的初始化在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))
Run Code Online (Sandbox Code Playgroud)

它给出了这个结果,表明普通Python的速度提高了3倍:

[ 20000.99999999    141.42489173]
0.2917748889885843

[20000.99999998944, 141.42489172698504]
0.10341173503547907
Run Code Online (Sandbox Code Playgroud)

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

aba*_*ert 5

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

这并不是说计算很简单,而是你没有利用NumPy.

NumPy的主要好处是矢量化:你可以一次性对一个数组的每个元素应用一个操作,并且在NumPy内部的一些紧密优化的C(或Fortran或C++或其他)循环内发生任何循环,而不是一个缓慢的通用Python迭代.

但是你只访问一个值,所以在C中没有循环.

最重要的是,因为数组中的值存储为"原生"的价值观,NumPy的功能不需要拆箱它们,拉动纯粹的C double出一个Python的float,然后在一个新的Python重新盒子里float时,任何Python数学函数必须的方式.

但你也没有这样做.实际上,你这个工作加倍:你将数值拉出float(装箱),然后将其传递给一个函数(必须将其拆箱,然后重新装箱以返回结果),然后将其存储回一个数组(再次取消装箱).

同时,因为np.sqrt设计用于数组,它必须首先检查你传递它的类型,并决定是否需要循环遍历数组或unbox并重新设置单个值或其他任何值,而math.sqrt只需要一个值.当您调用np.sqrt200000个元素的数组时,该类型开关的增加成本可以忽略不计,但是当您每次通过内循环执行此操作时,这是一个不同的故事.


所以,这不是一个不公平的考验.

你已经证明了使用NumPy一次取出一个值,一次一个地对它们进行操作,并且一次一个地将它们存储回数组中,这比不使用NumPy要慢.

但是,如果你将它与实际利用NumPy进行比较 - 例如,通过创建一个包含200000个浮点数的数组,然后调用np.sqrt该数组而不是循环遍历它并调用math.sqrt每个 - 你会证明使用NumPy的方式是打算比不使用它更快.