AnyMath 文档

推论;推论

推理的工作原理

在Julia编译器中,"类型推断"是指从输入值的类型中推导出后面值的类型的过程。 Julia的推理方法已在下面的博客文章中进行了描述:

调试编译器。jl

您可以启动Julia会话,编辑 编译器/*。jl (例如插入 印刷业 语句),然后替换 核心。编译器 在正在运行的会话中,通过导航到 基地 和执行 include("编译器/编译器。jl"). 这个技巧通常会导致更快的开发,而不是如果你为每个变化重建Julia。

或者,您可以使用https://github.com/timholy/Revise.jl[Revise.jl]包使用命令跟踪编译器更改 修改。轨道(核心。编译器) 在你的茱莉亚会议开始的时候。 如上所述https://timholy.github.io/Revise.jl/stable/[修订文档],对编译器的修改将在保存修改后的文件时反映出来。

推理的一个方便的切入点是 typeinf_code. 这是一个运行推理的演示 转换(Int,UInt(1)):

# Get the method
atypes = Tuple{Type{Int}, UInt}  # argument types
mths = methods(convert, atypes)  # worth checking that there is only one
m = first(mths)

# Create variables needed to call `typeinf_code`
interp = Core.Compiler.NativeInterpreter()
sparams = Core.svec()      # this particular method doesn't have type-parameters
run_optimizer = true       # run all inference optimizations
types = Tuple{typeof(convert), atypes.parameters...} # Tuple{typeof(convert), Type{Int}, UInt}
Core.Compiler.typeinf_code(interp, m, types, sparams, run_optimizer)

如果您的调试冒险需要 方法/方法,你可以通过调用查找 核心。编译器。专业知识 使用上面的许多变量。 A 代码信息 对象可用

# Returns the CodeInfo object for `convert(Int, ::UInt)`:
ci = (@code_typed convert(Int, UInt(1)))[1]

内联算法(内嵌性,内嵌性)

内联最难的工作有很多 ssa_inlining_pass!. 但是,如果你的问题是"为什么我的函数没有内联?"那么你很可能会对 内嵌性,内嵌性,它决定内联函数调用与否。

内嵌性,内嵌性 实现一个成本模型,其中"廉价"函数被内联;更具体地说,如果它们的预期运行时间与所需的时间相比不大,我们会内联函数https://en.wikipedia.org/wiki/Calling_convention[发出呼叫]给他们,如果他们没有内联。 成本模型非常简单,忽略了许多重要的细节:例如,所有 循环被分析,就好像它们将被执行一次,以及一个 如果。..别的。..结束 包括所有分支的总和成本。 同样值得承认的是,我们目前缺乏一套适合测试成本模型预测实际运行时成本的功能,尽管https://github.com/JuliaCI/BaseBenchmarks.jl[BaseBenchmarks]提供了大量关于内联算法任何修改的成功和失败的间接信息。

成本模型的基础是一个查找表,在 add_tfunc 和它的调用者,为Julia的每个内在函数分配估计成本(以CPU周期衡量)。 这些费用基于http://ithare.com/wp-content/uploads/part101_infographics_v08.png[通用体系结构的标准范围](参见https://www.agner.org/optimize/instruction_tables.pdf[Agner Fog的分析]以获取更多细节)。

我们用一些特殊情况来补充这个低级查找表。 例如,一个 :调用 表达式(预先推断所有输入和输出类型的调用)分配固定成本(当前为20个周期)。 相比之下,一个 :呼叫 expression,对于intrinsics/builtins以外的函数,表示调用将需要动态调度,在这种情况下,我们分配一个成本集 帕拉姆斯。内联-内联-内联 (目前设置为 1000). 请注意,这不是对动态调度的原始成本的"第一原则"估计,而是一个简单的启发式方法,表明动态调度非常昂贵。

每个语句在一个名为的函数中被分析其总成本 声明_cost. 您可以显示与每个语句关联的成本,如下所示:

julia> Base.print_statement_costs(stdout, map, (typeof(sqrt), Tuple{Int},)) # map(sqrt, (2,))
map(f, t::Tuple{Any}) @ Base tuple.jl:281
  0 1 ─ %1  = $(Expr(:boundscheck, true))::Bool
  0 │   %2  =   builtin Base.getfield(_3, 1, %1)::Int64
  1 │   %3  = intrinsic Base.sitofp(Float64, %2)::Float64
  0 │   %4  = intrinsic Base.lt_float(%3, 0.0)::Bool
  0 └──       goto #3 if not %4
  0 2 ─          invoke Base.Math.throw_complex_domainerror(:sqrt::Symbol, %3::Float64)::Union{}
  0 └──       unreachable
 20 3 ─ %8  = intrinsic Base.Math.sqrt_llvm(%3)::Float64
  0 └──       goto #4
  0 4 ─       goto #5
  0 5 ─ %11 =   builtin Core.tuple(%8)::Tuple{Float64}
  0 └──       return %11

行成本在左列。 这包括内联和其他形式的优化的后果。