Julia 的新天地


Julia 是一种面向科学、工程和相关技术计算领域的免费通用编程语言,自 2012 年首次公开发布以来,Julia 一直在稳步改进并拓宽其应用范围。作为 2024 年底发布的 1.11 版本的一部分,Julia 在其传统关注领域之外取得了一些进展,为用户提供了先进的工具,并在性能和程序员便利性方面取得了一些改进。Julia 的这些最新进展在很大程度上回应了新老用户长期以来的抱怨。我们上一次关注 Julia 语言是在一年前,当时它发布了上一个重要版本 Julia 1.10。

静态二进制文件

在已安装 Julia 的环境中共享 Julia 项目非常简单:提供包含程序的文本文件和一个名为 Project.toml 的附加文本文件,其中列出了代码的直接依赖关系。后一个文件由 Julia 自动生成。接收者只需执行一个实例化命令,Julia 就会安装所需的软件包,从而完全重现环境。

$ sudo subscribe today

Subscribe today and elevate your LWN privileges. 
You’ll have access to all of LWN’s high-quality 
articles as soon as they’re published, and help 
support LWN in the process. Act now and you can 
start with a free trial subscription.

这与碰巧是 Julia 用户的同事配合得很好;对于协作开发和科学研究中的可重复性而言,这都是很好的选择。然而,对于那些没有安装 Julia 的同事来说,这是一个死胡同。以 “应用程序 ”的形式分发程序可以解决这个问题,但是 Julia 的正常安装并没有提供明显的方法来做到这一点。

由于 Julia 是一种编译语言,人们可能会问问题出在哪里。毕竟,你可以将你的 Fortran 或 C 程序编译成一个紧凑的、自足的(也许取决于共享库)二进制文件,甚至可以通过交叉编译针对各种体系结构。任何人都可以运行这些二进制文件,而无需安装编译器。

使用 PackageCompiler 软件包将 Julia 程序编译成静态二进制文件也已经有一段时间了。经验丰富的 Julia 程序员可以使用这个软件包创建 “系统映像”(或 “sysimages”),其中包含 Julia 运行时以及项目中使用的软件包的冻结版本。这些系统映像可以立即加载,在当前不使用 Julia 读取-评估-打印循环(REPL)时可以节省时间。

1.10 在预编译和软件包加载时间方面的重大改进在很大程度上淘汰了使用 PackageCompiler 来节省启动时间的做法。PackageCompiler 可能仍然是二进制分发问题的一种解决方案,但有一个严重的问题:它生成的二进制文件非常庞大。

PackageCompiler 创建的系统映像包含 Julia 运行时、整个标准库、部分 LLVM 编译器等。 结果就是,一个 “hello, world “程序要负担约 150MB 的不用的东西。 (例如,编译后的 “hello, world “程序将包含 BLAS 线性代数例程,因为在目前的开发阶段,PackageCompiler 并没有发现这些例程是不需要的)。 这与 PackageCompiler 早期的版本相比是一个巨大的进步,早期的 PackageCompiler 把 “hello world “编译成了 900MB 的巨型文件,但与 Fortran 等价文件相比还是差了很多。 我刚刚用 gfortran 编译器处理了一个 Fortran 90 的 “hello world “程序,结果发现只有 17KB 的二进制文件。

为了减小二进制文件的大小,我们还使用了另一个静态编译包。不过,这个软件包只对相对较小的 Julia 程序有用,而且仅限于一组非常有限的语言特性。在这些限制条件下,StaticCompiler 可以生成真正的小型二进制文件,其大小与 C 和 Fortran 生成的二进制文件类似。然而,由于这些限制,人们的兴趣仍然集中在 PackageCompiler 的发展上。

为什么 PackageCompiler 不能直接去掉不需要的东西,并创建一个合理大小的二进制文件呢? 这是个显而易见的问题,但要找到一种方法来去除不需要的累赘却是个难题。 Julia 的动态设计以及它对 REPL 驱动开发的关注,意味着它的运行时和 “超前 “编译器是在幕后随时可用的。 它们用于为新遇到的参数类型混合物编译新方法,并随时准备好所有语言特性,包括各种类型的自省。 这就是 PackageCompiler 创建的二进制文件如此之大的根本原因,也是为什么需要做如此多的工作,才能让程序员几乎完全使用所有的语言特性,同时又不会将整个运行时拖入每个编译目标。

在过去的几年里,创建小型二进制文件的目标一直困扰着一些才华横溢的开发人员,最近他们取得了新的成功。 现在,Julia 的开发版本包含了一种名为 juliac 的实验性编译器调用方法。 这种编译器调用脚本带有一个 –trim 标记,可以 “修剪 “掉程序运行中不需要的部分,同时仍然允许使用几乎所有正常的 Julia 功能。 我们的想法是,用户可以在终端输入 juliac 而不是 julia 来编译二进制文件,而不是运行程序或启动 REPL。 与当前版本的 PackageCompiler 相比,最终的二进制文件大小减少了 90%,令人印象深刻。 截至本文撰写时,”hello world “的具体大小在开发版本中有所波动,但大约在 900KB 左右。

