在2023年的最后几天,Julia语言发布了最新的版本1.10,新版本带来哪些新功能和改善呢?请和虫虫一起学习尝鲜一下。
安装新的1.10版本目前可以在官方版本页面发布,大家可以按照对应平台下载安装。
对于已有版本,可以使juliaup更新版本。
也可以使用命令行安装:
Window下:
winget install julia -s msstoreLinux和MacOS平台使用:
curl -fsSL install.julialang.org | sh自举新解析器在Julia1.10中一个很有意义的工作是,是全新的使用Julial编写的解析器(JuliaSyntax.jl),用来取代老版本的Scheme编写解析器。 至此,Julial实现了名义上的自举性。另外新版本编译器也在如下方面进行了完善:
更高的解析性能:新解析器在代码解析方面有着显著地性能提高。
更明确的错误提示:错误消息可提供更具体的信息,标明了语法问题的确切位置。
高级源代码映射:解析器生成表达式来跟踪它们在源代码中的位置,促进精确的错误定位,并可用于构建诸如linter之类的工具。
对于一个错误的代码:
count = 0;for i = 1:10 count++ end老版本中告警为:
ERROR: syntax: unexpected "end"1.10中的告警为
错误消息至少进行了三处改进:
突出显示有问题的文件位置。(REPL[9]:1:38表示第二个REPL条目、第一行和第38字符。如果代码在文件中运行,则这将替换为文件路径以及行号和字符号。)
在上下文中指出了具体的违规标记。
错误位置现在标记的清楚,在count++之后需要一个标识符(即变量名)。
包加载时间改进在1.9中,通过允许包作者在预编译期间保存本机代码,TTFX(第一次调用时间,第一次调用函数时获得结果所需的时间)得到了极大的改进。在1.10中,在包加载的性能方面也有了持续的改善。
改善重要通过分析和改进OmniPackage.jl的加载时间来优化的,OmniPackage.jl是一个人造的“巨型包”,其唯一目的是依赖和加载大量依赖项。OmniPackage.jl最终总共加载了大约650个包,其中许多包的大小都很大。优化主要进行了:
对类型系统的改进使其能够随着方法和类型数量的增加而更好地扩展。
减少触发不必要的重新编译的失效。
将包从Requires.jl移至包扩展(可以预编译)。
优化于mul!调度机制。
许多其他性能升级。
优化后,基准测试表明在同样硬件条件下,记载时间减少了一半(48s =)19s)。
堆栈跟踪渲染的改进当发生错误时,Julia会打印出错误以及“堆栈跟踪”,用来帮助调试和发现错误。Julia中的这些堆栈跟踪非常详细,包含方法名称、参数名称和类型以及方法在模块和文件中的位置等信息。然而,在涉及复杂参数类型的复杂场景中,单个堆栈跟踪帧可能会占据整个终端屏幕。在Julia1.10 版本中,新引入了改进,使堆栈跟踪不再那么冗长。
导致冗长堆栈跟踪的一个主要因素是参数类型的使用,尤其是当这些类型相互嵌套时。为了解决这个问题,REPL现在将参数缩写为{…}。用户可以使用以下命令查看完整的堆栈跟踪sho自动定义的命令errREPL中的变量。
其他改进还包括:
省略#unused#堆栈跟踪中的变量名称。例如,zero(#unused#::Type{Any})现在显示为zero(::Type{Any})。
简化函数调用中关键字参数的显示。 例如,f(;x=3,y=2)之前显示为f(; kw::Base.Pairs{Symbol, Int64, Tuple{Symbol, Symbol}, NamedTuple{(:x, :y), Tuple{Int64, Int64}}})在堆栈跟踪中。现在,它显示为 f(; kw::@Kwargs{x::Int64, y::Int64}), 和 @Kwargs扩展到以前的格式。
在同一位置折叠连续的帧。定义f(x, y=1)隐式定义了两个方法:f(x)(调用f(x, 1))和f(x, y)。方法为f(x)现在在堆栈跟踪中被省略,因为它的存在只是为了调用f(x, y)。
隐藏内部生成的方法,通常是为参数转发而创建的,并且具有晦涩的名称。
例如,对Julia代码:
f(g, a; kw...) = error();@inline f(a; kw...) = f(identity, a; kw...);f(1)在老版本中,该代码的堆栈跟踪显示为:
随着Julia1.10 的改进,堆栈跟踪现在更加简洁:
新的堆栈跟踪更短且更易于阅读。
并行垃圾回收(GC)在1.10中Julia的垃圾回收进行了重大性能改善,现在Julia GC支持并行化的标记阶段,还引入了与应用程序线程同时运行部分清理阶段的可能性。这样可以加快多线程分配繁重工作负载的GC时间。
并行化可以通过命令行选项启用多线程GC --gcthreads=M,
该参数指定GC的标记阶段要使用的线程数。还可以通过以下方式启用上面提到的并发页面扫描--gcthreads=M,1,意义MGC标记阶段将使用多个线程,一 GC 线程负责与应用程序同时执行部分清理阶段。
默认情况下,GC 线程数设置为计算线程数的一半( --threads)。
Tracy和英特尔VTune分析集成在新版本中Julia运行时集成了Tracy分析器以及英特尔VTune分析器功能。分析器现在能够报告值得注意的事件,例如编译、主要和次要GC、失效和内存计数器等。构建Julia时可以通过以下方式启用分析支持WITH_TRACY=1和 WITH_ITTAPI=1 make选项。
下面是在分析Julia运行时时使用 Tracy 的示例。
LLVM 15跟踪LLVM的版本更新,Julia 1.10中LLVM升级到了15。这带来了新处理器和一般现代化更新的配置文件。
特别值得注意的是新的pass-manager的迁移,有望改进编译时间。LLVM 15改进了对x86 上Float16的支持。
通过升级到 LLVM 15,在Linux上的aarch64 CPU上使用JITLink。该链接器首次在Juliav1.8中引入,但是仅支持Apple Silicon(macOS上的aarch64 CPU),它解决了影响该平台上Julia的频繁分段错误错误。然而,由于LLVM内存管理器中的错误,不平凡的工作负载可能会生成太多内存映射(mmap)可能超出允许的映射限制。
其他更新系统镜像和包镜像的并行本机代码生成公开并行性,可以加速LLVM编译预编译(AOT)通过。现在,该工作被分成多个较小的块,而不是编译一个大型的整体编译单元。这种多线程加速了系统镜像和大型包镜像的编译,从而缩短了这些镜像的预编译时间。
使用的并行度可以通过环境变量控制JULIA_IMAGE_THREADS=n。
注意由于Windows原生COFF二进制文件的限制,在Windows上编译大镜像时会禁用多线程。
在并行预编译期间避免竞争Julia早期版本中,使用同一软件仓库运行的多个进程都会竞相将包预编译到缓存文件中,从而导致需要完成额外的工作并可能损坏这些缓存文件。
1.10引入了一种“pidfile”(进程ID文件)锁定机制,该机制可以对其进行编排,让每一时刻只有一个Julia进程可以预编译给定的缓存文件,其中缓存文件特定于预编译期间目标的Julia设置。
这种安排既有利于可能同时运行多个进程的本地用户,也有利于可能在同一共享仓库中运行数百个工作程序的高性能计算用户。
运行时并行预编译尽管Pkg安装后自动并行预编译依赖项,预编译发生在using/importtime以前是串行的,一次预编译一个依赖项。
开发包时,用户最终可能会在加载期间遇到预编译,并且如果已开发包中的代码更改位于正在加载的包的依赖关系树中,则串行预编译过程可能会特别慢。1.10在加载期间引入了并行预编译,以捕获这些情况并更快地进行预编译。