NumExpr作用:
通俗易懂的解释一下: 如 a+b+c 操作,一般情况是 a+b 是一个操作,把结果临时存起来 然后 再执行+c操作; 但是NumExpr 通过内置的一系列编译优化为 a+b+c; 节省了 中间的a+b的结果临时存起来这个操作,从而节省内存;
NumExpr在连续的数学运算上优化效果比较好;
安装方式:
pip install numexpr
适用范围:
支持运算符如下:
- 逻辑运算符:
&, |, ~
- 比较运算符:
<, <=, ==, !=, >=, >
- 一元算术运算符:
-
- 二进制算术运算符:
+, -, *, /, **, %, <<, >>
支持的函数如下:
where(bool, number1, number2): number
– number1 if the bool condition is true, number2 otherwise.{sin,cos,tan}(float|complex): float|complex
– trigonometric sine, cosine or tangent. [三角正弦、余弦或正切。]{arcsin,arccos,arctan}(float|complex): float|complex
– trigonometric inverse sine, cosine or tangent. [反三角正弦、余弦或正切]arctan2(float1, float2): float
– trigonometric inverse tangent of float1/float2. [float1/float2的三角反切线]{sinh,cosh,tanh}(float|complex): float|complex
– hyperbolic sine, cosine or tangent.[双曲正弦、余弦或正切]{arcsinh,arccosh,arctanh}(float|complex): float|complex
– hyperbolic inverse sine, cosine or tangent.[双曲反正弦、余弦或正切]{log,log10,log1p}(float|complex): float|complex
– natural, base-10 and log(1+x) logarithms.[自然对数、10进制对数和对数(1+x)]{exp,expm1}(float|complex): float|complex
– exponential and exponential minus one.[指数和指数减一]sqrt(float|complex): float|complex
– square root.[平方根]abs(float|complex): float|complex
– absolute value.[绝对值]conj(complex): complex
– conjugate value.[共轭值]{real,imag}(complex): float
– real or imaginary part of complex.[复数的实部或虚部]complex(float, float): complex
– complex from real and imaginary parts.[由实部和虚部复合而成]contains(str, str): bool
– returns True for every string inop1
that containsop2
.[对于op1中包含op2的每个字符串,返回True]
使用示例:
在一个复杂的有理函数表达式中加入更多的数组。假设,我们想计算下面涉及5个Numpy数组的值,每个数组都有100万个随机数(从正态分布抽取):
我们创建一个形状(1000000,5)的Numpy数组,并从中提取5个向量(1000000,1)用于有理函数:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import timeit
import numexpr as ne
import numpy as np
a = np.random.normal(size=(1000000, 5))
a1, a2, a3, a4, a5 = a[:, 0], a[:, 1], a[:, 2], a[:, 3], a[:, 4]
def tight_loop_very_slow():
"""
"""
c = (a1 ** 2 + 2 * a2 + (3 / a3)) / (np.sqrt(a4 ** 2 + a5 ** 2))
def tight_loop_slow():
"""
使用 numexpr 计算
"""
c = ne.evaluate("(a1**2+2*a2+(3/a3))/(sqrt(a4**2+a5**2))")
if __name__ == '__main__':
number = 100
x = timeit.timeit(stmt="tight_loop_very_slow()", number=number, globals=globals())
print(x)
x = timeit.timeit(stmt="tight_loop_slow()", number=number, globals=globals())
print(x)
运行结果如下:
发现 使用 NumExpr快了8倍;
总结:
- NumExpr虽然能通过减少临时变量的个数及利用多核cpu来提升速度;但是 注意 要结合实际的 被计算 的数据量进行评估; 如 矩阵元素数量过少的话,NumExpr自身的 字符串编译成矢量操作及 额外的代码 增加了 CPU的指令数,反而增加了耗时(相当于 耗时的装炮弹,结果是打蚊子); 因此 对于需要优化的代码 要结合数据量 并使用 timeit 进行最小改动 范围的测试比较;
- NumExpr所能操作的 numpy函数非常少; 使用范围有限;
- 如上代码示例中, 在使用 NumExpr时 sqrt 前面不要写成 np.sqrt 否则报错;
- 代码修改幅度较小;
相关链接:
https://numexpr.readthedocs.io/en/latest/index.html
https://blog.csdn.net/weixin_38754123/article/details/106935084