执行前编译
一般概览
下面提供了有关完整过程的当前实现的信息,该过程在用户编译新的AOT模块时在内部执行,例如,当他输入`using Foo’时。 最有可能的是,随着引入更高效的处理方法,这些信息将随时间而改变,因此当前的实现可能不完全匹配下面描述的数据流和功能。
代码图像的编译
首先,有必要定义应该编译成机器代码的方法。 这只能在编译代码实际运行时完成,因为需要编译的方法集取决于传递给方法的参数类型,并且具有某些类型组合的方法调用可能直到运行时才知 在此过程中,将跟踪编译器看到的确切方法以进行后续编译,从而创建编译跟踪。
目前,在编译映像时,Julia在不同的进程中开始跟踪生成,而不是在执行AOT编译的进程中。 这可能会影响预编译期间使用调试器的尝试。 要使用调试器调试预编译,最好使用rr调试器,记录整个过程树,使用`rr ps’确定相应的失败过程,然后使用`rr replay-p PID`仅重现失败过程。 |
定义要编译的方法后,传递’jl_create_system_image’函数。 此函数设置将机器代码序列化到文件时要使用的许多数据结构,然后使用方法数组调用`jl_create_native`。 'jl_create_native’为方法执行代码生成(codegen),创建一个或多个LLVM模块。 'jl_create_system_image’然后记录有关从模块生成代码时创建的内容的有用信息。
之后,模块与`jl_create_system_image`函数记录的信息一起传递给`jl_dump_native`函数。 'jl_dump_native’包含将模块序列化为位码,对象或程序集文件所需的代码,具体取决于传递给Julia的命令行参数。 然后将序列化的代码和信息作为存档写入文件。
最后一步是为使用’jl_dump_native’创建的存档中的目标文件运行系统链接器。 在此步骤结束时,将创建包含已编译代码的共享库。
方法的汇编
跟踪编译的方法
Julia有一个命令行标志,用于编写由JIT编译器编译的所有方法--'--trace-compile=filename'。 当一个函数被编译并且这个标志包含文件名时,Julia将输出一个预编译报告给这个文件,指出调用它的方法和参数的类型。 这将创建一个预编译脚本,可在稍后的aot编译过程中使用。 在包https://julialang …github.io/PrecompileTools.jl/stable /[PrecompileTools]包含为开发人员简化此过程的工具。
jl_create_system_image
`jl_create_system_image’保存后续恢复运行时状态所需的所有Julia特定元数据。 这包括代码实例、方法实例、方法表和类型信息等数据。 此函数还定义了将机器代码序列化为文件所需的数据结构。 最后,它调用’jl_create_native’来创建一个或多个包含传递方法的机器代码的LLVM模块。 `Jl_create_native’函数负责为传递给它的方法生成代码(codegen)。
'jl_dump_native`
'jl_dump_native’负责将包含机器码的LLVM模块序列化为文件。 除了模块之外,由’jl_create_system_image’创建的系统映像数据被编译为全局变量。 此方法的输出是包含系统映像的代码和数据的位码、对象和/或程序集的存档。 `jl_dump_native’通常是生成机器代码时最耗时的一个,大部分时间都花在优化LLVM的IR和创建机器代码上。 因此,该函数能够执行多线程优化和机器代码生成。 此多线程取决于模块的大小,但可以通过设置环境变量显式复盖。 'JULIA_IMAGE_THREADS'。 默认情况下,最大线程数等于可用线程数的一半,但如果设置较低的值,则可以减少编译期间的峰值内存消耗。
jl_dump_native还可以在与Julia加载器集成时创建针对各种体系结构优化的机器代码。 要运行,您需要设置一个环境变量。 'JULIA_CPU_TARGET'并在优化管道中间接执行多个版本控制传递。 为了使用多线程,在将模块拆分为在其自己的线程中创建的子模块之前,添加了一个注释步骤,该步骤使用整个模块中可用的信息来决定哪些函数应 注释后,单独的线程可以并行生成不同体系结构的代码,因为保证另一个子模块创建将被克隆函数调用的必要函数。
存档还包含有关模块序列化方式的一些其他元数据,例如用于序列化模块的线程数和编译的函数数。