Julia 1.13 Hoogtepunten

Julia versie 1.13 is uitgebracht. We willen alle bijdragers aan deze release en alle testers die hielpen bij het vinden van regressies en problemen in de pre-releases bedanken. Zonder jullie zou deze release niet mogelijk zijn geweest.

De volledige lijst met wijzigingen is te vinden in het NEWS-bestand, maar hieronder volgt een diepgaander overzicht van enkele hoogtepunten van deze release.

Verbeteringen in latentie (TTFX)

Bijdragers: Ian Butterworth en vele anderen

Julia 1.13 doet ongeveer 30% minder tijd over om pakketten voor te compileren dan 1.12, en ongeveer 10-20% minder tijd dan 1.10 (LTS), afhankelijk van de machine.

Time To First X (TTFX) — de tijd vanaf het opstarten van Julia tot het krijgen van een eerste resultaat — bestaat uit drie hoofdkosten: het voorcompileren van pakketten, het laden ervan en het uitvoeren van de code. Met behulp van community-submitted workflows via Julia-TTFX-Snippets zijn we begonnen deze kosten systematischer te meten op basis van praktijkvoorbeelden en Julia daarop te optimaliseren.

De monitoring is nu ook onderdeel van het eigen ontwikkelproces van Julia: nieuwe TTFX CI-jobs draaien op relevante pull requests en bij elke commit naar master. De resultaten worden bijgehouden op perf.julialang.org/ttfx. Deze tracking is live gegaan op 7 september 2026; metingen vóór die datum waren ad hoc.

Ook het opstarten van Julia 1.13 is ongeveer 20% sneller dan bij 1.12.

% 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

Verbeteringen aan de REPL

Syntaxhighlighting

Bijdragers: Timothy, Kristoffer Carlsson

De Julia REPL beschikt nu over syntaxhighlighting, zonder dat er een extern pakket zoals OhMyREPL.jl geladen hoeft te worden. Standaard is het kleurenschema vrij conservatief, maar dit is eenvoudig aan te passen (zie de documentatie voor de REPL).

Nieuwe geschiedeniszoekfunctie in fzf-stijl

Bijdrager: Timothy

De geschiedeniszoekfunctie (standaard toegankelijk via Ctrl-R) is herontworpen en werkt nu vergelijkbaar met de command-line fuzzy finder fzf.

De nieuwe zoekfunctie ondersteunt onder andere:

  • Fuzzy searching in de geschiedenis.
  • Weergave van welke REPL-modus werd gebruikt voor het commando.
  • Het selecteren van meerdere zoekresultaten om in de prompt-buffer te plaatsen.
  • Syntaxhighlighting van de code, passend bij de REPL zelf.

Typ ? in de geschiedeniszoekfunctie om de volledige help te zien.

Bracketed paste op Windows

Bracketed paste zorgt ervoor dat een applicatie die in een terminal draait kan herkennen wanneer tekst wordt geplakt (in tegenstelling tot tekst die wordt getypt). Dit maakt een efficiëntere en correctere verwerking van de geplakte tekst mogelijk. Deze functionaliteit was al lang beschikbaar op Linux en macOS, maar is nu eindelijk ook beschikbaar op Windows.

@FUNCTION

Bijdragers: Miles Cranmer, Jeff Bezanson

Net als de bestaande @MODULE en @FILE macro's, verwijst de nieuwe @FUNCTION macro naar de binnenste omhullende functie, zelfs als die functie anoniem is. Dit werkt in alle soorten functies en maakt deel uit van de publieke API, in tegenstelling tot de interne variabele #self#.

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

Wijzigingen in hashing

Bijdragers: Andy Dienes, Jameson Nash

De hash-functie is vervangen. Het byte-hashing algoritme is nu RapidhashNano. Deze hash wordt standaard gebruikt voor AbstractString en veel numerieke typen zoals BigInt, Rational, en grote Real of Integer waarden. Ook is het nu veel eenvoudiger voor aangepaste typen om gebruik te maken van de generieke implementaties zonder eerst naar een ondersteund type (zoals String) te hoeven converteren.

Deze wijziging biedt verschillende voordelen ten opzichte van de vorige implementatie gebaseerd op MurmurHash3:

  • Aanzienlijk betere prestaties.
  • Het is een streaming hash, waardoor de lengte van de invoer niet vooraf bekend hoeft te zijn.
  • De implementatie is verplaatst van C naar pure Julia voor betere leesbaarheid en onderhoudbaarheid.

Prestatieverbetering bij lange strings:

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

Voorbeeld van het gebruik van een snellere fallback:

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

Ook de hash voor kleine data met een vaste breedte is gewijzigd. De uiteindelijke mixing-stap is nu een single-round XMX-constructie met zorgvuldig afgestemde constanten. De mixing-stap zorgt nu correct voor 'avalanching' bij het samenvoegen van hash-aanroepen; voorheen vereenvoudigde de mixing-stap bij elke samenvoegingsdiepte altijd tot een lineaire functie.

