Julia 中的解构、变量创建和代码优化

问题描述 投票:0回答:1

这更多的是关于 Julia 语言如何工作的问题,而不是技术问题。我发现了结构和元组解构,但我想知道有关内存分配的底层过程是什么。

我目前正在编写代码来运行涉及 ODE 求解的模拟。我将所有参数存储在一个元组中,该元组用作 ODE 函数

ODE_fun
的参数。我认为解构元组是使用元组元素子集的好方法,并且可以防止您在每次引用参数
tuple.a
时编写:
a
。但它每次都会创建一个新变量吗?如果是,它看起来不如调用
tuple.a
最优,因为它会在 ODE 求解算法的每次迭代时创建一个新的
a
变量。这对于包含许多参数和向量的元组可能很重要。或者它是否创建了一种指向
tuple.a
的指针?有没有一种方法可以以最佳方式使用解构?不幸的是,文档相当不清楚......

optimization julia tuples ode destructuring
1个回答
0
投票

如果

NamedTuple
argument
的类型在编译时已知,那么无论您执行
myfun(argument.a, argument.b)
还是
myfun(argument...)

都没有关系

说明

由于元组具有固定类型,Julia 的编译器可以处理该类型(前提是类型不模糊)。

考虑这两个函数和一个

NamedTuple
mypars
:

function f(pars)
    +(pars...)
end
function f2(pars)
    +(pars.a, pars.b)
end

mypars = (;a=1, b=4)

可以清楚地看到两者都是非分配的:

julia> @btime f($mypars)
  1.400 ns (0 allocations: 0 bytes)

julia> @btime f2($mypars)
  1.400 ns (0 allocations: 0 bytes)

现在让我们看看编译过程。降低的代码明显不同

julia> @code_lowered f(mypars)
CodeInfo(
1 ─ %1 = Core._apply_iterate(Base.iterate, Main.:+, pars)
└──      return %1
)

julia> @code_lowered f2(mypars)
CodeInfo(
1 ─ %1 = Base.getproperty(pars, :a)
│   %2 = Base.getproperty(pars, :b)
│   %3 = %1 + %2
└──      return %3
)

但是,当推断类型编译器意识到这些基本上是相同的:

julia> @code_typed f(mypars)
CodeInfo(
1 ─ %1 = Core.getfield(pars, 1)::Int64
│   %2 = Core.getfield(pars, 2)::Int64
│   %3 = Base.add_int(%1, %2)::Int64
└──      return %3
) => Int64

julia> @code_typed f2(mypars)
CodeInfo(
1 ─ %1 = Base.getfield(pars, :a)::Int64
│   %2 = Base.getfield(pars, :b)::Int64
│   %3 = Base.add_int(%1, %2)::Int64
└──      return %3
) => Int64

这又意味着 LLVM 将获得相同的代码进行编译:

julia> @code_llvm f(mypars)
;  @ REPL[1]:1 within `f`
; Function Attrs: uwtable
define i64 @julia_f_702([2 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(16) %0) #0 {
top:
;  @ REPL[1]:2 within `f`
  %1 = getelementptr inbounds [2 x i64], [2 x i64]* %0, i64 0, i64 0
  %2 = getelementptr inbounds [2 x i64], [2 x i64]* %0, i64 0, i64 1
; ┌ @ int.jl:87 within `+`
   %unbox = load i64, i64* %1, align 8
   %unbox1 = load i64, i64* %2, align 8
   %3 = add i64 %unbox1, %unbox
; └
  ret i64 %3
}

julia> @code_llvm f2(mypars)
;  @ REPL[2]:1 within `f2`
; Function Attrs: uwtable
define i64 @julia_f2_704([2 x i64]* nocapture noundef nonnull readonly align 8 dereferenceable(16) %0) #0 {
top:
;  @ REPL[2]:2 within `f2`
; ┌ @ Base.jl:37 within `getproperty`
   %1 = getelementptr inbounds [2 x i64], [2 x i64]* %0, i64 0, i64 0
   %2 = getelementptr inbounds [2 x i64], [2 x i64]* %0, i64 0, i64 1
; └
; ┌ @ int.jl:87 within `+`
   %unbox = load i64, i64* %1, align 8
   %unbox1 = load i64, i64* %2, align 8
   %3 = add i64 %unbox1, %unbox
; └
  ret i64 %3
}

您可以运行

@code_native f(mypars)
@code_native f2(mypars)
来发现生成的二进制文件是相同的。

© www.soinside.com 2019 - 2024. All rights reserved.