HN 日本語サマリー

← 一覧へ戻る
プログラミング

Julia 1.13 の主な変更点

Julia 1.13 Highlights (julialang.org)

249 pointsby eigenspace38 コメント

要約

Julia 1.13 がリリースされ、パフォーマンスとユーザーエクスペリエンスの向上に重点が置かれています。主な改善点として、起動時間(TTFX)の約20%短縮、REPLでのシンタックスハイライトやfzf風履歴検索の導入、Windowsでのブラケットペースト対応などが挙げられます。さらに、ハッシュ関数の高速化や、システムイメージオブジェクトをスキップすることによるガベージコレクションの効率化も実現されています。

全文翻訳

Julia 1.13 のハイライト

Julia version 1.13 がリリースされました。このリリースに貢献してくださったすべての皆様、そしてプレリリース版の回帰や問題の発見に協力してくださったすべてのテスターの皆様に感謝いたします。皆様のご協力なしには、このリリースは実現しませんでした。

変更点の全リストは NEWS ファイル で確認できますが、ここではリリースにおけるいくつかのハイライトについて、より詳細な概要を説明します。

  1. レイテンシ(TTFX)の改善
  2. REPL の改善
    1. 構文ハイライト
    2. 新しい fzf スタイルの履歴検索
    3. Windows でのブラケットペースト
  3. @__FUNCTION__
  4. ハッシュ処理の変更
  5. マーキング中にイメージオブジェクトをスキップすることによる GC の高速化
  6. スケジューラと割り込みの修正
  7. 型アノテーションによるイントロスペクション
  8. --trace-eval によるトップレベル評価のトレース
  9. JuliaC/trim
  10. Pkg
    1. デフォルトの圧縮アルゴリズムを gzip から zstd に変更
    2. パフォーマンスの改善
    3. マニフェストで追跡されるパッケージのレジストリ
    4. ソースの再帰的な収集
    5. pkg> add が既にロードされているパッケージと同じバージョンを追加しようとするようになる
    6. Pkg.test は厳密な境界チェックの有効化をデフォルトとしなくなる
  11. Juliaup GUI

レイテンシ(TTFX)の改善

Ian Butterworth, 多数の貢献者

Julia 1.13 は、1.12 と比較してパッケージのプリコンパイルに約 30% 短い時間しかかからず、マシンによっては 1.10 (LTS) より約 10-20% 短い時間で済みます。

Time To First X (TTFX)、つまり Julia の起動から最初の結果を得るまでの時間は、プリコンパイル、ロード、コード実行の 3 つの主なコストで構成されます。コミュニティから提供されたワークフロー Julia-TTFX-Snippets の助けを借りて、これらのコストを実際の例でより体系的に測定し、Julia をそれらに対して最適化し始めました。

以下のチャートは、2 台のマシンで、現在提出されている 39 のワークフローすべてにおける幾何平均を示しています。プリコンパイルは 2 回の実行のうち最速、ロードと実行時間は 3 回の実行のうち最速です。

このモニタリングは、Julia 自身の開発プロセスにも組み込まれています。新しい TTFX CI ジョブが関連するプルリクエストと master へのすべてのコミットで実行され、結果は perf.julialang.org/ttfx で追跡されます。その追跡は 2026 年 9 月 7 日に開始されました。それ以前の測定は場当たり的でした。

Julia 1.13 の起動も 1.12 より約 20% 高速です。

% hyperfine --warmup 3 --runs 20 -N \
  --command-name "julia 1.12" "julia +1.12 --startup-file=no -e ''" \
  --command-name "julia 1.13" "julia +1.13 --startup-file=no -e ''"
Benchmark 1: julia 1.12
  Time (mean ± σ):      69.1 ms ±   1.0 ms    [User: 50.1 ms, System: 18.1 ms]
  Range (min … max):    68.0 ms …  72.6 ms    20 runs

Benchmark 2: julia 1.13
  Time (mean ± σ):      56.7 ms ±   0.5 ms    [User: 49.1 ms, System: 18.9 ms]
  Range (min … max):    56.0 ms …  58.1 ms    20 runs

Summary
  julia 1.13 ran
    1.22 ± 0.02 times faster than julia 1.12

REPL の改善

構文ハイライト

Timothy, Kristoffer Carlsson

Julia REPL には、外部パッケージ(OhMyREPL.jl など)をロードすることなく、構文ハイライト機能が追加されました。

[画像: REPL syntax highlighting]

