dpy*_*yro 48 python performance numpy python-3.x
使用Python的本机sum函数和NumPy 之间在性能和行为上有什么不同numpy.sum?sum适用于NumPy的数组,numpy.sum可以在Python列表上运行,它们都返回相同的有效结果(没有测试边缘情况,如溢出)但不同类型.
>>> import numpy as np
>>> np_a = np.array(range(5))
>>> np_a
array([0, 1, 2, 3, 4])
>>> type(np_a)
<class 'numpy.ndarray')
>>> py_a = list(range(5))
>>> py_a
[0, 1, 2, 3, 4]
>>> type(py_a)
<class 'list'>
# The numerical answer (10) is the same for the following sums:
>>> type(np.sum(np_a))
<class 'numpy.int32'>
>>> type(sum(np_a))
<class 'numpy.int32'>
>>> type(np.sum(py_a))
<class 'numpy.int32'>
>>> type(sum(py_a))
<class 'int'>
Run Code Online (Sandbox Code Playgroud)
编辑:我认为我的实际问题是numpy.sum在Python整数列表上使用比使用Python自己更快sum吗?
另外,使用Python整数与标量有numpy.int32什么影响(包括性能)?例如,对于a += 1,如果类型a是Python整数还是numpy.int32?,是否存在行为或性能差异?我很好奇是否使用NumPy标量数据类型更快,例如numpy.int32在Python代码中添加或减去很多的值.
为了澄清,我正在进行生物信息学模拟,其部分包括将多维numpy.ndarrays 折叠成单个标量和,然后进行另外处理.我使用的是Python 3.2和NumPy 1.6.
提前致谢!
Aka*_*all 57
我好奇并定时了.numpy.sumnumpy数组似乎要快得多,但列表上要慢得多.
import numpy as np
import timeit
x = range(1000)
# or
#x = np.random.standard_normal(1000)
def pure_sum():
return sum(x)
def numpy_sum():
return np.sum(x)
n = 10000
t1 = timeit.timeit(pure_sum, number = n)
print 'Pure Python Sum:', t1
t2 = timeit.timeit(numpy_sum, number = n)
print 'Numpy Sum:', t2
Run Code Online (Sandbox Code Playgroud)
结果时间x = range(1000):
Pure Python Sum: 0.445913167735
Numpy Sum: 8.54926219673
Run Code Online (Sandbox Code Playgroud)
结果时间x = np.random.standard_normal(1000):
Pure Python Sum: 12.1442425643
Numpy Sum: 0.303303771848
Run Code Online (Sandbox Code Playgroud)
我使用的是Python 2.7.2和Numpy 1.6.1
MSe*_*ert 24
[...]我在这里的问题是
numpy.sum在Python整数列表中使用比使用Python自己更快sum吗?
这个问题的答案是:不.
Pythons sum在列表上会更快,而NumPys sum在阵列上会更快.我实际上做了一个基准来显示时间(Python 3.6,NumPy 1.14):
import random
import numpy as np
import matplotlib.pyplot as plt
from simple_benchmark import benchmark
%matplotlib notebook
def numpy_sum(it):
return np.sum(it)
def python_sum(it):
return sum(it)
def numpy_sum_method(arr):
return arr.sum()
b_array = benchmark(
[numpy_sum, numpy_sum_method, python_sum],
arguments={2**i: np.random.randint(0, 10, 2**i) for i in range(2, 21)},
argument_name='array size',
function_aliases={numpy_sum: 'numpy.sum(<array>)', numpy_sum_method: '<array>.sum()', python_sum: "sum(<array>)"}
)
b_list = benchmark(
[numpy_sum, python_sum],
arguments={2**i: [random.randint(0, 10) for _ in range(2**i)] for i in range(2, 21)},
argument_name='list size',
function_aliases={numpy_sum: 'numpy.sum(<list>)', python_sum: "sum(<list>)"}
)
Run Code Online (Sandbox Code Playgroud)
有了这些结果:
f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
b_array.plot(ax=ax1)
b_list.plot(ax=ax2)
Run Code Online (Sandbox Code Playgroud)
左:在NumPy数组上; 右:在Python列表上.请注意,这是一个对数 - 对数图,因为基准测试涵盖了非常广泛的值.但是对于定性结果:降低意味着更好.
这表明对于列表,Pythons sum总是更快,np.sum或者sum数组上的方法会更快(除了Pythons sum更快的非常短的数组).
如果你有兴趣将这些相互比较,我也制作了一个包括所有这些的情节:
f, ax = plt.subplots(1)
b_array.plot(ax=ax)
b_list.plot(ax=ax)
ax.grid(which='both')
Run Code Online (Sandbox Code Playgroud)
有趣的是,numpy可以在Python和列表上与数组竞争的点大约是200个元素!请注意,这个数字可能取决于很多因素,例如Python/NumPy版本......不要太过于字面意思.
没有提到的是这种差异的原因(我的意思是大规模的差异,而不是短列表/数组的差异,其中函数只是具有不同的常量开销).假设CPython是一个Python列表,它是一个包含Python对象(在本例中为Python整数)的C(语言C)数组的包装器.这些整数可以看作围绕C整数的包装(实际上并不正确,因为Python整数可以任意大,所以它不能简单地使用一个 C整数,但它足够接近).
例如,像[1, 2, 3]这样的列表(示意性地,我遗漏了一些细节)存储如下:
然而,NumPy数组是包含C值的C数组的包装器(在这种情况下int或long取决于32或64位并且取决于操作系统).
所以NumPy数组np.array([1, 2, 3])就像这样:
接下来要理解的是这些功能如何工作:
sum迭代迭代(在本例中为列表或数组)并添加所有元素.sum 方法迭代存储的C数组并添加这些C值,最后将该值包装在Python类型中(在本例中为numpy.int32(或numpy.int64)并返回它).sum 函数将输入转换为array(至少如果它不是数组),然后使用NumPy sum 方法.显然,从C数组中添加C值要比添加Python对象快得多,这就是为什么NumPy函数可以更快的速度(参见上面的第二个图,数组上的NumPy函数在大型数组中胜过Python总和).
但是将Python列表转换为NumPy数组相对较慢,然后您仍然需要添加C值.这就是为什么对于列表来说 Python sum会更快.
剩下的唯一悬而未决的问题是,为什么是蟒蛇sum上的array这么慢(这是最慢的所有功能相比).这实际上与Pythons sum简单迭代你传入的任何事实有关.如果是列表,它会获得存储的Python对象,但是在1D NumPy数组的情况下,没有存储的Python对象,只有C值,所以Python和NumPy必须为每个元素创建一个Python对象(一个numpy.int32或者numpy.int64),然后必须添加这些Python对象.为C值创建包装器的原因是它非常慢.
另外,使用Python整数与标量numpy.int32有什么影响(包括性能)?例如,对于a + = 1,如果a的类型是Python整数或numpy.int32,是否存在行为或性能差异?
我做了一些测试,并且对于标量的加法和减法,你应该坚持使用Python整数.即使可能存在一些缓存,这意味着以下测试可能不具有完全代表性:
from itertools import repeat
python_integer = 1000
numpy_integer_32 = np.int32(1000)
numpy_integer_64 = np.int64(1000)
def repeatedly_add_one(val):
for _ in repeat(None, 100000):
_ = val + 1
%timeit repeatedly_add_one(python_integer)
3.7 ms ± 71.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_add_one(numpy_integer_32)
14.3 ms ± 162 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_add_one(numpy_integer_64)
18.5 ms ± 494 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
def repeatedly_sub_one(val):
for _ in repeat(None, 100000):
_ = val - 1
%timeit repeatedly_sub_one(python_integer)
3.75 ms ± 236 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_sub_one(numpy_integer_32)
15.7 ms ± 437 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_sub_one(numpy_integer_64)
19 ms ± 834 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
使用Python整数进行标量运算比使用NumPy标量运算快3-6倍.我没有检查为什么会这样,但我的猜测是NumPy标量很少使用,可能没有针对性能进行优化.
如果您实际执行两个操作数都是numpy标量的算术运算,则差异会变小一些:
def repeatedly_add_one(val):
one = type(val)(1) # create a 1 with the same type as the input
for _ in repeat(None, 100000):
_ = val + one
%timeit repeatedly_add_one(python_integer)
3.88 ms ± 273 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_add_one(numpy_integer_32)
6.12 ms ± 324 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit repeatedly_add_one(numpy_integer_64)
6.49 ms ± 265 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)
然后它只慢了2倍.
如果你想知道为什么我itertools.repeat在这里使用,for _ in range(...)而我可以简单地使用.原因是repeat速度更快,因此每个循环的开销更少.因为我只对加法/减法时间感兴趣,所以最好不要让循环开销弄乱时间(至少没那么多).
请注意,多维 numpy 数组上的 Python sum 只会沿第一个轴执行求和:
sum(np.array([[[2,3,4],[4,5,6]],[[7,8,9],[10,11,12]]]))
Out[47]:
array([[ 9, 11, 13],
[14, 16, 18]])
np.sum(np.array([[[2,3,4],[4,5,6]],[[7,8,9],[10,11,12]]]), axis=0)
Out[48]:
array([[ 9, 11, 13],
[14, 16, 18]])
np.sum(np.array([[[2,3,4],[4,5,6]],[[7,8,9],[10,11,12]]]))
Out[49]: 81
Run Code Online (Sandbox Code Playgroud)
Numpy应该快得多,特别是当你的数据已经是一个numpy数组时.
Numpy数组是标准C数组上的薄层.当numpy sum迭代时,它没有进行类型检查,而且速度非常快.速度应与使用标准C进行操作相当.
相比之下,使用python的总和,它必须首先将numpy数组转换为python数组,然后迭代该数组.它必须进行一些类型检查,并且通常会变慢.
python总和慢于numpy sum的确切数量没有明确定义,因为与在python中编写自己的sum函数相比,python sum将是一个稍微优化的函数.
| 归档时间: |
|
| 查看次数: |
36858 次 |
| 最近记录: |