Deze wijziging introduceert een data-afhankelijkheid (en dus potentieel lagere prestaties) bij het sequentieel hashen van elementen in een strakke loop (bijv. foldr(hash, collection)), maar het algoritme voor het hashen van AbstractArray is voor kleine tot middelgrote maten gedeeltelijk uitgerold, waarbij meerdere hash-accumulatoren parallel worden bijgehouden, wat bij de meeste lengtes veel sneller zal zijn.

Belangrijke herinneringen:

  • Hashing blijft non-cryptografisch.
  • De standaard seed is gewijzigd.
  • Aangepaste hash-methoden moeten de seed altijd als argument accepteren, zoals hash(x::MyType, h::UInt), en nooit een standaardwaarde bieden zoals hash(x::MyType, h::UInt=0), aangezien de correcte seed wordt bepaald door de aanroeper.

Snellere GC door het overslaan van image-objecten tijdens het markeren

Bijdrager: Cody Tapscott

Elke Julia-sessie start met een groot aantal objecten die zijn geladen vanuit het systeemmaal (sysimage), en elk geladen pakket brengt zijn eigen package image mee (methodetabellen, type-informatie, gecompileerde code, constanten, etc.). Deze objecten worden nooit vrijgegeven en zelden gewijzigd, maar tot nu toe liep een volledige garbage collection door al deze objecten heen om ze als bereikbaar te markeren.

In Julia 1.13 worden objecten in de sysimage en in package images geladen als 'permanent gemarkeerd', waardoor de markeerfase ze niet meer betreedt. De weinige mutaties die wel plaatsvinden in image-objecten worden apart bijgehouden, zodat nieuwe objecten waarnaar ze verwijzen toch in leven blijven. Het resultaat is dat de kosten van een volledige collectie nu schalen met de grootte van de heap die het programma daadwerkelijk heeft gecreëerd, en niet met de hoeveelheid geladen code.

Tijd voor een volledige collectie in een nieuwe sessie:

# 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)

Tijd voor een volledige collectie (GC.gc(true)) op een Apple M4 Pro:

Scenario1.121.13
Bare session35 ms2 ms
using Revise50 ms11 ms
using Cthulhu59 ms18 ms
using PythonCall90 ms30 ms
using GLMakie187 ms68 ms

Omdat volledige collecties vaker worden getriggerd bij programma's met een grote live heap, vertaalt dit zich in een vermindering van de totale GC-tijd in echte workloads:

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)

Scheduler- en interrupt-fixes

Bijdragers: Kiran Pamnany, Jameson Nash, Ian Butterworth