デフォルトでは、カラースキームはかなり控えめですが、簡単にカスタマイズできます(REPL のドキュメントを参照してください)。例として、Monokai カラースキームを使用した同じコードを示します。

[画像: REPL syntax highlighting with the Monokai color scheme]

新しい fzf スタイルの履歴検索

Timothy

履歴検索(デフォルトでは Ctrl-R で入力)が再設計され、コマンドラインのファジーファインダー fzf と同様に動作するようになりました。

[画像: REPL history search]

[画像: REPL history search for LinearAlgebra]

新しい履歴検索は、とりわけ以下の機能をサポートしています。

  • 履歴内でのファジー検索。
  • コマンドに使用された REPL モードの表示。
  • 複数の検索結果を選択してプロンプトバッファに入れる機能。
  • REPL 自体と一致するコードの構文ハイライト。

履歴検索を入力し、? と入力すると、完全なヘルプが表示されます。

Windows でのブラケットペースト

ブラケットペースト は、ターミナルで実行されているアプリケーションが、テキストが単にタイプされたのではなく貼り付けられたことを認識できるようにします。これにより、貼り付けられたテキストのより効率的で正確な処理が可能になります。この機能は Linux および macOS では長らく有効でしたが、ついに Windows でも利用可能になりました。具体的な例として、以下のビデオは、Windows でブラケットペーストを有効にする前と後で、約 500 行の関数を Julia REPL に貼り付けたときの動作を示しています。

Before: [動画]

After: [動画]

@__FUNCTION__

Miles Cranmer, Jeff Bezanson

既存の @__MODULE__ および @__FILE__ マクロと同様に、新しい @__FUNCTION__ マクロは、匿名関数であっても、最も内側の包含関数を参照します。これはあらゆる種類の関数で機能するはずであり、内部変数 #self# とは異なり、パブリック API です。

julia> fact = n -> n <= 1 ? 1 : n * @__FUNCTION__()(n - 1);

julia> fact(5)
120

ハッシュ処理の変更

Andy Dienes, Jameson Nash

ハッシュ関数が置き換えられました。バイトハッシュアルゴリズムは現在 RapidhashNano です。このハッシュは、AbstractString および BigInt、Rational、大きな Real または Integer 値などの多くの数値型でデフォルトで使用されます。また、カスタム型が、まずサポートされている型(String など)に変換することなく、汎用実装をオプトインすることがはるかに容易になりました。この変更は、MurmurHash3 に基づく既存の実装と比較して、いくつかの利点を提供します。パフォーマンスが大幅に向上し、ストリーミングハッシュであるため入力の length を事前に必要としなくなり、C から純粋な Julia に移行されたため、可読性と保守性が向上しました。

長い文字列でのパフォーマンスの向上を示すために:

using BenchmarkTools, Downloads

io = IOBuffer()
Downloads.download("https://www.gutenberg.org/cache/epub/1080/pg1080.txt", io)
s = String(take!(io));

# 1.12
@btime hash($s)
  8.555 μs (0 allocations: 0 bytes)
0x5fbd2717019846ea

# 1.13
@btime hash($s)
  1.742 μs (0 allocations: 0 bytes)
0x718308e795047519

そして、より高速なフォールバックをオプトインするデモンストレーション:

struct MyString <: AbstractString
    s::String
end
m = MyString(s);

# 1.12
Base.iterate(m::MyString) = iterate(m.s)
Base.iterate(m::MyString, i::Integer) = iterate(m.s, i)
@btime hash($m)
  204.583 μs (21 allocations: 107.02 KiB)
0x5fbd2717019846ea

# 1.13
Base.codeunit(m::MyString) = codeunit(m.s)
Base.codeunits(m::MyString) = codeunits(m.s)
@btime hash($m)
  1.750 μs (0 allocations: 0 bytes)
0x718308e795047519

小さな固定幅データのハッシュも変更されました。最終的な混合ステップは、注意深く調整された定数を持つ単一ラウンド XMX 構成になり、混合ステップはハッシュ呼び出しを合成する際に適切にアバランシェするようになりました。以前は混合ステップは、すべての合成深度で線形関数に単純化されていました。混合ステップのこの変更はデータ依存性(したがって潜在的にパフォーマンスの低下)を導入しますが、AbstractArray のハッシュアルゴリズムは、小さなサイズから中程度のサイズで部分的にアンロールされ、複数のハッシュアキュムレータを並列に維持しており、ほとんどの長さで大幅に高速になります。

