在python 3下考虑Ipython上的这个性能测试:
创建范围,range_iterator和生成器
In [1]: g1 = range(1000000)
In [2]: g2 = iter(range(1000000))
In [3]: g3 = (i for i in range(1000000))
使用python原生和来衡量求和的时间
In [4]: %timeit sum(g1)
10 loops, best of 3: 47.4 ms per loop
In [5]: %timeit sum(g2)
The slowest run took 374430.34 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 123 ns per loop
In [6]: %timeit sum(g3)
The slowest run took 1302907.54 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 128 ns per loop
不确定是否应该担心警告。范围版本的时间变化很长(为什么?),但range_iterator和生成器是相似的。
现在让我们使用numpy.sum
In [7]: import numpy as np
In [8]: %timeit np.sum(g1)
10 loops, best of 3: 174 ms per loop
In [9]: %timeit np.sum(g2)
The slowest run took 8.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 6.51 µs per loop
In [10]: %timeit np.sum(g3)
The slowest run took 9.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 446 ns per loop
g1和g3变得慢了~3.5,但是range_iterator g2现在比原始总和慢约50倍。 g3获胜。
In [11]: type(g1)
Out[11]: range
In [12]: type(g2)
Out[12]: range_iterator
In [13]: type(g3)
Out[13]: generator
为什么对numpy.sum上的range_iterator这样的惩罚?是否应避免此类物品?它是否概括 - 做"自制"发电机总是击败numpy上的其他物体?
编辑1:我意识到np.sum不会评估range_iterator但会返回另一个range_iterator对象。所以这种比较并不好。为什么不对它进行评估?
编辑2:我也意识到numpy.sum会以整数形式保留范围,因此会因整数溢出而在我的总和上给出错误的结果。
In [12]: sum(range(1000000))
Out[12]: 499999500000
In [13]: np.sum(range(1000000))
Out[13]: 1783293664
In [14]: np.sum(range(1000000), dtype=float)
Out[14]: 499999500000.0
中间结论 - 不要在非numpy对象上使用numpy.sum ......?
答案 0 :(得分:3)
你看过iter上重复总和的结果吗?
95:~/mypy$ g2=iter(range(10))
96:~/mypy$ sum(g2)
Out[96]: 45
97:~/mypy$ sum(g2)
Out[97]: 0
98:~/mypy$ sum(g2)
Out[98]: 0
为什么是0?因为g2
只能使用一次。同样适用于生成器表达式。
或者用列表
查看100:~/mypy$ g2=iter(range(10))
101:~/mypy$ list(g2)
Out[101]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
102:~/mypy$ list(g2)
Out[102]: []
在Python 3中,range
是范围对象,而不是列表。所以它是一个迭代器,每次使用时都会重新生成。
至于np.sum
,np.sum(range(10))
必须先制作数组。
在列表上操作时,Python sum
非常快,比np.sum
更快:
116:~/mypy$ %%timeit x=list(range(10000))
...: sum(x)
1000 loops, best of 3: 202 µs per loop
117:~/mypy$ %%timeit x=list(range(10000))
...: np.sum(x)
1000 loops, best of 3: 1.62 ms per loop
但是在数组上运行,np.sum
做得更好
118:~/mypy$ %%timeit x=np.arange(10000)
...: sum(x)
100 loops, best of 3: 5.92 ms per loop
119:~/mypy$ %%timeit x=np.arange(10000)
...: np.sum(x)
<caching warning>
100000 loops, best of 3: 18.6 µs per loop
另一个时间 - 制作阵列的各种方法。 fromiter
可能比np.array
更快;但内置arange
要好得多。
124:~/mypy$ timeit np.array(range(100000))
10 loops, best of 3: 39.2 ms per loop
125:~/mypy$ timeit np.fromiter(range(100000),int)
100 loops, best of 3: 12.9 ms per loop
126:~/mypy$ timeit np.arange(100000)
The slowest run took 6.93 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 106 µs per loop
如果您打算使用列表,请使用range
;但如果需要使用数组,请使用numpy's
自己的范围。创建数组时会产生开销,因此在处理大数组时它们更有价值。
==================
关于np.sum
如何处理迭代器的问题 - 它没有。看看np.array
对这样一个对象的作用:
In [12]: np.array(iter(range(10)))
Out[12]: array(<range_iterator object at 0xb5998f98>, dtype=object)
它生成一个带有dtype对象的单个元素数组。
fromiter
将评估此迭代:
In [13]: np.fromiter(iter(range(10)),int)
Out[13]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
在将输入转换为数组时, np.array
遵循一些复杂的规则。它主要用于工作,包括数字列表或嵌套的等长列表。
如果您对np
函数如何处理非数组对象有疑问,请首先检查np.array
对该对象的作用。