numpy功能的时间效率组合

时间:2016-04-30 18:37:45

标签: python performance numpy

我有两个三维numpy数组A,B(大小〜(1000,1000,3) - >图像处理)和元素方面的函数。

这些功能是顺序的:

import numpy as np
A  = A ** 3
A = np.maximum(A, 0.001)
C = np.divide(B, A)

由于操作这3个命令的功能是时间要求严格的过程的瓶颈,我想问一下是否有办法通过单次访问内存中的每个元素来执行所有这些操作,即最快的性能。

我能找到的唯一组合是分隔部分,例如由于爱因斯坦总和,hereherethis one是特殊情况。

有没有办法在不需要编写自定义ufunc的情况下一次访问内存中的每个元素(从而节省时间)?

2 个答案:

答案 0 :(得分:3)

  

有没有办法在不需要编写自定义ufunc的情况下一次访问内存中的每个元素(从而节省时间)?

是的,这正是numexpr的设计目标。

import numpy as np
import numexpr as ne

def func1(A, B):
    A = A ** 3
    A = np.maximum(A, 0.001)
    return np.divide(B, A)

def func2(A, B):
    return ne.evaluate("B / where(A**3 > 0.001, A**3, 0.001)",
                       local_dict={'A':A,'B':B})

A, B = np.random.randn(2, 1000, 1000, 3)

print(np.allclose(func1(A, B), func2(A, B)))
# True

numexpr比原始代码提高了70倍:

In [1]: %%timeit A, B = np.random.randn(2, 1000, 1000, 3)
func1(A, B)
   ....: 
1 loop, best of 3: 837 ms per loop

In [2]: %%timeit A, B = np.random.randn(2, 1000, 1000, 3)
func2(A, B)
   ....: 
The slowest run took 8.87 times longer than the fastest. This could mean that an
intermediate result is being cached.
100 loops, best of 3: 11.5 ms per loop

部分原因是因为默认情况下numexpr将使用多个线程进行计算,但即使使用单个线程,它仍会压缩天真的矢量化:

In [3]: ne.set_num_threads(1)
Out[3]: 8

In [4]: %%timeit A, B = np.random.randn(2, 1000, 1000, 3)
func2(A, B)
   ....: 
10 loops, best of 3: 47.3 ms per loop

答案 1 :(得分:2)

坦率地说,@ali_m's solution中列出的numexpr单线程看起来像是要走的路,给定相关的加速数。在这篇文章中列出的保留NumPy的内容是另一种建议

让我们一次一个地指示这些指示,看看是否存在任何瓶颈 -

In [108]: # Random input arrays
     ...: A = np.random.rand(1000,1000,3)
     ...: B = np.random.rand(1000,1000,3)
     ...: 

In [109]: %timeit A**3
1 loops, best of 3: 442 ms per loop

In [110]: A  = A ** 3

In [111]: %timeit np.maximum(A, 0.001)
100 loops, best of 3: 16.4 ms per loop

In [112]: A = np.maximum(A, 0.001)

In [113]: %timeit np.divide(B, A)
10 loops, best of 3: 19.7 ms per loop

因此,似乎功率计算占据了总运行时间的很大一部分。

我们在那里介绍np.einsum,但请注意所涉及的数据类型。

In [114]: # Random input arrays
     ...: A = np.random.rand(1000,1000,3)
     ...: B = np.random.rand(1000,1000,3)
     ...: 

In [115]: %timeit A**3
1 loops, best of 3: 442 ms per loop

In [116]: %timeit np.einsum('ijk,ijk,ijk->ijk',A,A,A)
10 loops, best of 3: 28.3 ms per loop

In [117]: np.allclose(A**3,np.einsum('ijk,ijk,ijk->ijk',A,A,A))
Out[117]: True

那是一个很好的加速。