Inactieve threads parkeren nu in een speciale scheduler-taak in plaats van vast te houden aan de laatste taak die ze hebben uitgevoerd, waardoor beëindigde taken direct door de garbage collector kunnen worden opgeruimd (#57544). Dit gaat samen met een reeks andere scheduler-fixes, waaronder fixes die interrupts weer betrouwbaar maken (#62665):

  • Ctrl-C bereikt weer de gebruikerscode, inclusief scripts die geblokkeerd zijn in sleep of IO, en Distributed.interrupt werkt weer.
  • De REPL overleeft herhaalde en slecht getimede Ctrl-C drukken.
  • @spawn wekt nu één inactieve thread in de threadpool van de taak, in plaats van elke thread (#61826). Code die veel gebruikmaakt van spawn is hierdoor sneller: variërend van nauwelijks merkbaar op macOS, tot 1.1-1.6x op een 16-core Linux machine, en 10-300x op Windows en zwaar overbelaste machines.
  • Diverse races die leidden tot verloren taken en deadlocks zijn opgelost.

Er wordt gewerkt aan een correct mechanisme voor taakannulering, gepland voor Julia 1.14.

Introspectie met type-annotaties

De macro's voor code-introspectie (@which, @codetyped, @codewarntype, etc.) accepteren nu aanroepsyntaxis waarbij argumenten worden opgegeven als typen in plaats van waarden, gebruikmakend van dezelfde ::T syntaxis als in methode-definities en stacktraces. Waarden en typen kunnen vrij worden gemengd, en keyword-argumenten worden ondersteund.

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

Dit betekent dat een frame direct uit een stacktrace kan worden gekopieerd en in @which kan worden geplakt om de aangeroepen methode te vinden:

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

Broadcasting-expressies worden nu ook ondersteund in @codelowered, @codetyped and @code_warntype:

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

Top-level evaluatie traceren met --trace-eval

Bijdrager: Ian Butterworth

De nieuwe command-line vlag --trace-eval toont de voortgang van de evaluatie op het hoogste niveau. Dit helpt om te zien hoe een testsuite of script vordert, bijvoorbeeld om vastlopers te identificeren.

% 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

Deze vlag is ook automatisch ingeschakeld wanneer de optie "debug logging" is geactiveerd voor een CI-run (bijvoorbeeld in GitHub Actions).

JuliaC/trim

Bijdragers: Cody Tapscott en vele anderen

Het juliac.jl script in de Julia-repository is omgezet tot een officieel pakket/applicatie: JuliaC.jl.

Er kan nu meer code worden 'getrimd' (verwijderd), zoals finalizers, @cfunction en mapreduce. Ook zijn diverse bugs in het trimming-proces zelf opgelost, wat de betrouwbaarheid verbetert.

Pkg

Bijdrager: Kristoffer Carlsson

Pkg heeft in versie 1.13 aanzienlijke aandacht gekregen. Hieronder volgen de meest opvallende wijzigingen:

Wijziging in standaard compressiealgoritme van gzip naar zstd

Voor downloads van een pakketserver (registries, pakketten en artifacts) vraagt Pkg nu standaard om een zstd-gecomprimeerd archief in plaats van een gzipped archief. Voor het type bestanden dat Pkg downloadt, biedt zstd doorgaans zowel een betere compressieratio als aanzienlijk betere decompressieprestaties.

Voorbeeld (downloads voor Plots, Makie en ModelingToolkit):

Metriekgzipzstd
Totaal aantal downloads405405
Totale downloadgrootte307.99 MB239.31 MB
Totale decompressietijd8.77 s5.50 s
Gemiddelde decompressietijd21.98 ms13.77 ms

Prestatieverbeteringen

Er zijn micro-optimalisaties doorgevoerd in de resolver en de verwerking van de registry.

Benchmark resolver-snelheid (Plots toevoegen aan een lege omgeving):

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)

Daarnaast kloont Pkg nu repositories met efficiëntere instellingen om onnodige data-downloads te voorkomen:

# 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)

Registries voor pakketten die in de manifest worden bijgehouden

Voorheen moest je handmatig zorgen dat de registries die vereist waren door een manifest beschikbaar waren. Nu wordt de registry waar elk pakket vandaan komt geregistreerd in de manifest en wordt deze automatisch geïnstalleerd bij het instantiëren van de manifest.

Recursief bronnen verzamelen

Pkg verzamelt nu recursief [sources] vermeldingen van pakketten die via URL zijn opgehaald. Hierdoor kunnen private dependency-chains worden opgelost zonder dat alle afhankelijkheden van een privépakket in een registry hoeven te staan.

pkg> add probeert nu dezelfde versie toe te voegen als reeds geladen pakketten

Julia staat toe dat de actieve project tijdens een sessie wordt gewijzigd. Dit kon leiden tot herhaaldelijk voorcompileren van pakketten wanneer een nieuwe versie werd aangetrokken die net iets afweek van een reeds geladen versie, terwijl de geladen versie vaak ook aan de compatibiliteitsvoorwaarden voldeed.

In 1.13 geeft Pkg de voorkeur aan de reeds geladen versie van een pakket bij het oplossen van pkg> add, mits de compatibiliteitsbeperkingen van de omgeving dit toelaten. Zo hoeft er niets opnieuw gecompileerd te worden. pkg> status zal nog steeds aangeven dat er een nieuwere versie beschikbaar is.

Pkg.test activeert standaard geen strikte bounds-checking meer

Voorheen startte Pkg.test altijd met --check-bounds=yes, wat bounds-checking forceerde zelfs binnen @inbounds blokken. Omdat precompile-cachebestanden specifiek zijn voor de bounds-checking modus, moesten het pakket en al zijn afhankelijkheden vaak opnieuw worden gecompileerd.

Pkg.test laat de bounds-checking modus nu ongewijzigd, zodat het testproces deze overneemt van de ouder-Julia-sessie en bestaande precompile-bestanden kan hergebruiken. Voor het oude gedrag kun je Julia starten met --check-bounds=yes of de vlag expliciet meegeven: Pkg.test(; julia_args=["--check-bounds=yes"]).

Juliaup GUI

Bijdrager: Ian Butterworth

Juliaup, de versiebeheerder voor Julia, heeft nu een grafische interface naast de command-line. Deze wordt meegeleverd met Juliaup 1.22 en later op alle ondersteunde platforms. Na een juliaup self update kan deze worden geopend met:

juliaup gui

De interface bevat de volgende tabbladen:

  • Installed: Toont elk geïnstalleerd kanaal als een tegel of lijstregel. Hiermee kan een kanaal worden gestart (eventueel met een aangepast project, argumenten en omgevingsvariabelen), als standaard worden ingesteld of worden verwijderd. Er zijn ook acties om alles bij te werken of ongebruikte versies op te ruimen.
  • Available: Lijst alles in de kanaaldatabase, inclusief release, lts, rc, nightly en pr{nummer} kanalen voor het testen van pull requests.
  • Configuration: Geeft toegang tot instellingen van Juliaup, zoals het update-interval van de versiedatabase en automatische zelf-updates.