我需要计算一个二维的平均值。这里我保留了所有的行:
import numpy as np, time
x = np.random.random((100000, 500))
t0 = time.time()
y = x.mean(axis=0) # y.shape is (500,) as expected
print(time.time() - t0) # 36 milliseconds
当我筛选并选择一些行时,我注意到速度慢了8倍。所以我尝试了一个简单的测试,其中selected_rows
实际上是 * 所有行 *。但仍然慢了8倍:
selected_rows = np.arange(100000)
t0 = time.time()
y = x[selected_rows, :].mean(axis=0) # selecting all rows!
print(time.time() - t0) # 280 milliseconds! (for the same result as above!)
是否有办法加快选择某些行(selected_rows
)和计算.mean(axis=0)
的过程?
在selected_rows
=所有行的特定情况下,* 不 * 将执行速度降低8倍会很有趣。
3条答案
按热度按时间ohtdti5x1#
当你执行
x[selected_rows, :]
时,其中selected_rows
是一个数组,它会执行高级索引来创建一个 new 数组。相反,如果您执行了一个 slice 操作,则会创建原始数组的一个 view,这样花费的时间更少。例如:
不幸的是,没有一种好的方法可以将所有可能的
selected_rows
表示为切片,因此,如果您有一个不能表示为切片的selected_rows
,您没有任何其他选择,只能承受性能上的损失。这里的dankal444's answer对您的情况没有帮助,因为
mean
调用的轴是您首先要过滤的轴。然而,如果过滤轴和mean
轴不同,这是最好的方法--将新数组的创建保存到压缩一个轴之后。与基本切片相比,您仍然会受到性能影响。但是它没有在mean
调用之前进行索引时那么大。例如,如果您需要
.mean(axis=1)
,由此可见
mean
之前的索引是迄今为止最差的(t1
)mean
之前切片,因为您不必花费额外的时间计算不必要行的平均值(t3
)mean
之后的索引(t2
)和切片(t4
)都优于在mean
之前的索引,但不优于在mean
之前的切片k7fdbhmy2#
AFAIK,这是不可能的,只有在Numpy有效地做到这一点。第二个代码是缓慢的,因为**
x[selected_rows, :]
创建一个新的数组**(如PranavHosangadi所解释的,在一般情况下无法创建视图)。这意味着需要分配新的缓冲区,数据填充(由于写入分配高速缓存策略,导致在x86-64平台上读取它,并导致与系统分配器有关的慢速页面错误)从x
中读取,这些操作也必须从内存中读取。所有这些操作都非常昂贵,更不用说mean
函数然后从内存中读回新创建的缓冲区。Numpy还在C代码内部以次优方式执行这种奇特的索引操作(由于高级迭代器的开销、最后一个轴的小尺寸以及Numpy代码中需要优化的可能情况的数量)。(它们是为这样的使用情况设计的),但是所得到的性能将是相当令人失望的。Numba和Cython可以帮助加快速度。@dankal444使用Numba提供了一个非常快的串行实现。下面是一个更快的基于块的并行实现(也有点复杂):
以下是在我的计算机上使用i5- 9600 KF处理器(6核)的性能结果:
计算受内存限制:它使RAM带宽的吞吐量的大约80%饱和,这对于高级Numba/Cython代码几乎是最佳的。
zrfyljdw3#
(很抱歉此答案为第一个版本)
问题在于创建新数组,与计算平均值相比,这需要花费大量时间。
我尝试使用
numba
优化整个过程:与numpy相比,它的运行速度几乎没有变慢,但要慢得多(慢20%?)。编译后(第一次调用此函数),我得到了大致相同的时间。对于索引较少的数组,您应该会看到一些增益。