提高从 Julia 中的字符串生成的 SymPy 函数的性能

Improving the performance of SymPy function generated from string in Julia

在 Julia 中使用 SymPy 转换表达式的字符串我注意到原生 Julia 函数 fast_fct 的实现与从细绳。有没有一种方法可以加快 SymPy 函数的速度,或者是否有一种不同的、更快的方法来实现相同的功能?

请赋予 功能 string_to_function

最小工作示例:

using SymPy

function string_to_function(fct_string)
    expression = SymPy.sympify.(fct_string)
    variables = free_symbols(expression)
    function(args...)
        subs.(expression, (variables .=> args)...)
    end
end

function fast_fct(x, y, z)
    return x + y + z
end

slow_fct = string_to_function("x + y + z")

基准测试

N = 100000
@time for i in 0:N
    x, y, z = rand(3)
    fast_fct(x, y, z)
end

@time for i in 0:N
    x, y, z = rand(3)
    slow_fct(x, y, z)
end

大致结果

>>>  0.014453 seconds (398.98 k allocations: 16.769 MiB, 40.48% gc time)
>>> 31.364378 seconds (13.04 M allocations: 377.752 MiB, 0.64% gc time, 0.41% compilation time)

实际上,通过适当的基准测试,差异甚至更大,因为您还测量了其他东西...

using BenchmarkTools
x, y, z = rand(3)
@btime fast_fct($x, $y, $z) #   4.500 ns (0 allocations: 0 bytes)
@btime slow_fct($x, $y, $z) # 162.210 μs (119 allocations: 3.22 KiB)

几点观察:

  • 我认为这个微基准测试没有多大用处,除非您真的对这些非常基本的基本操作感兴趣。当然,直接的 Julia 方式几乎是瞬时的,sympy 方式需要经过大量固定的计算成本。
  • 如果性能很重要,请查看 Symbolics.jl Julia 中符号计算的本机实现。这应该快得多(但是,对于像这样的例子,它永远不会接近......)。然而它很新,文档还没有 sympy 好。

为此,lambdify 中有一些示例。 Antonello 指出 Symbolics 可能更快——他们有更好的 lambdify 版本——但这里使用 @eval 可能已经足够好了:

julia> @btime fast_fct(x...) setup=(x=rand(3))
  86.283 ns (4 allocations: 64 bytes)
2.2829680705749293

julia> med_fct = lambdify(SymPy.sympify("x + y + z"))
#101 (generic function with 1 method)

julia> @btime med_fct(x...) setup=(x=rand(3))
  939.393 ns (16 allocations: 304 bytes)
1.5532948656814223

julia> ex = lambdify(SymPy.sympify("x + y + z"), invoke_latest=false)
:(function var"##321"(x, y, z)
      x + y + z
  end)

julia> @eval asfast_fct(x,y,z) = ($ex)(x,y,z) # avoid invoke_latest call
asfast_fct (generic function with 1 method)

julia> @btime asfast_fct(x...)  setup=(x=rand(3))
  89.872 ns (4 allocations: 64 bytes)
1.1222502647060117