1. 首页
  2. >
  3. 编程技术
  4. >
  5. Python

Julia 1.6发布,亮点功能介绍

日前,Julia语言官方发布了新的版本1.6。据悉该版本有可能是下一个Julia的长期支持(LTS)版本。那么该版本带来了哪些亮点呢?下面一起学习一下。

Julia 1.6发布,亮点功能介绍

概述

作为一个集万种风华于一体的明星语言,一直是语言界一个特别生,特别吸引眼球。他将C语言的速度、Ruby的灵活、Python的易用性全整合在一起,支持并行处理,易于学习和使用,尤其适合科学和工程计算该版本。此次推出的1.6版本更是在以下方面做了大幅度的优化。

  1. 优化编译:

通过并行预编译,编译时间百分比显示,消除不必要的重新编译,减少编译器延迟,优化程序包延迟的工具。

3、二进制加载加速。

4、加载和网络选项。

5. 改进的堆栈跟踪格式。

编译优化

并行预编译

执行模块中的所有语句通常涉及编译大量代码,因此Julia一般都会创建模块的预编译缓存以减少此时间。在1.6中,该软件包的预编译速度更快,预编译发生在切出pkg>模式时候发生。

之前预编译只支持单线程序列形式,在代码加载过程中需要打包时,需要一个一个顺次预编译依赖项。

