Numpy向量操作的并行化
NumPy是一个包含多维数组对象以及数组处理程序集合的库。正如你所知道的,它并不进行并行操作,但是并行执行操作可以为我们提供很大的性能优势。我们将使用numexpr库来实现NumPy的并行操作。
NumExpr
NumExpr是NumPy的一个快速数字表达式评估器。作用于数组的表达式,如3a+4b(其中a和b是数组),用它可以加速,并且比在Python中进行时占用的内存更少。此外,它的多线程能力可以利用所有的核心,与NumPy相比,性能有了显著的提升。
为了我们的需要,我们将使用numexpr包中的evaluate函数。
语法: numexpr.evaluate(ex, local_dict=None, global_dict=None, out=None, order=’K’, casting=’safe’, **kwargs)
参数:
- ex: 一个字符串形成一个涉及操作数和运算符的表达式
- local_dict:一个字典,用于替换当前帧中的本地操作数。
- global_dict:一个字典,用于替换当前帧中的全局操作数。
- out: 一个现有的数组,结果将被存储在这里。需要注意确保这个数组的大小与结果数组的大小一致。
- order: 控制操作数的迭代顺序。
- casting: 控制在进行复制或缓冲时可能发生的数据转换。
返回:结果数组
示例 1:
在这个例子中,我只使用了evaluate函数的ex参数来执行a和b之间的加法。
import numpy as np
import numexpr as ne
a = np.array([[6, 6], [0, 7]])
b = np.array([[19, 17], [13, 19]])
print(ne.evaluate('a+b'))
输出:
[[25. 23.]
[13. 26.]]
示例 2:
在这个例子中,我用local_dict参数和ex一起,从局部范围传递c和d的操作数。
import numpy as np
import numexpr as ne
a = np.array([[6, 6], [0, 7]])
b = np.array([[19, 17], [13, 19]])
print(ne.evaluate('c+d', local_dict={'c': a, 'd': b}))
输出:
[[25. 23.]
[13. 26.]]
示例 3:
在这个例子中,我用global_dict参数来传递d的操作数,而用local_dict参数来传递ex参数中c的操作数。
import numpy as np
import numexpr as ne
a = np.array([[6, 6], [0, 7]])
def calc():
b = np.array([[19, 17], [13, 19]])
out = np.zeros((2, 2))
ne.evaluate('c+d', local_dict={'c': b},
global_dict={'d': a}, out=out)
print(out)
calc()
输出:
[[25. 23.]
[13. 26.]]
对比性能
现在,让我们把我们的学习成果用于测试。在下面的例子中,我们将使用NumPy和NumExpr进行同样的操作,并使用timeit对其进行计时。
示例 1:
我们将执行一个简单的代数表达式,涉及对两行向量的加法和乘法,每个向量的大小为1000000。为了做到这一点,我们使用了NumPy包中的加法和乘法函数,并从NumExpr中评估,以模仿它。
import numpy as np
import numexpr as ne
a = np.arange(1000000)
b = np.arange(1000000)
timeit np.add(np.multiply(2, a), np.multiply(4, b))
timeit ne.evaluate('2*a+4*b')
输出:
输出
示例 2:
在这个例子中,我们将使用NumPy库中的sin函数,以及使用NumExpr的对应函数对一个1000000大小的行向量进行评估。
import numpy as np
import numexpr as ne
a = np.arange(1000000)
timeit np.sin(a)
timeit ne.evaluate('sin(a)')
输出:
输出
示例 3:
在这个例子中,我们将使用NumPy库中的cos函数及其对应的NumExpr的evaluate函数来处理一个(10000, 10)大小的矩阵。
import numpy as np
import numexpr as ne
a = np.random.randint(0, 1000, size=(10000, 10))
timeit np.cos(a)
timeit ne.evaluate('cos(a)')
输出:
输出