可以提高计算速度:
- 通过提取公因子而不是重复计算来提高一点(在我的示例中提高了 36 倍)。
- 例如通过使用 numba 创建扩展模块,可以获得更大的加速(高达 100x-1000x)。
前言
我假设这是“在 sympy 中计算一次函数,然后在不同的项目中多次使用它”类型的案例。因此,包含一些手动复制粘贴和创建文件。 但是可以使用函数自动创建新文件,以及编译步骤,但我现在将其排除在外。
我遇到了类似的问题,我对不同的方法进行了一些基准测试。我使用的函数很长(len(str(expr)) = 45857),cse(expr) 将其分解为 72 个子表达式。在这里复制粘贴太长了,但这里是使用 sympy 创建的函数最多可提高 100x-1000x 速度的步骤。
基准测试
A) 评估单个浮点数
是时候为每个参数使用 一个浮点值 来评估函数了。使用timeit myfunc(*params)。
- 基线:使用
modules="numpy" 进行羔羊化处理:277µs
- (1) 将
str(expr) 复制粘贴到函数定义中:275µs(无差异)
- (2)
cse 之后的表达式复制粘贴:8.2 µs(33 倍改进)
- (3) 在
cse(optimizations="basic") 之后复制粘贴表达式:7.6µs(36 倍改进)
- (4) 使用numba编译代码为
func_numba_f():0.25µs(1090x提升)
- (5) 使用 sympy
autowrap:0.47 µs(589 倍改进)
B) 评估 1000 个浮点数的 np.array
- (1) 将
str(expr) 复制粘贴到函数定义:15100 µs |每个值 15.1µs
- (2) 在
cse 之后复制粘贴表达式:493 µs |每个值 0.49µs(提高 31 倍)
- (3)
cse(optimizations="basic") 之后的表达式复制粘贴:413µs |每个值 0.41µs(提高 37 倍)
- (4) 使用numba编译代码为
func_numba_arr(): 114µs |每个值 0.11µs(提高 132 倍)
- (5) 带有 np.vectorize 的 sympy 自动换行:480µs |每个值 0.48µs(提高 31 倍)
(1) 复制粘贴str(expr)
- 只需将表达式字符串复制粘贴到新函数中,然后返回值。
- 将函数保存在另一个文件中。
(2) cse 之后的表达式复制粘贴
- 想法:通过识别共同部分使代码更短。
- 首先,复制粘贴常用部分:
repl, redu = cse(K)
for variable, expr in repl:
print(f"{variable} = {expr}")
- 然后,复制粘贴返回值:
print(redu[0])。
- 创建另一个文件,然后粘贴到函数定义中
(3) 在cse(optimizations="basic") 之后复制粘贴表达式
- 与 (2) 相同,但使用
optimizations="basic"
- 这会创建比 (2) 略短的代码
(4)使用numba编译代码
- 使用numba.pycc.CC 编译代码。因此,如 (3) 所示,创建一个带有复制粘贴功能的函数
- 然后,使用代码创建
src_mymodule.py
from numba.pycc import CC
cc = CC("my_numba_module")
@cc.export("func_numba_f", "f8(f8, f8, f8, f8, f8)")
@cc.export("func_numba_arr", "f8[:](f8[:],f8[:],f8[:],f8[:],f8[:])")
def myfunc(x1, x2, x3, x4, x5):
# your function definition here
return value
if __name__ == "__main__":
cc.compile()
- 在函数
func_numba_f() 中有五个 浮点值输入变量和一个浮点值输出变量。 f8 表示浮动。
-
func_numba_arr() 是使用 dtype="float64" 或 dtype="float32" 处理 np.arrays 的版本,具体取决于您用于编译它的内容。
- 然后通过运行
python src_mymodule.py 编译一次代码。这将创建 my_numba_module.cp38-win_amd64.pyd 或类似的。它只能与文件名中的相同的 python 版本和位数一起使用。
- 然后,在另一个 python 文件中,您将导入函数并使用它们,例如:
from my_numba_module import func_numba_f, func_numba_arr
out = func_numba_f(4,3,2,1,100)
# or:
args = [np.array([x]*N, dtype='float64') for x in (4,3,2,1,100)]
out_arr = func_numba_arr(*args)
(5) 使用 sympy autowrap
from sympy.utilities.autowrap import autowrap
func = autowrap(expr, backend='cython')
- 通过指定
temp_dir参数,它保存了所有源文件(.c、.h、.pyx)和一个.pyd(win)/.so(unix)文件,以后可以用来导入函数on with(假设temp_dir 在sys.path 中):
from wrapper_module_1 import autofunc_c
- 这是迄今为止最简单的方法,尽管生成的 C 代码没有经过高度优化。如果需要,可以在其他步骤中重命名 autowrap 的输出。
- 该函数只接受标量,但可以用np.vectorize 向量化
func = np.vectorize(func)