在1.5以及更老版本中,以一个DifferentialEquations(一个流行的软件包,具有大量依赖为例:

Julia 1.6发布,亮点功能介绍

在1.6中,pkg>模式增加了高度并行化的预编译操作,该操作在程序包操作后自动调用,以使活动环境随时可以加载。

Julia 1.6发布,亮点功能介绍

1.5的DifferentialEquations代码加载预编译过程需要大约8分钟的时间才完成预编译和加载,而且整个过程没有进度条显示。

在1.6并发模式下,仅仅花了大概1分钟多的时间进行预编译,同时显示了进度。 之后,第一次加载程序包时,它将以全速加载。新的并行预编译过程采用深度优先的方法来遍历清单中的依赖树,首先对没有依赖项的程序包进行预编译,然后向上处理环境文件中列出的程序包。Project.toml,允许同时预编译多个软件包。 操作是多进程的,而不是多线程的,因此不受Julia线程数的限制。默认情况下,Julia将根据CPU内核数生成最大的CPU平衡包预编译作业负载。预编译期间的错误只会抛出Project中列出的软件包,以允许可能在清单中列出但未加载的依赖项,并且自动预编译过程会记住在给定环境中某个软件包是否出错,并且在更改之前不会重试。自动预编译可以通过ctrl-c正常中断,在配置中,可通过设置环境变量JULIA_PKG_PRECOMPILE_AUTO=0来禁用。

Julia 1.6发布,亮点功能介绍

编译时间百分比

一个小的更改对新手的使用:即计时宏 @time 和@timev,现在用来报告是编译上花费的任何时间。

julia> x = rand(10,10);

julia> @time x * x;

0.540600 seconds (2.35 M allocations: 126.526 MiB, 4.43% gc time, 99.94% compilation time)

julia> @time x * x;

0.000010 seconds (1 allocation: 896 bytes)

鉴于Julia的Just In Time(JIT/Just Ahead of Time(JAOT)编译,第一次运行代码时,编译开销通常很可观,在后续调用中可以看到很大的速度改进。这个更改突出了该行为,既可以作为提醒,也可以作为消除不必要的编译工作(即过度专业化的代码)的工具。

消除不必要的重新编译

Julia的最强大功能之一是其可扩展性:可以将新方法添加到先前定义的函数中,并在新类型上使用先前定义的方法。但是这些新实体可能会迫使Julia重新编译代码以解决分发中的更改。这分为两个步骤:首先,将“过时”的代码失效 ,将其标记为不适合使用;其次,根据需要再次考虑新的方法和类型,从头开始编译代码。

Julia的早期版本有些保守,在某些情况下,没有实际标记分派,因此会使旧代码无效。而且,在许多地方,Julia及其标准库的编写方式都打破了Julia的类型推断。由于编译器有时仅由于新方法而不得不使代码无效,因此可能应用有关类型的任何不确定性都会放大无效的风险和频率。在Julia的旧版本中,这些影响的组合使无效化变得很普遍:仅加载某些软件包会导致Julia预先编译的代码中多达10%的会无效。重新编译的延迟有时会使交互式会话感觉迟钝。当Julia的程序包加载代码中发生无效时,它还会延迟下一个程序包的加载,从而导致长时间的等待。

在1.6版中,使旧代码无效的方案已变得更加准确和具有选择性。而且,Julia及其标准库得到了彻底的改头换面,以帮助类型推断有效地执行。这样Julia变得更精简,更快,它对方法的无效性更加不渗透,并且在交互式会话中感觉更加灵敏且敏捷。

减少编译器延迟

除了使库代码对编译器更友好之外,通过尝试加快编译器本身的速度。

在1.6之前,Julia以排序的顺序存储方法。还通过在插入时确定模棱两可的方法,希望避免在以后的每个查询中重复进行该工作。不幸的是,对部分订单进行排序需要二次时间,并且该时间在程序包加载期间开始显着显示(当需要将程序包的方法插入当前活动的方法表中时)。

新版本中通过使过程变得惰性,将排序和歧义检测移到用于查找匹配方法的算法中来改进了事情。该算法运行非常频繁,乍看之下,更改好像没有什么用。但是,绝大多数查询针对的是足够具体的类型,因此可以轻松消除大多数可能的匹配。

主要可见改进是打包加载,除了解决无效性之外,还增加了一些额外的速度。

为了完善推理质量特性通过努力,既可以在认为不利的情况下迅速停止分析,也可以在可能的情况下提取更精确的信息。对于复杂的代码(例如绘图库)来说,这两个方面都可以带来很大的好处,该库可以分支到大量不同的配置选项。

除了更新Julia版本之外,无需对代码进行任何操作即可轻松获得其中的大部分好处。

还有一个用于分析编译时间的通用框架,用于调查哪些功能对执行延迟的影响最大。

还对许多内部数据结构进行了许多微优化。这些不会再次影响代码的工作方式,但是应该改善代码的动态执行方式。例如,invokelatest 现在比try几乎与动态调度一样快。几个复杂的内部数据结构(也就是树)也变成了简单的哈希表,不仅改善了扩展性能,而且使线程安全性降低了。这会影响某些关键领域,例如类型分配(apply_type和tuple),方法优化查找(MethodInstance),然后分派(jl_apply_generic)。

优化程序包延迟的工具

Julia 1.6可以配合SnoopCompile v2.2.0或更高版本使用,提供用于编译器自省的新工具,尤其是(但不仅限于)用于类型推断的工具。开发人员可以使用新工具来分析类型推断,并确定特定的包实现选择如何与编译时间交互。采用者已使用这些工具消除了百分之几到大部分的首次使用延迟。

二进制加载加​速

为打包提供可靠的便携式二进制文件是所有包装环境都必须面对的挑战,尽管Julia的策略始终是优先考虑可靠性和可重复性,而不要考虑所有其他问题,但过去这需要付出一定的代价。对于可靠性和可重复性问题,Julia解决方案是通过BinaryBuilder.jl框架更充分地隔离已安装的二进制文件。在该框架构中使用JLL软件包建类库,这些软件包提供了标准化的API,Julia软件包可以使用这些API来访问提供的二进制文件。这种易用性和安装可靠性导致大大提高了打包的性能。

对比说明,在Julia 1.4中,加载GTK+3堆栈通常要花费左右7秒,应用需要500毫秒。使用Julia v1.6时,同样的机器,该库堆栈应用只需不到200毫秒。

另一个主要问题是由这么多小型JLL软件包提供绑定所产生的消耗;当JLL软件包足够轻巧以至于不影响整体加载时间,需要消除代码推断,代码生成和数据结构加载。通过实验发现程序包加载时间的最大消耗源于后端信息的反序列化,即后端功能中的链接。Base返回到软件包,如果存在影响到该软件包的无效操作,则将导致函数重新编译 Base功能。看起来似乎违反直觉,只是简单地使用了Base可以非常迅速地软件包增加预编译缓存文件,从而导致加载时间增加。虽然单个包增加本身很小(最坏情况下需要3-10ms ),但是,加载数十个JLL软件包时,累加效应很明显。

通过精简JLL程序包的工作导致创建了一个新程序包JLLWrappers.jl。该包提供了自动生成JLL程序包所需的绑定的宏,并通过使用尽可能少的功能和数据结构来实现。通过限制后端和数据结构的数量,以及集中每个JLL软件包使用的模板代码段,可以极大地缩短加载时间,而且还可以改善编译时间。另外,可以直接在JLL包API中进行改进 JLLWappers.jl无需重新部署数百个JLL。由于这些JLL包仅定义了简单,轻量级函数(用于加载库和返回路径等)的瘦打包,因此它们无法从大多数Julia代码进行的繁琐优化中受益。

Julia 1.6发布,亮点功能介绍

原始非JLLWrapperized的加载时间的加快 GTK3_jll。从高峰期开始打包,Julia v1.4下的6.73的秒数降至Julia v1.6的2.34,这完全是编译器改进。

使用所有相关JLLWrappers软件包的精简JLLWrappers实现,可进一步减少加载时间,使之压缩到140ms。

下载和网络选项

在老版本中,需要使用Base.download或者Pkg在Julia中下载包,这实际的下载是通过某种外部流程完成的,取决于系统,可能为curl, wget, fetch 或者 PowerShell。尽管此方法大部分很有效,但此方法仍存在一些主要缺点:

太慢。为每次下载启动一个新的过程非常昂贵;更糟糕的是,这些进程无法共享TCP连接或重用已协商的TLS连接,因此每次下载都需要进行TCP SYN/ACK,然后还要进行TLS秘密握手,所有这些都花费大量时间。

不一致的。由于下载内容的确切方式取决于系统上要安装的内容,因此下载行为非常不一致。在一个系统上进行的下载可能无法在另一系统上进行。此外,任何人可能遇到的任何问题,都不可避免地超出了Julia解决的范围,对于使用Julia的人来说,这只是一个非常理想的解决方案,它只希望能够下载内容。

不灵活。下载内容的核心要求很简单:URL输入,文件输出。但是也许需要在请求中传递一些自定义标头。或者,也许需要查看返回了哪些标头。通常,希望显示大下载的进度。一些下载命令具有其中一些选项,但只能支持所有下载方法都支持的选项,这迫使下载变得非常不灵活。

在Julia 1.6中,所有包下载都统一到libcurl-7.73.0库,通过新的Downloads.jl标准库调用。下载是在过程中完成的,其中TCP+TLS连接被共享和重用。如果服务器支持HTTP/2,则对该服务器的多个请求甚至可以复用到同一HTTPS连接上。这样,将保证下载更有效,更快。

由于统一了下载方法,因此,如果它可以在一个系统上运行,则很可能在任何地方都可以运行。 不会因为系统curl版本很旧而导致下载中断。libcurl高度可配置,可以将自定义标头与请求一起传递,查看响应中包含哪些标头,并获得下载进度,所有地方都以相同的方式进行。

作为全新下载的一部分,已在macOS和Windows上使用内置的TLS堆栈,该协议允许下载使用内置的机制,通过系统的证书颁发机构根证书集合来验证TLS服务器的身份。在Linux和FreeBSD上,还查找带有CA根证书的PEM文件的标准位置。 使用系统CA根证书的优势在于,大多数系统会自动使这些CA根保持最新。在Windows和macOS上,操作系统将在执行证书验证时检查已撤销的证书(Linux没有标准方法)。Julia本身仍然附带了相当最新的CA根列表,但是默认情况下,除非找不到系统CA根列表,才会启用。

如果由于某些原因无法解决问题,Julia 1.6还会引入 NetworkOptions.jl stdlib:此包充当网络配置选项的中心位置,这些选项可以由各种环境变量控制,并用于修改网络库的行为,例如libcurl和libgit2以一致的方式。例如,如果想完全关闭HTTPS主机验证,则可以在Shell中执行

export JULIA_SSL_NO_VERIFY_HOSTS="**"

HTTPS下载时,Downloads和LibGit2软件包都不会进行主机验证。 NetworkOptions中还有其他各种可用选项,包括:

JULIA_SSL_CA_ROOTS_PATH 提供CA根的自定义PEM文件

SSH_KNOWN_HOSTS_FILES 对SSH已知主机使用非标准位置

JULIA_*_VERIFY_HOSTS 用于精确控制哪些主机应该或不应该通过各种传输方式(包括TLS和SSH)进行验证的变量

新版本中,Julia本身随附的所有网络有关的软件包都支持这些选项,通过使用 NetworkOptions配置mbedtls等库。这样可以统一Julia生态系统中网络的行一致的配置。

改进堆栈跟踪格式

早在Julia版本0.6中,对堆栈跟踪的格式进行了大修 ,在该版本中在这方面做了进一步的改进。新旧的堆栈跟踪打印的对比示例,如下

Julia 1.6发布,亮点功能介绍

Julia 1.6发布,亮点功能介绍

值得提及的一些改进包括:

可以显示方法中的参数名称。

与周围的文字相比,函数名高亮显示。

显示模块定义,并且模块也用着色显示。

由于通常重要性较低,因此不再高亮函数的路径。

通过显示~显示相对路径,缩短路径。