Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Python列表上的'for'比Numpy数组上的'for'快?_Python_Arrays_Performance_Numpy - Fatal编程技术网

为什么Python列表上的'for'比Numpy数组上的'for'快?

为什么Python列表上的'for'比Numpy数组上的'for'快?,python,arrays,performance,numpy,Python,Arrays,Performance,Numpy,因此,我没有讲一个很长的故事,而是在编写一些代码,从二进制文件中读取一些数据,然后使用for循环遍历每个点。所以我完成了代码,它运行速度慢得离谱。我从大约128个数据通道循环了大约60000个点,这需要一分钟或更长的时间来处理。这比我预期的Python运行速度要慢得多。因此,我使用Numpy使整个过程更加高效,但在试图找出原始进程运行如此缓慢的原因时,我们进行了一些类型检查,发现我在Numpy数组而不是Python列表上循环。好的,没有什么大不了的,我们的测试设置的输入是相同的,我在循环之前将N

因此,我没有讲一个很长的故事,而是在编写一些代码,从二进制文件中读取一些数据,然后使用for循环遍历每个点。所以我完成了代码,它运行速度慢得离谱。我从大约128个数据通道循环了大约60000个点,这需要一分钟或更长的时间来处理。这比我预期的Python运行速度要慢得多。因此,我使用Numpy使整个过程更加高效,但在试图找出原始进程运行如此缓慢的原因时,我们进行了一些类型检查,发现我在Numpy数组而不是Python列表上循环。好的,没有什么大不了的,我们的测试设置的输入是相同的,我在循环之前将Numpy数组转换为列表。运行一分钟的慢代码现在只需10秒。我被打倒了。我唯一想做的就是把一个Numpy数组改成一个Python列表,我把它改了回去,它又慢得像泥一样。我简直不敢相信,所以我去找更确切的证据

$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k+1"
100 loops, best of 3: 5.46 msec per loop

$ python -m timeit "for k in range(5000): k+1"
1000 loops, best of 3: 256 usec per loop
发生了什么事?我知道Numpy数组和Python列表是不同的,但是为什么迭代数组中的每个点要慢得多呢


我相信,我在运行Numpy 10.1的Python 2.6和2.7中都观察到了这种行为。

我们可以做一些调查来找出这一点:

>>> import numpy as np
>>> a = np.arange(32)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])
>>> a.data
<read-write buffer for 0x107d01e40, size 256, offset 0 at 0x107d199b0>
>>> id(a.data)
4433424176
>>> id(a[0])
4424950096
>>> id(a[1])
4424950096
>>> for item in a:
...   print id(item)
... 
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
4424950096
4424950120
这是一件好事,因为这会打破python中的不变量,即两个对象在其生命周期中从来没有相同的
id

那么,numpy是如何做到这一点的呢?答案是,numpy必须用python类型(例如,在本例中为
numpy.float64
numpy.int64
)包装返回的对象,如果逐项迭代,这需要时间。迭代时会进一步证明这一点——我们看到在数组上迭代时,我们在两个单独的ID之间交替。这意味着python的内存分配器和垃圾收集器正在超时工作,以创建新对象,然后释放它们

列表没有此内存分配器/垃圾收集器开销。列表中的对象已经作为python对象存在(并且在迭代后它们仍然存在),因此它们都不会在列表的迭代中扮演任何角色

时间安排方法: 还要注意的是,你的时间安排被你的假设稍微偏离了一点。您假设
k+1
在这两种情况下所需的时间大致相同,但事实并非如此。请注意,如果我重复您的计时而不进行任何添加:

mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k"
1000 loops, best of 3: 233 usec per loop
mgilson$ python -m timeit "for k in range(5000): k"
10000 loops, best of 3: 114 usec per loop
$ python -m timeit -s "v = 1" "v + 1"
10000000 loops, best of 3: 0.0261 usec per loop
mgilson$ python -m timeit -s "import numpy; v = numpy.int64(1)" "v + 1"
10000000 loops, best of 3: 0.121 usec per loop
只有大约2倍的差异。但是,进行加法会产生5倍左右的差异:

mgilson$ python -m timeit "for k in range(5000): k+1"
10000 loops, best of 3: 179 usec per loop
mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k+1"
1000 loops, best of 3: 786 usec per loop
为了好玩,我们只需添加以下内容:

mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k"
1000 loops, best of 3: 233 usec per loop
mgilson$ python -m timeit "for k in range(5000): k"
10000 loops, best of 3: 114 usec per loop
$ python -m timeit -s "v = 1" "v + 1"
10000000 loops, best of 3: 0.0261 usec per loop
mgilson$ python -m timeit -s "import numpy; v = numpy.int64(1)" "v + 1"
10000000 loops, best of 3: 0.121 usec per loop
最后,您的时间还包括列表/数组构建时间,但这并不理想:

mgilson$ python -m timeit -s "v = range(5000)" "for k in v: k"
10000 loops, best of 3: 80.2 usec per loop
mgilson$ python -m timeit -s "import numpy; v = numpy.arange(5000)" "for k in v: k"
1000 loops, best of 3: 237 usec per loop
注意,在本例中,numpy实际上离列表解决方案更远。这表明迭代确实较慢,如果将numpy类型转换为标准python类型,可能会获得一些加速

1请注意,在切片时这不会花费很多时间,因为这只需要分配O(1)个新对象,因为numpy会将视图返回到原始数组中。

使用python 2.7

以下是我的速度和xrange:

python -m timeit -s "import numpy" "for k in numpy.arange(5000): k+1"
1000个循环,最好为3:1.22毫秒/循环

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"
10000个循环,最好3个循环:每个循环186 usec

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"
10000个循环,最好为3:161个循环

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"

Numpy的速度明显较慢,因为它正在迭代特定于Numpy的数组。这不是它的主要预期功能。在许多情况下,它们应该被视为一个整体的数字集合,而不是简单的列表/可编辑列表。例如,如果我们有一个相当大的数字列表,我们想将其提升到三次方,我们可以这样做:

python -m timeit "lst1 = [x for x in range(100000)];" "lst2 = map(lambda x: x**3, lst1)"
10个循环,每个循环最好3:125毫秒

注意:lst1表示任意列表。我知道,通过在范围内对x执行x**3,您可以在原始lambda内加快速度,但这是对一个应该已经存在且可能不是连续的列表的取消对齐

无论如何,numpy应该被视为一个数组:

python -m timeit -s "import numpy" "lst1 = numpy.arange(100000)" "lst2 = lst1**2"
10000个循环,每个循环最好3:120 usec

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"
假设有两个任意值列表,每个值都要相乘。在vanilla python中,您可以执行以下操作:

python -m timeit -s "lst1 = [x for x in xrange(0, 10000, 2)]" "lst2 = [x for x in xrange(2, 10002, 2)]" "lst3 = [x*y for x,y in zip(lst1, lst2)]"
1000个循环,每个循环最好3:736 usec

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"
在努比:

python -m timeit -s "import numpy" "lst1 = numpy.arange(0, 10000, 2)" "lst2 = numpy.arange(2, 10002, 2)" "lst3 = lst1*lst2"
100000个循环,每个循环最好3:10.9 usec

python -m timeit "for k in range(5000): k+1"
python -m timeit "for k in xrange(5000): k+1"

在最后两个例子中,NumPy一飞冲天,成为了明显的赢家。对于列表上的简单迭代,range或xrange就足够了,但是您的示例没有考虑Numpy数组的真正用途。这是比较飞机和汽车;是的,飞机的速度通常比预期的要快,但尝试飞往当地超市并不谨慎。

您使用的是哪种Python版本?不是numpy专家,但我认为关键是您通常不想使用Python循环迭代numpy数组,因为这样您就失去了速度提升(实际上,更多的是因为开销)。为了猜测一个猜测,NUMPI必须从C到Python,以产生它的值,其中Python <代码> 循环是非常理想的。您可以考虑查看<代码> DIS.DIS(So.Fun-FUNC)。查看是否有任何内容。
numpy.arange
正在创建一个numpy数组及其附带的所有开销…(事实上,它通常操作起来更快…)我使用矢量化和Numpy数组优化了慢代码,所以我很清楚这有什么目的。我真的想问一个问题,为什么在两个基本相同的对象上循环会如此不同。我认为mgilson可能是对的,它与数据转换有关。是的,好的,我认为这似乎是正确的几乎被数据所包围