2

我目前正在优化我的代码的运行时间,但它仍然不在我希望的时间消耗范围内。我已经到了80% 的时间都花在对我的 sympy 矩阵表达式运行lambdify() 并在执行高斯求积时评估生成的 lambda 函数上。代码的所有其他方面都得到了充分优化,因此我希望有人可以帮助我优化我的lambdifying和评估sympy表达式的代码中的实质性“瓶颈”。

该代码是在具有 Python 3.5.2 的 64 位 Windows 7 机器上编写的(下面的示例说明了代码,在 Jupyter QtConsole 上执行)和以下模块版本:

  • 同情:1.0
  • 麻木:1.11.1
  • 麻木:0.27

羔羊()

我认为 lambdify() 使用大量时间的原因是 sympy 表达式的复杂性(这涉及 sympy Piecewise() 表达式的乘法)。这些表达式的简化是不可能的,因为它们是使用标准 Alpert 算法从勒让德缩放函数创建的小波函数。这里给出了一个这样的矩阵和时间比较的小例子,它与“更简单”的矩阵进行了比较:

from sympy import *
import numpy as np
import timeit

xi1 = symbols('xi1')
xi2 = symbols('xi2')
M = Matrix([[-0.0015625*(3.46410161513775*(0.00624999999999998*xi2 - 
           0.99375)*Piecewise((-1, 0.00624999999999998*xi2 - 0.99375 >= 0), 
           (1, 0.00624999999999998*xi2 - 0.99375 < 0)) + 
           1.73205080756888)*Piecewise((1, And(0.00624999999999998*xi2 - 
           0.99375 <= 1, 0.00624999999999998*xi2 - 
           0.99375 >= -1)), (0, True))], 
          [-0.00156249999999999*(0.0187499999999999*xi2 + 2.0*Piecewise((-1, 
           0.00624999999999998*xi2 - 0.99375 >= 0), (1, 
           0.00624999999999998*xi2 - 0.99375 < 0)) - 2.98125)*Piecewise((1, 
           And(0.00624999999999998*xi2 - 0.99375 <= 1, 
           0.00624999999999998*xi2 - 0.99375 >= -1)), (0, True))], 
          [-0.00270632938682636*xi1*(3.46410161513775*
           (0.00624999999999998*xi2 - 0.99375)*Piecewise((-1, 
           0.00624999999999998*xi2 - 0.99375 >= 0), (1, 
           0.00624999999999998*xi2 - 0.99375 < 0)) + 
           1.73205080756888)*Piecewise((1, And(0.00624999999999998*xi2 - 
           0.99375 <= 1, 0.00624999999999998*xi2 - 0.99375 >= -1)), (0, 
           True))]])
M_simpl = Matrix([(xi2**2),(xi2**2)*xi1,(xi2**2)*(xi1**2)])

时间比较产生:

import timeit

%timeit lambdify([xi1,xi2], M, 'numpy')
10 loops, best of 3: 23 ms per loop
%timeit lambdify([xi1,xi2], M_simpl, 'numpy')
100 loops, best of 3: 2.47 ms per loop

这表明更复杂的表达式的处理速度比更简单的 Matrix 慢了近 10 倍,当将 lambdify() 应用于其中几种类型的矩阵时,这对运行时做出了重大贡献。研究我在 sympy.utilities.autowrap 中了解到更快的 ufuncify() 函数的主题,这似乎在使用 Fortran 或 C++ 后端时效果最好。但是,在我的情况下,这不是最好的选择,因为该函数尚未扩展到 sympy 矩阵,我希望代码足够通用,其他适应代码的 Windows 用户不需要安装 C++ 编译器等。 所以,有没有在不使用其他编译器的情况下为这些类型的 sympy 表达式实现lambdify() 函数的加速?

Lambda 函数评估

上述 sympy 矩阵的lambdified 函数在特定坐标处的评估时也表现不同。这通过以下简单的 5 点正交示例进行说明:

# Quadrature coordinates
xi_v = np.array([[-1,-1], [-0.5,-0.5], [0,0], [0.5,0.5], [1,1]])
# Quadrature weights
w = np.array([3, 2, 1, 2, 3])

# Quadrature
def quad_func(func, xi_v, w):
    G = np.zeros((3, 1))
    for i in range(0, len(w), 1):
        G += w[i]*func(*xi_v[i,:])
    return G

# Testing time usage
f = lambdify([xi1,xi2], M, 'numpy')
%timeit quad_func(f, xi_v, w)
1000 loops, best of 3: 852 µs per loop
f_simpl = lambdify([xi1,xi2], M_simpl, 'numpy')
%timeit quad_func(f_simpl, xi_v, w)
10000 loops, best of 3: 33.9 µs per loop

我的第一直觉是从 numba 模块中引入 jit 以加快评估速度。然而,这导致了一个弹出窗口,指出 python 已停止工作,并且内核重新启动(对于 f 和 f_simpl 都会发生):

import numba

quad_func_jit = numba.jit(quad_func)
quad_func_jit(f, xi_v, w)

Kernel died, restarting

再说一遍,是否有办法加快这些 lambda 函数评估以减少总运行时间?或者可能是某种避免 numba.jit 崩溃的方法?

4

0 回答 0