Engee 文档

结论

输出操作原理

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

调试编译器。jl

您可以启动Julia会话,编辑’编译器/*。jl’文件(例如,插入’print’语句),然后替换’Core。编译器’在运行会话中通过转到’base’并执行’include("编译器/编译器。jl")`。 这有助于更快地开发,而不是在每次更改时重建Julia。

或者你可以使用一个包https://github.com/timholy/Revise …​jl[修订。jl]使用`Revise跟踪编译器更改。轨道(核心。编译器)'在Julia会话开始时。 正如在解释https://timholy …​github.io/Revise.jl/stable/[查看文档],保存修改后的文件后会反映编译器的变化。

类型推断的一个方便的入口点是’typeinf_code'。 下面是一个示例,演示`convert(Int,UInt(1))'中的输出执行。

# Получаем метод
atypes = Tuple{Type{Int}, UInt}  # типы аргументов
mths = methods(convert, atypes)  # стоит проверить, что существует только один
m = first(mths)

# Создаем переменные, необходимые для вызова `typeinf_code`
interp = Core.Compiler.NativeInterpreter()
sparams = Core.svec()      # у этого конкретного метода нет параметров типа
run_optimizer = true       # выполнить все оптимизации вывода
types = Tuple{typeof(convert), atypes.parameters...} # Tuple{typeof(convert), Type{Int}, UInt}
Core.Compiler.typeinf_code(interp, m, types, sparams, run_optimizer)

如果调试需要`MethodInstance`的实例,可以通过调用`Core’方法找到它。编译器。specialize_method’并使用上述许多变量。 'CodeInfo’对象可以如下获得:

# Возвращает объект CodeInfo для `convert(Int, ::UInt)`:
ci = (@code_typed convert(Int, UInt(1)))[1]

嵌入算法’inline_worthy`)

大多数最困难的嵌入工作都是在’ssa_inlining_pass中完成的!`. 但是,如果您想找出函数未嵌入的原因,您很可能会对`inline_worthy`算法感兴趣,该算法决定是否嵌入函数调用。

'inline_worthy’实现了嵌入"廉价"功能的成本模型。 特别是,我们嵌入函数,如果它们的估计执行时间与完成它们所需的时间相比较短。 https://en.wikipedia.org/wiki/Calling_convention [呼叫]如果它们没有嵌入。 成本模型非常简单,忽略了许多重要的细节:例如,所有`for`循环都被分析,好像它们将被执行一次,以及`if的成本。..别的。..end'包括所有分支的总成本。 同样值得认识到的是,我们目前没有一组适合测试成本模型在运行时预测实际成本的能力的函数,尽管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分析])。

我们用一些特殊情况来补充这个低级搜索表。 例如,表达式`:invoke'(预先定义了所有输入和输出类型的调用)被分配一个固定成本(当前为20个周期)。 相反,对于内部或内置函数以外的函数的表达式`:call’表示调用将需要动态调度,在这种情况下,我们使用`Params分配值集。inline_nonleaf_penalty'(当前设置为`1000`)。 请注意,这不是动态调度的初始成本的"一线"估计,而仅仅是指示动态调度的极端高成本的启发式。

在名为`statement_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  = Base.getfield(_3, 1, %1)::Int64
  1 │   %3  = Base.sitofp(Float64, %2)::Float64
  0 │   %4  = Base.lt_float(%3, 0.0)::Bool
  0 └──       goto #3, если не %4
  0 2 ─       invoke Base.Math.throw_complex_domainerror(:sqrt::Symbol, %3::Float64)::Union{}
  0 └──       unreachable
 20 3 ─ %8  = Base.Math.sqrt_llvm(%3)::Float64
  0 └──       goto #4
  0 4 ─       goto #5
  0 5 ─ %11 = Core.tuple(%8)::Tuple{Float64}
  0 └──       return %11

该行的成本显示在左列中。 这包括嵌入和其他形式的优化的后果。