Julia语言发布新版本1.8,亮点剖析

今日Julia官方宣布发布一个新的正式版本1.8。改版本的发布累积了3个beta和4个候选版本后,其完整的更新列表可以看官方发布文档,请和虫虫一起学习其两点新功能。

结构的const字段

新版中,Julia语法对mutable struct const字段前缀:

mutable struct Tx::Intconst y::Float64end

在新的语法结构下,字段y将为常量(在创建类型后不能重新分配)。该功能可以用来强制执行不变量,同时编译器也可以利用它来改进生成的代码。

调用@inline

在之前版本中,@inline宏只能用于方法定义,并且在该方法的所有调用点内联。但是,如果应该内联函数,则为给定的调用选择会很有用。新版本中,可以应用 @inline给定调用的宏作为@inline f(x)这将告诉编译器在该特定调用处内联该方法。

类型化的全局变量

Julia中的非常量全局变量会带来性能损失,因为编译器无法推断它们的类型,它们在运行时可以重新分配给其他类型的另一个对象。在Julia 1.8中,可以使用x::T语法T是全局的类型。尝试将变量重新分配给另一种类型的对象时出错:


julia> x::Int = 00julia> x = "string"ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64…

类型注释全局变量消除了使用非常量全局变量的大部分(但不是全部)成本。

新的默认调度程序@threads

在Julia中,@threads用于并行化的宏。甚至在Julia 1.3中引入通用并行任务运行时之前循环。由于历史原因,@threads一直在提供静态调度以避免意外依赖这种严格行为而破坏程序。

为了与多任务系统的其余部分很好地协同工作,在 Julia 1.5 中引入科:static调度程序以帮助将来更改默认调度行为做好准备;在Julia 1.8中,使用@threads可以充分利用动态和可组合的任务调度器。

作为对比,下面的程序模拟CPU密集型工作负载,需要seconds秒完成:

julia> function busywait(seconds)tstart = time_ns()while (time_ns() - tstart) / 1e9 < secondsendend;

Julia 1.8之前,@threads始终使用所有工作线程。 比如 @threads在完成之前的所有任务之前不要完成:


运行时间约为6 秒。 这意味着为内部创建的一项任务@threads等待任务busywait(5)去完成。

在 Julia 1.8 中

只需2秒,因为其中一个未占用的线程可以运行两次1秒的迭代来完成for循环。

profile

新的分配分析器

不必要的堆分配会严重降低性能,现有的跟踪它们的工具(即@time和--track-allocation) 并没有完全提供细粒度细节、漂亮的可视化和易用性。1.18中新创建了分配分析器(Profile.Allocs),它使用每个类型、大小和堆栈跟踪来捕获堆分配,以便可以使用 PProf.jl,如下所示,使用VS Code 的Julia扩展。

CPU 分析更新

线程和任务分析

CPU分析器现在收集每个样本的元数据,包括线程ID和任务ID,这样可以为特定线程或任务或其组生成分析报告。

默认打印选项保持不变,显示分组的所有线程和任务(为简洁起见,堆栈跟踪已被缩短)

请注意,现在每个线程都显示了一个利用率百分比,表明在采样期间唯一特别活跃的Julia 线程是线程 1。

外部配置文件查看器现在还支持线程和任务选择。


分析已运行的代码

1.8还引入了对已经运行的代码进行分析的选项。假设已经开始了一些长时间运行的代码,并且进展似乎很慢。通过发送触发1秒配置文件,可以随时查看正在发生的事情而不会中断 SIGINFO在包括 MacOS 在内的BSD上,或SIGUSR1在Linux 上(在Windows上不支持)。

将立即打印堆栈跟踪,然后在下一个屈服点进行配置文件采样后的报告。

这里,信号在执行后不久发送sleep.

包更新

包加载时间

添加了一个新工具,以深入了解加载依赖项对包加载时间的影响。

宏是 InteractiveUtils.@time_imports它在 REPL 中直接可用。

任何编译时间都将突出显示为加载时间的百分比,如果任何编译时间,正在重新编译无效方法 ,则将突出显示为编译时间的百分比。

带有可升级包指示器的Pkg状态更新

Julia中的包倾向于声明对其依赖版本的兼容性约束。 样做是为了确保最终得到一组应该可以很好地相互配合的版本。但是,这意味着有时包管理器可能不会为提供所有包的最新版本。可能没有最新版本的软件包的另一个原因是自从上次更新以来已经发布了新版本。例如,当遇到已在更高版本中修复的错误或包的文档与在本地运行的内容不同步时,这有时会令人困惑。

因此,包管理器现在将在安装包或使用状态输出时显示一个小指示器(pkg>st) 对于不在最新版本上的软件包。它还将尝试预测包是否有机会更新(pkg>up) 或者环境中的某些其他软件包“阻止它”(具有阻止软件包更新的兼容性限制)。

还有一个新标志 --outdated可以将其传递给状态打印以查看最新版本是什么以及哪些软件包的兼容性阻止了其他软件包的更新。

如上图,可以看到Plots和NanMath都不是最新版本,并且Plots和RecipesPipeline正在阻止NanMath 升级。

系统镜像的Pkg支持

使用PackageCompiler.jl包,可以创建一个带有包的“sysimage”,可以改善这些包的加载时间。这样做的一个缺点是,当使用带有包的自定义sysimage时,这些包的版本被“冻结”,因此无论环境中安装了什么包版本,最终都会加载sysimage中的版本。明确地说,如果 sysimage 中有版本0.1 的包,并且使用包管理器添加版本0.2,仍将使用版本 0.1。

在1.8中,包管理器了解何时使用自定义sysimage,并且不会为安装与加载的sysimage中不同版本的包。

改进的预编译

Julia通过将模块定义保存为第二种格式(缓存文件扩展名 .ji) 可以比原始源文件更快地加载。这些缓存文件包括包中的模块、类型、方法、全局变量和其他对象。为了减少第一次执行包方法的时间,开发人员长期以来一直可以选择保存一些类型推断的结果:可以添加显式 precompile指令或插入触发方法编译的小工作量。

不幸的是,较旧的 Julia 版本最终丢弃了很多编译后的代码:类型推断代码仅保存在包中定义的方法中,而对于所有其他方法,会被丢弃了。

Julia 1.8通过自动保存链接回包的所有类型推断代码来解决这两个限制:包拥有的方法或编译器可以证明由包中的方法调用的任何新推断代码。更具体地说,通过成功的类型推断链链接到包的所有代码都将被缓存;

Julia 仅丢弃仅由运行时调度调用的其他包中的方法的类型推断结果。

使用Julia 1.8,对于具有“可预测”类型的工作负载,通常可以完全消除类型推断作为延迟的来源。在使用十几个不同软件包的测试中,可以观察到初始工作负载的时间减少了百分之几到20 倍。

改进对苹果Silicon 支持

Julia 1.7提供了第一个实验性预览。虽然这通常适用于基本使用,但用户经常遇到分段错误,从而对体验产生负面影响。这些问题是由于 Julia 在内部如何使用LLVM来生成和链接该平台的代码,最终在 Julia 1.8 中通过迁移到更现代的链接器来,该链接器对macOS上的 RM CPU有更好的支持。 但是,此修复需要升级到LLVM 13,该更改无法向后移植到 v1.7 系列。 因此1.7 总是会受到Apple Silicon 频繁崩溃的影响。

在1.8 中,Apple Silicon 成为第2层支持的平台,现在在专用Apple Silicon机器上被持续集成 (CI) 覆盖。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章