如何将 Symbolics.jl 表达式转换为函数并保证速度?

我想用 Symbolics.jl 做一些符号运算,然后把得到的表达式转换为函数,再后续使用。不过我用 build_function 生成函数后,速度只有直接定义函数的四分之一。请问这一点如何优化?谢谢大家!

下面是例子:

using Symbolics
using BenchmarkTools

@variables x y
f(x,y) = x^2 + sin(x+y)
D = Differential(x)
expr = expand_derivatives(D(f(x,y)))  # 输出:2x + cos(x + y)

# 由表达式生成函数
f_expr = build_function(expr, x, y, expression = Val{false})
# 直接定义函数
f_defn(x, y) = 2x + cos(x + y)

# benchmark
@btime for x in rand(100), y in rand(100)
	f_expr(x, y)
end
# 输出:239.600 μs (30101 allocations: 557.12 KiB)

@btime for x in rand(100), y in rand(100)
	f_defn(x, y)
end
# 输出:60.300 μs (101 allocations: 88.38 KiB)

我在 Julia Programming Language - A forum for users and developers (julialang.org) 里也问了 :joy:,得到了答案,供参考:

How to convert Symbolics.jl expressions to functions while keeping speed? - General Usage - Julia Programming Language (julialang.org)

2 个赞

我之前也遇到这个问题. 另外建议不要设置 expression = Val{false}, 否则调用 f_expr 会出现类型不稳定.例如

julia> function a(x)
           x .+ f_expr(x...)   
       end
a (generic function with 1 method)

julia> @code_warntype a([1.0,2.0])
MethodInstance for a(::Vector{Float64})
  from a(x) in Main at REPL[4]:1
Arguments
  #self#::Core.Const(a)        
  x::Vector{Float64}
Body::Any
1 ─ %1 = Core._apply_iterate(Base.iterate, Main.f_expr, x)::Any
│   %2 = Base.broadcasted(Main.:+, x, %1)::Any
│   %3 = Base.materialize(%2)::Any
└──      return %3