いくつかの重要なリマインダー:hash は非暗号学的なままです。また、デフォルトのシードが変更されました。カスタム hash メソッドは、常にシードを引数として受け取る必要があります(例:hash(x::MyType, h::UInt))し、デフォルト値を提供しないでください(例:hash(x::MyType, h::UInt=0))、なぜなら正しいシードは呼び出し元によって決定されるからです。

マーキング中にイメージオブジェクトをスキップすることによる GC の高速化

Cody Tapscott

すべての Julia セッションは、システムイメージからロードされた多数のオブジェクトで開始され、ロードされるすべてのパッケージは、メソッドテーブル、型情報、コンパイル済みコード、定数など、さらに多くのオブジェクトを含む独自のパッケージイメージをもたらします。これらのオブジェクトは決して解放されず、めったに変更されませんが、これまではフルガベージコレクションは、ヒープ上の他のオブジェクトと同様に、それらすべてをウォークして到達可能としてマークしていました。多数の大きなパッケージがロードされたセッションでは、これはフルコレクションの主要なコストになる可能性があります。

Julia 1.13 では、sysimage およびパッケージイメージ内のオブジェクトは、永続的にマークされた状態でロードされ、マークフェーズはそれらに入ることはありません。イメージオブジェクトに対して発生する少数の変更(たとえば、既存の関数にメソッドが追加された場合)は個別に追跡されるため、それらが指す新しいオブジェクトは引き続き生存します。その結果、フルコレクションのコストは、ロードされたコードの量ではなく、プログラムが実際に作成したヒープのサイズに比例するようになりました。

違いを最も簡単に確認するには、新しいセッションでのフルコレクションの時間を計測します。

# 1.12
julia> @time GC.gc()
  0.035493 seconds (99.90% gc time)

# 1.13
julia> @time GC.gc()
  0.000528 seconds (99.08% gc time)

以下の表は、Apple M4 Pro でのフルコレクション(GC.gc(true))の時間を示しています。まずベアセッションで、次に増加するサイズのパッケージをロードした後です。インクリメンタル(若い世代)コレクションは、この変更の影響を受けず、両方のバージョンで同様に高速です。

1.121.13
Bare session35 ms2 ms
using Revise50 ms11 ms
using Cthulhu59 ms18 ms
using PythonCall90 ms30 ms
using GLMakie187 ms68 ms

大きなライブヒープを持つプログラムではフルコレクションがより頻繁にトリガーされるため、実際のワークロードでも全体的な GC 時間の削減として現れます。次の例では、イテレーション全体で生存する Dict にランダムなベクトルを挿入するため、割り当てられたオブジェクトの大部分が古い世代に昇格します。

function work(n)
    d = Dict{Int,Vector{Float64}}()
    for i in 1:n
        d[i % 50_000] = rand(64)
    end
    return length(d)
end

# 1.12
julia> @time work(5_000_000)
  1.699095 seconds (10.00 M allocations: 2.688 GiB, 79.80% gc time)

# 1.13
julia> @time work(5_000_000)
  0.566276 seconds (10.00 M allocations: 2.688 GiB, 44.32% gc time)

詳細については、プルリクエスト を参照してください。

スケジューラと割り込みの修正

Kiran Pamnany, Jameson Nash, Ian Butterworth

