Julia 1.7 版本发布,科学计算领域高性能语言
Julia 编程语言 1.7 版本已发布,这是一种通用的高性能语言,在科学计算和数值分析中较为流行。Julia 1.7 是 1.x 系列版本中的第七个次要版本,添加了一些新特性和功能,主要更改如下:
新的 Xoshiro256 系列随机数生成器(RNG)
Julia 一开始使用流行的 Mersenne Twister 算法作为其默认的随机数生成器,但 Mersenne Twister 的计算周期较长,而且会带来较大的开销。1.7 版本引进了开销较小的 Xoshiro256 系列随机数生成器。
新的线程功能
解决了运行时中的大量竞争条件,追踪了同步错误,改进了对多线程调度工作负载的支持,使默认的随机数生成器对线程更加友好,并添加了原子(atomic)作为原始语言功能。
- 现在为特定的字节大小定义了原子指针操作 ( #37847 )。
- 支持声明和使用可变结构的单个字段作为原子;查看新的@atomic宏 ( #37847 )。
- 如果
JULIA_NUM_THREADS环境变量设置为auto,则线程数将设置为 CPU 线程数 ( #38952 )。 - 每个
Task对象都有一个本地随机数生成器状态,默认情况下提供并行模拟代码的可重复执行(独立于调度)。默认生成器的并行速度也明显快于以前的版本 ( #40546 )。 - 任务现在可以在重新调度时在线程之间迁移。(以前任务始终在最先执行它的线程上运行)( #40715 )。
包管理器
自动安装包
如果注册表中有一个没有安装的包,那么当在 REPL 中尝试加载包时会自动安装。
之前:
julia> using Foo
ERROR: ArgumentError: Package Foo not found in current path:
- Run `import Pkg; Pkg.add("Foo")` to install the Foo package.
Stacktrace:
[1] require(into::Module, mod::Symbol)
@ Base ./loading.jl:871
(@1.6) pkg> add Foo
...
julia> using Foo
julia> Foo
Foo
现在轻松安装
julia> using Foo │ Package Foo not found, but a package named Foo is available from a registry. │ Install package? │ (@v1.7) pkg> add Foo └ (y/n) [y]: y ... julia> Foo Foo
默认情况下,软件包将安装到当前活动环境中,通过 y 或 return 键选择。要取消选择用 n 或 Ctrl-c。
新的清单格式
用户在 Julia 中添加包时,包管理器 (Pkg) 都会写出一个名为“清单("manifest")”的 TOML 文件,里面包含该包所有依赖项的准确版本。但不同的包版本可能与不同的 Julia 版本兼容,因此,之前不建议将在一个 Julia 版本中创建的清单与另一个 Julia 版本一起使用。
1.7 版中更改了此清单格式,以便所有依赖项都放在一个公共[deps]密钥下,这释放了全局命名空间,以便julia_version 可以添加条目 。读取新清单的能力也将向后移植到 Julia 1.6,因此在 Julia 1.6.2 及更高版本中,Pkg 将保留现有清单的格式,只有新建的清单才会采用新的清单格式。
推理改进
此版本附带了许多类型推断改进。通过这些改进,Julia 1.7 将更“聪明地”推断程序类型,以提高性能。
1.7 可以传播在过程间(inter-procedurally,即跨任何函数调用)派生的类型约束 isa 和 === 条件某些 Julia 程序的编写方式是:行为会根据运行时类型而改变,而这种改进的推论性能使这些程序运行得更快。例如,现在在 x === nothing和 isnothing(x) 之间没有推断性差异:
julia> code_typed((Union{Nothing,Int},); optimize=false) do x
return isnothing(x) ? 0 : x
end |> first
--- v1.6
+++ v1.7
@@ -1,6 +1,6 @@
CodeInfo(
1 ─ %1 = Main.isnothing(x)::Bool
└── goto #3 if not %1
2 ─ return 0
-3 ─ return x
-) => Union{Nothing, Int64}
+3 ─ return x::Int64
+) => Int64
这种过程间约束传播适用于任意泛型函数:
julia> ispositive(a) = isa(a, Number) && a > 0;
julia> code_typed((Union{Nothing,Int},); optimize=false) do x
return ispositive(x) ? x : 0
end |> first
--- v1.6
+++ v1.7
@@ -1,6 +1,6 @@
CodeInfo(
1 ─ %1 = Main.ispositive(x)::Bool
└── goto #3 if not %1
-2 ─ return x
+2 ─ return x::Int64
3 ─ return 0
-) => Union{Nothing, Int64}
+) => Int64
另一个显著变化是:Julia 1.7 可以用预先计算的常量替代更多的运行时计算,并通过在编译时解析条件分支来消除死代码。例如,在1.7中,特殊函数的计算可以在编译时完全折叠:
julia> code_typed((Int,)) do n
n + sin(sum(sincos(42))) # no runtime computation of `sum(sincos(42))` in 1.7!
end |> first
--- v1.6 +++ v1.7 @@ -1,32 +1,5 @@ CodeInfo( -1 ─ %1 = Base.muladd_float(0.16933292771007588, 2.7557313707070068e-6, -0.0001984126982985795)::Float64 -│ %2 = Base.muladd_float(0.16933292771007588, %1, 0.00833333333332249)::Float64 -│ %3 = Base.muladd_float(0.16933292771007588, 1.58969099521155e-10, -2.5050760253406863e-8)::Float64 -│ ... many runtime computations ... -│ %27 = invoke Main.sin(%26::Float64)::Float64 -│ %28 = Base.sitofp(Float64, n)::Float64 -│ %29 = Base.add_float(%28, %27)::Float64 -└── return %29 +1 ─ %1 = Base.sitofp(Float64, n)::Float64 +│ %2 = Base.add_float(%1, -0.9678422808766897)::Float64 +└── return %2 ) => Float64
以下是此版本的推理改进 PR 列表:
这些推理改进最初是由 JET.jl 的需求推动的,JET.jl 是 Julia 的静态分析器,由 Julia 编译器的类型推理实现提供支持。1.7 中的这些推理改进使 JET 能够更准确、更快地分析程序。
多维数组改进
多维数组,尤其是 3 维或更多维的数组,是科学编程和机器学习的有用结构。在 Julia v1.7 中添加了相关语法:能够为多维数组编写文字。这种新语法使 Julia 中的多维数组比以前更容易操作
Julia v1.7: [1 2 ; 3 4 ;;; 5 6 ; 7 8] or [1 ; 3 ;; 2 ; 4 ;;; 5 ; 7 ;; 6 ; 8] Python with Numpy: import numpy as np np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) MATLAB: A = [1 2; 3 4] A(:,:,2) = [5 6; 7 8] R: array(c(1, 3, 2, 4, 5, 7, 6, 8), dim = c(2, 2, 2))
该语法是当前语法的直接扩展:一个额外的分号 == 一个额外的维度:
julia> [1 2 ; 3 4]
2×2 Matrix{Int64}:
1 2
3 4
julia> [1 2 ;;; 3 4]
1×2×2 Array{Int64, 3}:
[:, :, 1] =
1 2
[:, :, 2] =
3 4
julia> [1 2 ;;;; 3 4]
1×2×1×2 Array{Int64, 4}:
[:, :, 1, 1] =
1 2
[:, :, 1, 2] =
3 4
julia> using BenchmarkTools
julia> @btime [1 2 ;;;; 3 4];
44.838 ns (2 allocations: 160 bytes)
julia (v1.6)> @btime cat([1 2], [3 4], dims = 4); # clear, but slow, and gets worse with more dimensions
1.380 μs (23 allocations: 1.05 KiB)
julia (v1.6)> @btime reshape([1; 2; 3; 4], (1, 2, 1, 2)); # fast, but intent less clear
65.884 ns (2 allocations: 192 bytes)
对于多维数组基本操作有性能的显着改进,随着涉及更多维度,差异会大大提高。
为了便于阅读较大的数组表达式,也可以接受换行符:
julia> [ 1 2
3 4
;;;
5 6
7 8 ]
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 2
3 4
[:, :, 2] =
5 6
7 8
此语法还可以按列优先顺序而不是行优先顺序编写数组,使用;;行代替空格:
julia> [1 ; 2 ;; 3 ; 4 ;;; 5 ; 6 ;; 7 ; 8]
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 3
2 4
[:, :, 2] =
5 7
6 8
1.7 版本是一个大版本更新,除了以上内容,此版本还包含一些语言功能和语法的改动,更多亮点更新可阅览官方公告,完整修改列表可在 GitHub 查看。