Julia 的下一个重要版本 1.12 可能会在 2025 年中期发布。它将最终包含生成大小合理、适合发布的静态二进制文件的功能。这将是对已并入开发分支的 juliac 机制的改进。

静态编译将彻底解决许多 Julia 程序员的抱怨和愿望,他们希望自己喜爱的语言能够像传统的科学计算语言一样,完成数十年来的工作。

juliaup

在 Linux 和 BSD 等操作系统上,Julia 的安装和升级一直都很简单:只需下载并展开 tar 包,然后将 julia 二进制文件的链接放到路径中即可。 Julia 的软件包系统还可以轻松地在同一台机器上安装任意数量的 Julia 版本,并为使用不同版本语言的项目正确解决依赖关系。 不过,从论坛上的问题可以明显看出,有很多 Julia 用户不太习惯使用命令行工具来浏览文件系统。 安装、升级或在不同版本间切换常常会导致混乱。

现在有了一种名为 “juliaup “的实用工具,可以让他们的生活变得更简单。 由于 juliaup 已成为安装和升级 Julia 的推荐方式,它现在甚至已被 Linux 用户广泛使用(不过我的习惯已定,并不使用它)。 这些都是围绕其 “通道 “概念构建的。 juliaup 频道的一些示例包括 release,指的是 Julia 的最新稳定版本;lts,指的是 “长期支持 “版本;beta,指的是最新的 beta 版;nightly,指的是每晚构建的版本,这也许并不令人意外。 有了这个通道抽象,我们就不需要再参考 Julia 的版本号了。

要获取最新的夜间版本,用户只需键入:

 $ juliaup update nightly

juliaup 还提供了一个新的、灵活的 julia 命令来启动语言。 要运行夜间构建,命令如下:

$ julia +nightly
执行命令后:
$ juliaup default release

纯 julia 命令现在将使用稳定版。

以上只是 juliaup 运行方式的几个例子。它还提供了一系列其他命令,如列出可用通道、一次性更新所有通道或引用特定的 Julia 版本等。

网络浏览器中的 Julia

如今,用 Julia 编写的相当复杂的模拟完全可以在网页浏览器中运行。亚历山大-巴特(Alexander Barth)创建了一个在浏览器中运行的有趣且令人上瘾的流体流动模拟。访问者可以用鼠标绘制边界,并观察压力场的变化。这位作者还创建了一个基于浏览器的表面波模拟。对混沌理论感兴趣的人可以在浏览器中找到著名的洛伦兹常微分方程系统的解决方案。它的速度非常快,而且演示还附带了一个教程,解释了它是如何完成的。

这三个示例不是用 JavaScript 编写的,而是用 Julia 编写的,并借助 WebAssembly 在浏览器中运行。WebAssembly (通常缩写为 “Wasm”)于 2015 年推出,是一种二进制指令格式,已广泛应用于现代网络浏览器中。只要有人为其编写了 WebAssembly 编译器,任何语言都有可能在浏览器中运行。

令人鼓舞的是,越来越多的源语言都有了 WebAssembly 编译器,并处于不同的开发阶段。我们已经可以用 Python、C、Go、Lua、Rust 等语言来替代 JavaScript。然而,尽管有上述令人印象深刻的例子,WebAssembly 的 Julia 仍然处于早期开发阶段。该语言只支持有限的子集(例如,不支持多维数组)。不过,创建小型二进制文件时遇到的问题与针对 WebAssembly 的问题有一些重叠,因此前者的最新进展很可能有助于加快后者的进程。

有兴趣让自己的 Julia 程序在浏览器中运行的读者,可以从 WebAssemblyCompiler 软件包开始。作者警告说,这些代码是试验性的,但它附带了指导性示例。

1.11 版的功能

Julia 的最新版本是 1.11.3,于 2025 年 1 月 21 日发布。10 月 8 日发布的 1.11 主版本的发布说明详细介绍了该版本对语言的改进。在此,我将总结自 1.10 以来一些最重要、最有趣的语言发展。

MemoryRef 数据类型:迄今为止,许多数组操作都是用 C 语言实现的,这造成了一些开销。现在,由于有了名为 MemoryRef 的新数据类型,这些操作都转移到了 Julia 代码中。这一变化带来的性能改进包括:将项目插入数组末尾的 push!() 函数的速度提高了一倍。WebAssembly 软件包的维护者表示,MemoryRef 数据类型最终将允许在面向 WebAssembly 的 Julia 程序中使用多维数组。

public 关键字:正如我们在介绍 Julia 包系统的文章中所指出的,模块中的名称可以显式导出,这样就可以在不使用名称间距的情况下使用它们。 例如,Base 是每个 Julia 环境都会自动导入的一个库。 它提供了数百个函数,从 sin() 等基本数学工具到 split() 等字符串操作例程。 Julia 程序员可以通过调用这些函数未加修饰的名称来使用它们,因为这些函数是由定义它们的模块导出的。