アイドル状態のスレッドは、最後に実行したタスクを保持するのではなく、専用のスケジューラタスクでパークするようになったため、完了したタスクは速やかにガベージコレクションできるようになりました(#57544)。これは、割り込みを再び信頼できるものにする一連の関連するスケジューラ修正とともに提供されます(#62665)。

  • Ctrl-C が、sleep や IO でブロックされているスクリプトを含め、ユーザーコードに再び到達するようになり、Distributed.interrupt も機能するようになりました。
  • REPL は、繰り返しやタイミングの悪い Ctrl-C のプレスに耐えるようになりました。
  • @spawn は、タスクのスレッドプール内のすべてのスレッドではなく、1 つのアイドルスレッドをウェイクアップするようになりました(#61826)。Spawn 重複コードは、macOS ではほとんど変化がないか、16 コア Linux マシンでは 1.1〜1.6 倍、Windows および過負荷のマシンでは 10〜300 倍高速化します。これは、すべてのスレッドをウェイクアップすることが主要なコストだったためです。
  • いくつかのタスク消失やデッドロックの競合が修正されました。

適切なタスクキャンセル機構の作業が進行中であり、Julia 1.14 で計画されています。

型アノテーションによるイントロスペクション

コードイントロスペクションマクロ(@which、@code_typed、@code_warntype など)は、メソッド定義やスタックトレースの ::T 構文と同じように、値の代わりに型の引数を受け入れる呼び出し式をサポートするようになりました。値と型は自由に混在でき、キーワード引数もサポートされています。

julia> @which push!(::Vector{Int}, 1)
push!(a::Vector{T}, item) where T
     @ Base array.jl:1339

julia> @which sort!(::Vector{Int}; by = ::Function)
kwcall(::NamedTuple, ::typeof(sort!), v::AbstractVector{T}) where T
     @ Base.Sort sort.jl:1734

これにより、スタックトレースからフレームをコピーして @which に貼り付けることで、呼び出されたメソッドを見つけることができます。

julia> @which Base.Order.lt(o::Base.Order.Lt{typeof(isless)}, a::Int64, b::Int64)
lt(o::Base.Order.Lt, a, b)
     @ Base.Order ordering.jl:121

ブロードキャスト式も @code_lowered、@code_typed、@code_warntype でサポートされています。

julia> @code_warntype (::Vector{Int}) .+ 1.0

--trace-eval によるトップレベル評価のトレース

Ian Butterworth

新しい --trace-eval コマンドラインフラグは、トップレベル評価の進行状況を表示し、テストスイートやスクリプトの進捗状況を確認するのに役立ちます(例:ハングを特定するため)。たとえば:

% julia --trace-eval script.jl
eval: #= /Users/me/.julia/config/startup.jl:1 =#
eval: #= /Users/me/.julia/config/startup.jl:2 =#
eval: #= /Users/me/.julia/config/startup.jl:3 =#
eval: #= script.jl:1 =#
eval: #= script.jl:2 =#
Hello world

GitHub Actions で示されているように、「デバッグログを有効にする」オプションが CI 実行でオンになっている場合にも自動的に有効になります。

[画像: GitHub Actions re-run dialog with "Enable debug logging" checked]

JuliaC/trim

Cody Tapscott, 多数の貢献者

juliac.jl スクリプトが、正式なパッケージ/アプリケーションである JuliaC.jl になりました。

ファイナライザ、@cfunction、mapreduce など、より多くのコードをトリムできるようになりました。

トリミングプロセス自体のいくつかのバグも修正され、信頼性が向上しました。

Pkg

Kristoffer Carlsson

Pkg は 1.13 でかなりの注目を集めました。ここでは、より注目すべき変更点と改善点をいくつかリストします。

デフォルトの圧縮アルゴリズムを gzip から zstd に変更

パッケージサーバーからのダウンロード(レジストリ、パッケージ、アーティファクト)について、Pkg はデフォルトで gzip 圧縮アーカイブではなく zstd 圧縮アーカイブを要求するようになります。Pkg が通常ダウンロードするファイルの種類では、zstd 圧縮はより優れた圧縮率と大幅に優れた解凍パフォーマンスの両方を持つ傾向があります。例として、Plots、Makie、ModelingToolkit パッケージのパッケージとアーティファクトをダウンロードした場合の数値を示します。

gzipzstd
Total downloads405405
Total download size307.99 MB239.31 MB
Total decompression time8.77 s5.50 s
Average decompression time21.98 ms13.77 ms
パフォーマンスの改善

リゾルバとレジストリ処理にいくつかのマイクロ最適化が施され、Pkg 操作の全体的なパフォーマンスが向上しました。これらの改善の一部は既に 1.12 にバックポートされているため、適切なパフォーマンス比較を行うには、これらのバックポートを一切受けていない 1.12.1 と比較します。

リゾルバ速度への影響を評価するために、次のベンチマークを行います。空の環境に Plots を追加し、削除してから、再度 Plots を追加するのにかかる時間をベンチマークします。これにより、Plots のすべてのファイルが既にダウンロードされていることが保証されます。さらに、自動プリコンパイルが無効になり、レジストリキャッシュがクリアされるため、最初から再読み込みする必要があります。これは、この環境に Plots を追加するのにかかる時間が主にレジストリ処理と解決であることを意味します。

julia> ENV["JULIA_PKG_PRECOMPILE_AUTO"] = 0

# 1.12.1
julia> empty!(Pkg.Registry.REGISTRY_CACHE); @time Pkg.add("Plots"; io=devnull)
  1.257017 seconds (8.83 M allocations: 681.328 MiB, 16.31% gc time)

# 1.13.0
julia> empty!(Pkg.Registry.REGISTRY_CACHE); @time Pkg.add("Plots"; io=devnull)
  0.745170 seconds (4.43 M allocations: 304.580 MiB, 26.90% gc time)

さらに、Pkg はより効率的な設定でリポジトリをクローンするようになり、不要なデータのダウンロードを回避します。

# 1.12.1
julia> @time Pkg.add(name="Plots"; rev="master")
     Cloning git-repo `https://github.com/JuliaPlots/Plots.jl.git`
...
 10.953074 seconds (4.51 M allocations: 330.819 MiB, 1.68% gc time)

# 1.13.0
julia> @time Pkg.add(name="Plots"; rev="master")
     Cloning git-repo `https://github.com/JuliaPlots/Plots.jl.git`
...
  2.980337 seconds (2.87 M allocations: 189.202 MiB, 3.87% gc time)
マニフェストで追跡されるパッケージのレジストリ

以前は、マニフェストをインスタンス化するには、そのマニフェストが必要とするレジストリが利用可能であることを手動で確認する必要がありました。現在、各パッケージが由来するレジストリはマニフェストに記録されており、マニフェストのインスタンス化(またはその他のパッケージ操作)時に自動的にインストールされます。

ソースの再帰的な収集

Pkg は現在、URL で取得されたパッケージから [sources] エントリを再帰的に収集するようになり、プライベートパッケージの依存関係チェーンが、プライベートパッケージのすべての依存関係がレジストリにあることを必要とせずに解決できるようになります。

pkg> add が既にロードされているパッケージと同じバージョンを追加しようとするようになる

Julia は常にセッション中にアクティブなプロジェクトを変更することを許可し、スタック環境(最も一般的にはデフォルト環境を介して)をサポートしていますが、これはパッケージの繰り返しプリコンパイルにつながる可能性のある厄介な問題を引き起こします。たとえば、startup.jl 中にパッケージのバージョンがデフォルト環境からロードされ、次にユーザーがアクティブなプロジェクトに新しいパッケージを追加し、その依存関係の別のバージョンがプルされます。Pkg はアクティブなプロジェクトの依存関係グラフをプリコンパイルするため、新しいバージョンがプリコンパイルされますが、既にロードされているバージョンは互換性制約を同様に満たしていた可能性が高いです。

1.13 では、Pkg は、環境の互換性制約がそれを許可する場合、pkg> add を解決する際に既にロードされているパッケージの現在ロードされているバージョンを優先するため、何も再プリコンパイルする必要がなくなります。通常どおり、pkg> status は新しいバージョンが利用可能であることをフラグ付けします。

Pkg.test は厳密な境界チェックの有効化をデフォルトとしなくなる

以前は、Pkg.test は常にテストプロセスを --check-bounds=yes で起動していました。これは @inbounds ブロック内でも境界チェックを強制します。プリコンパイルキャッシュファイルは境界チェックモードに固有であるため、テストされているパッケージとそのすべての依存関係は、テストが開始される前に再コンパイルする必要があることが多く、これらのキャッシュファイルは通常の開発では役に立たなくなっていました。Pkg.test は境界チェックモードをそのままにするため、テストプロセスは親 Julia セッションからそれを継承し、開発中に生成されたプリコンパイルファイルを再利用できます。以前の動作を取得するには、Pkg.test を実行する前に --check-bounds=yes を付けて Julia を起動するか、Pkg.test(; julia_args=["--check-bounds=yes"]) でフラグを明示的に渡します。

Juliaup GUI

Ian Butterworth

Julia バージョンマネージャーである Juliaup には、コマンドラインに加えてグラフィカルインターフェイスが追加されました。これは、Juliaup 1.22 以降、Juliaup がサポートするすべてのプラットフォームで提供されるため、juliaup self update の後、次のように開くことができます。

juliaup gui

Installed タブには、インストールされている各チャネルがタイルまたはリスト行として表示されます。そこからチャネルを起動したり、カスタムプロジェクト、引数、環境変数とともに起動したり、デフォルトとして設定したり、削除したりできます。また、すべてを更新したり、どのチャネルも使用しなくなったバージョンをガベージコレクションしたりするためのワンクリックアクションもあります。

[画像: The Juliaup GUI's Installed tab, showing installed Julia channels as tiles]

Available タブには、release、lts、rc、nightly、およびプルリクエストのテスト用の pr{number} チャネルを含む、チャネルデータベース内のすべてがリストされており、それぞれにインストールボタンがあります。既存の Julia バイナリをカスタムチャネル名にリンクすることもできます。Configuration タブには、バージョンデータベースの更新間隔や自動セルフアップデートなど、Juliaup の設定が公開されています。

[画像: The Juliaup GUI's Available tab, listing channels that can be installed]