不过,Base 还包含一些在不寻常情况下有用的函数,但这些函数没有被导出。 其中一个例子是 Base.OneTo(n),它的返回值类似于区间 1:n,但在类型系统的处理方式上略有不同。 那些知道自己需要这个函数的人必须以 Base 为前缀为其命名空间。 尽管没有导出,但 Base.OneTo() 可以 “安全 “地使用;它是供公众使用的,不会破坏未来的代码。 但并非每个模块中定义的每个未导出函数都是如此。 到目前为止,还没有标准的方法来区分安全的未导出函数和应被视为 “私有 “的函数,因为后者可能无法面向未来。 现在,Julia 的 public 关键字填补了这一空白,它标记了即使未导出也可以安全使用的名称。 当然,Julia 不会阻止程序员使用不那么受祝福的名称,但现在它会向他们发出警告。

stdlib 删除: Julia 1.11 延续了几年来的进程,将模块从标准库中删除,并将其转化为普通包。 这使得模块的开发可以独立于语言的发布,从而加快了改进的速度。 这也使得 Julia 在发布时可以使用更小的系统映像(sysimage),剔除不必要的库,启动速度更快,占用内存更少。 在最新版本中,stdlib 中的所有模块都将拥有自己的版本号,从而正式确定它们的独立存在。

@main: Julia 1.11 新增了一个 @main 宏,用于标记运行程序(而非导入程序)时,Julia 程序文件中的哪个函数将作为入口点。 这样,程序员就可以将同一文件用作导入其他程序的资源和执行文件。 例如,一个由函数定义列表组成的模块可以导入到另一个程序中,而不会产生任何副作用。 但是,如果其中一个函数用 @main 修饰,那么在执行同一文件时(例如输入 “julia file.jl“),除了被定义的函数外,如此标记的函数也将被运行。 这与在文件底部插入一行调用主函数名称的语义相同;由于有了 @main,一个版本的文件现在可以双管齐下。

结语

由于 Julia 在过去几个版本中不断改进,启动时间已经变得足够快,我已经开始使用这种语言编写实用程序脚本,而这在几年前是不切实际的。 这一趋势在最新版本中仍在继续,而下一版本中编译程序将变得更加容易,这将使 Julia 更适合构建系统实用程序。 在我看来,它已经成为取代 Bash 和 Python 的有力竞争者。 如果 WebAssembly targeting 继续取得进展(编译方面的改进也将促进这一进展),那么 Julia 也将成为 JavaScript 的受欢迎的替代品(或补充品),进一步扩大其应用范围。

小二进制文件问题的解决方案将作为 1.12 版本的一部分发布,这将消除那些因难以共享编译程序而远离 Julia 语言的人的疑虑。 对于Julia最初的核心用户来说,他们习惯于将大量编译时间分摊到以天或周为单位的仿真时间中,新语言在某些交互环境中的笨拙是一种可以接受的折衷,因为他们终于拥有了一种现代的、表现力丰富的媒介,而不需要在性能上做出妥协。 Julia的开发者在几个关键领域成功地解决了批评者的异议,从而确保了他们的语言能够扩大其应用范围,远远超出最初的核心用户群。

本文文字及图片出自 New horizons for Julia

你也许感兴趣的:

共有 77 条讨论

  1. 我知道,我知道,我知道,从 Julia 发布的第一天起,人们可能就一直在不停地重复这句话,但这门语言确实在每个版本中都有明显的进步。我所经历的每一个版本(1.8-1.11,以及现在的 1.12 晚间版本)都增加了有意义的新特性、性能改进,并解决了许多最古老、最尖锐的痛点

    1.12 中的“-trim ”功能仍将被视为实验性的,如果让我猜的话,它要到 1.14 左右才会被视为 “稳定”,但我真的很高兴看到它在生态系统中被广泛使用!

    1. 我们的对冲基金使用 Julia 已经有 4 年了。它为我们节省了大量资金。

      对于没有太多软件工程经验的数学研究人员来说,它的语法非常简单,而且比我们之前使用了 5 年的 numpy 快得多。

      软件包管理非常合理,虽然比不上 Rust,但比 Python/Javascript 要好得多。

      我们有一个非琐碎的宏,它使我们的代码库缩短了约 15%,这在很多其他语言中是很难做到的。

      REPL 真的很棒,在我看来仅次于 Clojure。

      我希望看到更多的 “静态 Julia”,例如更好地支持静态类型检查和二进制文件,幸运的是,这似乎是该语言的发展方向!

        1. 我们遇到过 Arrow 的问题–它或它的依赖程序经常会在更新时崩溃,因此我们停止了使用。但文章中提到的其他问题我们从未遇到过。特别是,很多问题都来自于使用 OffsetArrays.jl,这是一个旨在让你为数组使用不同起始索引的库,而我们并没有使用它。

          值得注意的是,我们最初将大约 15kloc 的 numpy 数据/数值管道迁移到 Julia,除了发现 Python 库中的一个 bug 外,大部分发现的问题都是相同的。

          我想说的是,我们在 Julia 中遇到的最大困扰与 Expr 类型的实现方式有关,尤其是它是可变的这一事实,这使得我们想要进行的一些元编程变得更加困难。但这本身并不是一个错误,只是我不喜欢的一种设计选择。

        2. 我不会假装 Julia 没有自己的 “缺点”,但根据我的经验,这些缺点往往属于以下两类之一

          * 使用 “OffsetArrays.jl”(实际上,只要避免使用这个软件包就能解决大部分问题)

          * 只有当你的名字是 Lovecraft 时才能通过代码审查的可怕的诅咒语法,但由于某些原因,解析器允许使用它

          如果你不做这两件事(至少我个人不做),我不认为 “正确性问题 ”的发生率会比我在其他生态系统中遇到的高或低。事实上,可能更低

          别忘了,其他语言也不能幸免… 我也很喜欢 “polars ”库,但在我使用它的两年中,我遇到了两个不同的 “正确性 ”错误。对于任何大型代码来说,这都是正常现象。

        3. 还有 Julia 编程语言类型系统的悲剧:https://medium.com/@393069484/a-tragedy-of-julias-type-syste…

      1. > 我们的对冲基金一直在悄悄使用 Julia…

        这个!我发现 Julia 在数学上有很多价值,尤其是在金融分析方面。

        Julia 的 DataFrame 和一般开发工作流程非常适合研究,在部署方面也毫不逊色。

        我知道 Pandas 和 Python 中的其他工具,但出于某种原因,Julia 对我的使用情况来说更符合 “人体工学”。

      2. 金融经济学家,Julia 爱好者,希望从学术界过渡到产业界,如果您有招聘的话。如果您有任何反馈/建议,将不胜感激。电子邮件和网站请见个人简介。

        1. 我也是一名金融经济学家和朱莉娅的爱好者 — 在我看来,它是一个非常棒的金融工具

    2. 我可以确认,至少从 0.4 版开始就是如此!

  2. 我有点难过,因为我想我已经完成了从在第一份工作中倡导使用 Julia 到成为一个彻头彻尾的憎恨者的过程。我对类型系统有不满,也对没有一个好的自动生成库成为标准/默认库有不满。

    但我对Julia的主要不满在于它的内存管理方法。我们鼓励你思考内存是如何分配的,例如使用静态数组(StaticArrays)来分配较小的、不可变的数组,或者预分配数组并对其进行就地操作。但实际上,你并不能控制分配,而且一些类型的不稳定性很容易导致不必要的分配–即使你贴上了一堆类型注解,编译器也应该得到足够的信息来强制类型稳定,或者至少是类型检查崩溃/失败。

    在这一点上,我认为人们最好将时间/精力投入到 Rust 上,以处理 CPU 上的计算量较大的工作负载(数据帧的 polars、numpy 功能的 ndarray、自动衍射的 Enzyme),并使用 torch/jax 处理以 GPU 为中心的工作(此外,为 Rust 库编写 Python 绑定要比编写 C++ 或 Julia 库容易得多)。

    1. 在编写对性能要求极高的数值代码时,Julia 是我的首选工具。我从事计算物理方面的工作,发现 Julia 及其生态系统在这一领域远比 Rust 好用。

      的确,意外的动态行为确实令人担忧,而且可能成为性能杀手。幸运的是,这种语言有很好的工具。在 VSCode 中,我经常使用可视化剖析工具 `@profview` 来获取火焰图。任何动态问题都会以红色高亮显示,可以快速诊断。此外,还有一些不错的静态分析软件包,如 JET.jl。在开发过程中,可以使用 `report_opt` 静态排除意外的动态行为。这种检查也可以纳入项目的单元测试中。实际上,这对我来说已经不是什么大问题了。但公平地说,对于 Julia 的新用户来说,学习曲线还是很大的。参见 https://docs.julialang.org/en/v1/manual/performance-tips/

      1. 在我专业编写 Julia 的那一年多时间里,这些工具其实并不存在(那只是三年前的事情了),所以很高兴看到这门语言的进步。

        与此同时,我预计未来几年 Rust 生态系统将在该领域超越 Julia。Polars 已经比 pandas 更好用了,我还看到了一些关于 numpy 风格张量库的有前途的工作,而且在将 enzyme 集成到 Rust 中方面取得的进展也给我留下了深刻印象(我永远无法让它在 Julia 中运行)。下面是我最近看到的一个不错的示例软件仓库:

        https://github.com/ChemAI-Lab/molpipx/

        1. 我不是你要回复的人,但 Rust 永远不会有 REPL,也很可能永远无法快速编译。对于很多数值和科学用例来说,这是一个根本性的限制因素。关于性能,你当然是对的,但如果你需要几十次、几百次或几千次地调整数据,性能就不总是那么重要了。在这种情况下,等待时间超过 5 秒左右就会非常烦人。

          我认为总有一天会出现一种介于 Zig 和 Rust 之间的东西,作为编译速度、安全性、可编程性与内存和性能权衡之间的最佳折衷方案。

          1. 同意。Julia 的 REPL + JIT + Revise.jl 组合感觉就像魔法。编译器会自动检测源代码的变化,并在眨眼之间提供完全优化的机器代码的热代码重载!

            此外,值得强调的是,Julia 的用户体验一直在大幅改善,即使是在过去的 3 年中也是如此。Julia 1.9引入了本地代码的缓存功能[1],现在Julia 1.11的新Julia进程的首次绘图时间通常不到一秒。

            说了这么多,Rust 也绝对是一门神奇的语言,对于静态分析优先于交互式开发工作流的大规模软件开发工作来说,Rust 可能是首选。

            [1] https://julialang.org/blog/2023/04/julia-1.9-highlights/#cac

          2. 有一个由 evcxr 制作的锈迹回复

            还有一些其他的,如果我没记错的话,我在 https://youtu.be/eRHlFkomZJg 的评论中看到过,但现在没看到了,我想只有 evcxr 才有。

          3. 为 Rust 编写 Python 绑定非常简单,这可能是 “消费 ”高性能库(如物理模拟器、数据帧、图形、类型检查器等)的最简单方法。

              1. 没错,但 Julia 并不非常适合大规模软件开发,所以我更愿意使用 Rust。

                1. > Julia 不太适合大规模软件开发

                  为什么?

                  1. 我是一个一开始对 Julia 持怀疑态度的人,但现在发现它对我的日常工作非常有价值。以下是你今天可能更喜欢 Rust 的一些原因:

                    – Julia 缺乏正式的接口规范。Julia 的多方法分派(multi-method dispatch)功能为其带来了很大的灵活性。这一特性通常被认为是允许代码重用的主要卖点。生态系统中的许多 Julia 软件包都可以 “正常工作 ”的方式进行组合。例如,将 CuArrays.jl 与 KrylovKit.jl 结合使用,就能获得稀疏迭代求解器的 GPU 加速 (https://github.com/Jutho/KrylovKit.jl/issues/15)。但这种集成的实际 “拥有者 ”并不总是很明确。由于 Julia 中的公共接口并不总是有完善的文档记录,因此很容易出现故障,有时会让人感觉像是 “远距离操作”。这一点在 OffsetArrys.jl 软件包中尤为明显,该软件包突然引入了可以从任意整数索引开始的数组。(这正是 Yuri 博客文章的主题,对大多数人来说,简单的解决方案就是避免使用 OffsetArrays)。Rust 的社区理念和形式化特征系统偏向于提供静态的正确性保证。但这些限制也剥夺了将不同软件包整合在一起的灵活性。例如,Julia 对类型特化的支持一直都很出色,而要将其融入 Rust 中,即使是以非常有限的形式:https://users.rust-lang.org/t/the-state-of-specialization/11….,也是出了名的挑战。相反,关于在 Julia 中设计正式接口系统的讨论也很多,但这仍然是一个挑战:https://discourse.julialang.org/t/proposal-adding-optional-s…

                    – Julia 是围绕及时编译而设计的。例如,每次使用新的参数类型调用函数时,都会对该特殊化进行全新编译。当你希望获得最佳性能时,这一点非常重要。此外,由于 Julia 允许将语法重新定义为值级对象,因此您可以根据运行时值定制优化的 Julia 代码。对于某些类型的数字运算代码来说,所有这一切都非常强大。但是,随身携带完整的 LLVM 系统显然会阻碍小型预编译二进制文件的发布。因此,LWN 讨论了预览版 juliac 功能,该功能将提供完全静态编译模式。

                    – Rust 的借用检查器值得羡慕。在任何其他语言中,我都会怀念安全地传递堆栈分配变量的引用,或者知道引用值不能被修改的能力。

                    最后,对于大多数不太 “定制 ”的机器学习或数据分析项目,我可能会推荐 Python(而不是 Rust!)。PyTorch 和 JAX 的发展势头非常强劲。Julia 社区正在这一领域开发一些非常有趣的软件包。特别是 Lux.jl、Enzyme.jl、Reactant.jl 和所有 SciML。这些软件包功能强大,但仍然非常具有研究性。对于简单的事情,Python 可能会减少摩擦。

                    最好的语言取决于你的用例。即使 Julia 不能满足所有可能的使用情况,但它也能很好地满足自己的需求。

                    1. 因为 Julia 允许将语法重新定义为值级对象,所以您可以根据运行时的值定制优化 Julia 代码”(最好能举例说明),您能否对这一耐人寻味的评论进行扩展?

                    2. 似乎有道理。谢谢。

                2. 这不是它的设计初衷,而是科学计算。

    2. 你的观点很有道理,直到你提到 Rust,整个论证才显得可疑起来。Rust 没有 REPL 的交互性或动态性。对于普通的科学家程序员来说,这完全是一种痛苦。鉴于安全并不是该领域的迫切需求,即使是 C++ 也更胜一筹,因为整个计算基础架构在很大程度上都是基于 C++ 的。

      1. 哦,我主张用 Rust 编写高质量的库/组件,然后使用 Maturin 生成 Python 绑定以实现交互,[1] 就是这种工作流程的一个例子,看起来相当流畅。

        [1]https://github.com/ChemAI-Lab/molpipx/

        1. 然后你又回到了 “两种语言问题”。我相信这对你和很多人来说都不是问题,但它有自己广为人知的名字是有原因的。对于大多数不是软件开发人员,而是工程师或研究人员的人来说,这确实是个问题。

          1. 是的,我想我对 Julia 的看法是,它表明,为了让科学家/工程师 “容易接受 ”一门语言而做出的必要让步,将不可避免地导致一门不适合开发大型、健壮软件项目的语言。

    3. > 我对 Julia 的主要不满在于它的内存管理方法。

      我不是一个十足的憎恨者,但我也有这方面的问题。具体来说,你根本无法控制它,你只是被承诺 “如果你把事情做对了,它就会很棒”。事实的确如此!问题在于,任何一个微不足道的小错误都会因分配而导致灾难性的性能故障。因为良好的性能取决于类型的稳定性,而类型的稳定性会传播,所以任何地方的错误都会传播到所有地方。想想看:如果一个变量由于程序员的失误而变得类型不稳定,那么任何使用该变量的函数都可能变得类型不稳定,而任何使用该函数输出的函数也可能变得类型不稳定,等等。结果就是,这迫使你更仔细地考虑类型和数据结构。大量使用 Julia 编程让我成为了一名更好的程序员。我不是 C++ 专家,但我相信在 C++ 中,这类错误最终总会被局部化。

      1. 这种说法其实并不正确。类型不稳定性往往会在函数边界消失,这也是 Julia 大力推广使用函数的原因之一,它有助于将类型不稳定性 “本地化”。

      2. 我在研究生院时是一名 Haskell 程序员,正是 Julia 让我懂得了 “哦,有时候程序员确实比类型系统/编译器更了解情况”。我认为他们在语言中处理多重分派的方式(以及由于类型不稳定而导致的分配)确实是这门语言的原罪,而且我不认为它可以被修复,所以我不禁觉得任何改进 Julia 的努力都是在浪费时间。

  3. 小型静态二进制文件可能是 Julia 的杀手级功能。

    虽然我一直很关注 Julia,它也是一门可爱的语言,尤其是乘法调度和一般的简洁性,但我从未真正用到过它。

    Python 一直是更好的选择,因为它有更强大的生态系统和更快的启动时间。我很少需要 Julia 提供的额外性能。但 Python 部署起来很麻烦,所以 Julia 开始变得很有吸引力,至少对于不想进行复杂的 docker 设置的小型项目来说是这样。

    Julia 确实优先考虑了正确的事情。

    1. 你可以用 uv run –script main.py 来使用 uv,还可以用 –with numpy 给它一些要求,比如 numpy。

      你也可以把它上传到 pypi,比如说用 heytest 这样的名字,然后直接做 uvx heytest 就可以了。

      Uv 太棒了,Uv 就是爱。

      1. 我开始使用 UV。我的第一个想法是 “太好了,Python 现在更像 Julia 了”。

      2. 我还能轻松安装 UV,甚至创建 UV 的静态二进制文件

  4. 文章将 Julia 描述为 “面向科学、工程和相关技术计算领域的通用编程语言”。

    我认为这是正确的。如果您要求解微分方程或运行一些数值天气代码,或求解整数程序或模拟电路,最好考虑一下 Julia,而不是使用 Fortran、C++ 或 MATLAB。它是一门更好用的语言,而且速度仍然很快。

    我的经验是,想要使用 Julia 进行数据科学,尤其是深度学习的人通常会发现,你可以完成工作,但通常使用 PyTorch 或 Jax 会更容易,而且只需接受 Python 的局限性即可。

    1. 上文关于 Julia 语言作为 Fortran 或 C++ 替代语言的固有局限性的评论似乎与你的观点相矛盾。

      1. 这些限制有时确实存在。

        不过,我读过科学家们写的足够多的 Fortran 和 C++ 代码,因此热切希望他们的代码是用 Julia 写的。在 Julia 中,我不认为他们能写出如此放荡不羁的代码。如果你只是以天真烂漫的方式做事,通常会过得很好,代码既可读又快。

        类型不稳定性和内存分配是个隐患,但我认为只要掌握一些基本技巧,人们就能学会避免这种情况,尤其是在较简单的任务中。(编辑:还有,如果人们确实把这些事情搞砸了,他们会注意到是因为他们的代码太慢了,而如果他们的代码很快,但却难以理解、无法维护或扩展,人们就不会注意到)。

  5. Julia仍然是个非常好用的工具。我有一个梦想,希望试图为 pytorch 提供 Julia 接口的 Torch.jl 能够神奇地起飞。Python 还可以,但 julia 实在是太快、太有趣、太容易扩展了。

    1. 您是否尝试过在 Pytorch 中使用 PythonCall.jl(原则上允许您使用任意 Python 库)?

  6. Julia 不是脚本语言,或者换句话说,Julia 的范围规则对脚本不友好

    目前,如果在一个文件中

        local s = 0
        for i = 1:10
            t = s + i
            s = t
            println("$s")
        end
        println("$s")
    

    执行该文件时,会出现以下错误

        ERROR: LoadError: UndefVarError: s not defined
    1. 请注意,如果在全局作用域中执行此操作,`s` 的本地声明将导致它在功能上不存在。

      您可以将其写为

          s = 0
          for i ∈ 1:10
              t = s + i
              global s = t
              println("$s")
          end
          println("$s")
      

      or

          let s = 0
              for i ∈ 1:10
                  t = s + i
                  s = t
                  println("$s")
              end
              println("$s")
          end
      1. 另一种方法是将代码放入一个函数中,然后调用该函数。

            function test()
                s = 0
                for i = 1:10
                   t = s + i
                   s = t
                   println("$s")
                end
                println("$s")
            end
            test()
        

        对于好奇的人,文档中解释了这种范围界定的原理。从根本上说,这是为了防止所谓的 “超距作用”。

      2. 当然,你在技术层面上的观点是正确的,但我还是倾向于同意这样的说法:Julia 对大量使用底层脚本的工作流程并不特别友好

        1. 我不同意。就我而言,Julia 在繁琐与自由之间取得了很好的平衡。

          当然,有些事情我觉得它太挑剔了,我希望它能说 “是的是的,我明白你的意思”,也有些事情我发现自己在想 “伙计,我真希望你能向我抱怨一下,这显然是个错误/风格不好!”

          但总的来说,我发现它是一门大部分时候都不会妨碍我,让我自己编写代码的语言,我觉得这对编写脚本非常有用,也很好。

          我认为,如果一个人觉得脚本中的范围规则等东西很烦人,那可能只是因为他了解并更熟悉另一种有不同规则的语言。

          1. 你看,我觉得它恰恰相反–表面上很挑剔,但它会让最愚蠢的事情溜之大吉。我不希望我的函数在某些情况下(也许只出现在物理模拟或运动规划算法的深处)因类型不稳定而挣扎,并分配大量内存,如果可能的话,我首先就不想让它编译。

            1. 正如我们在另一个主题中所讨论的,这是可能的!我们可以使用静态分析工具 JET.jl 逐个函数地 “退出 ”Julia 的动态行为:https://aviatesk.github.io/JET.jl/dev/optanalysis/。

              为什么不在所有地方都将其作为默认设置呢?在很多科学用例中,使用 Python 风格的动态类型和交互性非常方便。Julia 的一个亮点是,它可以做到这一点,同时_也_可以在一门语言中实现高性能。

              顺便说一句,我也很喜欢 Rust 的静态类型系统和整体设计。但对于我的日常工作(数值方法和计算物理研究)来说,我发现 Julia 是完成工作最有效的方法–快速算法原型设计、数据分析、绘图生成等。

    2. 抱歉–你为什么希望局部声明在其他代码的作用域中?

      s 是在语句中定义的,出了语句就是未定义的,因为它没有其他作用域 – 它在顶层。只要写 s =0,一切就都好了,因为 s 也存在于你的代码所在的全局作用域中……

      1. 那语言不是应该标明在该作用域中使用 local 会出错吗?

        1. 是的,错误报告一直是我对 Julia 的抱怨之一(尽管它已经有了很大改进)。我认为 Julia 开发社区对酷炫的语言特性和优化非常感兴趣,但对像我这样不理解代码为什么会崩溃的傻瓜却不那么感同身受。在这方面做更多工作会更好。

          虽然让解释器理解哪些超出范围的东西应该在范围内代码才能工作(因为它们都超出范围了)是个挑战。

    3. 没错,但有人说过 Julia 是一种脚本语言吗?

  7. 我很高兴看到静态编译正在进行中!如果静态编译能够流行起来,人们能够构建秘密编译 Julia 代码的 Python 包,我就能看到一个更多的人选择使用 Julia 而不是 C 或 C++ 的世界。尽管编写能作为 Python 包使用的 Rust 库是一件乐事,但 Julia 仍然会面临竞争。

    Julia目前最大的问题是增长。Julia一直没有指数级增长,要么保持小幅线性增长,要么人气下降。在YouTube上搜索教程,在Twitch上搜索WatchPeopleCode,或者在GitHub上搜索基准;Julia甚至都不在讨论的范围内–根本没有任何思想共享。

    这是有道理的。在大型代码库和大型团队中使用 Julia 会面临许多人体工程学方面的挑战。Julia 没有正式的界面,LSP 建议往往是错误的,也没有选项类型。这让编写 Julia 代码变得非常麻烦。这也使得它很难向那些使用过提供这些特性的语言的开发者进行宣传。

    此外,Julia 的拥护者所推崇的核心理念是该语言非常快。这在可控基准测试中是正确的,但在实际场景和实践中,对于高速开发的团队来说,编写和维护快速代码是一件非常痛苦的事情,因为这需要大量的规范和对内存分配的深刻理解,以及对 Julia 能做和不能做的假设的理解。你可以编写速度极快的代码,但如果你对程序中的其他地方做了改动,你的代码就会突然停止运行。我们曾经遇到过这样的情况:由于一行代码的类型不稳定,测试代码的运行时间从 10 分钟变成了 2 个多小时。发现这种情况并非易事。作为参考,如果这个问题没有被发现,我们的生产版本就会从 8 小时缩短到 4 天。

    缺乏发展确实对语言造成了伤害。搜索阳光下的任何主题,你都能找到 Python 包,甚至可能找到 Rust crate。而在 Julia 中,你通常需要从头开始编写。对于数据处理来说,软件包是必不可少的,但贡献者却很紧张。如果你所依赖的开源代码有些不受欢迎,无法按照你的意愿运行,你可能会想我只需提交一份 PR,但它可能会被搁置几个月到一年。

    Julia社区需要关注Julia开发者需要并将从中受益的编程语言。软件世界变化非常快,Julia 也需要跟上变化的步伐。

    1. > 我们的测试代码曾因为一行代码的类型不稳定而从运行 10 分钟缩短到超过 2 小时。

      对于那些可能不太熟悉的人来说,工具有时能帮上大忙。ProfileView.jl 软件包(或直接调用 VSCode 中的 @profview 宏)将显示交互式火焰图。类型不稳定性用红色高亮显示,内存分配用黄色高亮显示。这将有助于确定发生类型不稳定或分配的确切行。此外,为了防止回归,我非常喜欢在单元测试中使用 JET.jl 静态分析包。

      1. 如果这么容易识别,为什么不直接将其作为 JIT 错误呢?手动检查这一切听起来太可怕了。我宁愿我的编译器帮我做。

        1. 在性能不重要的情况下,动态行为可以作为一种很好的默认设置。因此,Julia 可以让你在性能不重要的地方像 Python 一样编码,然后在性能重要的地方像 C++ 或 Fortran 一样编码。

        2. 因为动态行为本身并没有什么错误或不正确的地方。在很多情况下,它都超级有用。

    2. 在将近 25 年的时间里,Python 也没有任何指数级的增长,一直都是缓慢而稳定的。

  8. 我几乎总是喜欢用 Julia 编程。它真的是一种美妙的体验。如果我能凭借 Julia 的技能在业界找到一份工作就好了……

  9. 我非常喜欢 Julia 的发展方向和它的权衡管理。我希望它能获得更多的关注。

  10. Julia 与 Python 和 Jax 相比如何?

    我很喜欢 Python 的易用性,相比之下,我尝试过几次 Julia,感觉很笨拙,而 Jax 则让一切都快得惊人。

    1. “笨拙”?在比较 Julia 和 Python 时,我会用这个词,但我会将两者的角色对调一下。我的意思是,Python 运行得很好,几乎无所不能,但它给人的感觉确实很 “笨拙”。

      1. 我目前正在重写一个从 JAX 到 Julia 的小型模型,我发现 Julia 的代码更容易编写、更简洁,而且 Julia 的调试工具也更容易使用。

    2. 对于 Jax 能很好解决的问题,理论上可以有一个很好的 Julia 对应库,但实际上并没有出现。相反,每隔三年左右,就会有一位杰出的个人研究者写出一个更好的 autograd 库,但最终还是被放弃了。

      在其他领域,茱莉亚语言是最好的。在一些领域(微分方程、数学优化),茱莉亚语言实际上是最先进的。

      1. 即使我对 Julia 恨之入骨,但它在微分方程方面的抽象程度确实很高,克里斯-拉考卡斯(Chris Rackaukas)在这方面写出了一些很棒的软件。对于某些领域(ODEs、统计建模/可视化)来说,它是一个很好的工具,我也明白为什么 Pumas 会使用这种语言。

        我不认为 Julia 是替代传统上用 C++/Fortran 编写的复杂物理仿真代码的绝佳工具,我认为使用 JAX/Rust/C++ 一般会更好。

    3. 查看 Lux.jl 和 Reactant.jl,了解使用 Jax 的 XLA 编译器后端创建 Julia 库的现代尝试!

      1. 有时真的觉得 JAX 应该是一个可以嵌入其他语言的通用 DSL,只要你有办法将结构转化为 pytrees,将函数转化为 jaxpr。

  11. > 下一个主要的 Julia 版本 1.12 可能会在 2025 年年中发布。它将最终包含生成大小合理、适合发布的静态二进制文件的功能。

    如果 Julia 早期就有这个功能,我会很激动,但现在有点太晚了。我已经跳槽了。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注