Flux.jl: Regression in package load time

Created on 1 May 2020  Β·  14Comments  Β·  Source: FluxML/Flux.jl

There seems to have been a regression in package load time. I've seen this on MacOS (no CUDA) and Linux on Aarch64 (with CUDA)

   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.4.1 (2020-04-14)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |


julia> @time using Flux
[ Info: Precompiling Flux [587475ba-b771-5e3f-ad9e-33799f191a9c]
 87.462174 seconds (55.38 M allocations: 2.974 GiB, 1.08% gc time)
julia> exit()
...
julia> @time using Flux
 17.692934 seconds (54.86 M allocations: 2.950 GiB, 4.54% gc time)

Worth noting that if Revise is loaded, there's also an additional wait for the REPL to be available again, which seems to be demonstratable via. @time @time (Revise issue https://github.com/timholy/Revise.jl/issues/456):

julia> @time @time using Flux
 18.218871 seconds (54.86 M allocations: 2.951 GiB, 4.66% gc time)
 29.519402 seconds (93.88 M allocations: 4.444 GiB, 4.69% gc time)
  [587475ba] Flux v0.10.4
  [295af30f] Revise v2.6.4

Most helpful comment

This is awesome!

All 14 comments

Probably related to a recent Zygote release, and related to FluxML/Zygote.jl#607

Could we try with explicitly loading flux with a few old zygote releases to help bisect the issue?

This was happening before zygote https://github.com/FluxML/Zygote.jl/pull/607. I’m happy to try to bisect it

That would be really good, if you could take a look at it

Thanks @ianshmean !

Bisecting Zygote load times on 1.4.1 on MacOS (without Revise loaded). v0.4.4 hits a Deprecated syntax '(;)', so I stopped at v0.4.5

β”‚ Row β”‚ version   β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 4.51003 β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 4.48009 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 4.48958 β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 19.0388 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 18.8542 β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 20.1503 β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 20.1037 β”‚
β”‚ 8   β”‚ v"0.4.12" β”‚ 21.7398 β”‚
β”‚ 9   β”‚ v"0.4.11" β”‚ 21.6091 β”‚
β”‚ 10  β”‚ v"0.4.10" β”‚ 19.4583 β”‚
β”‚ 11  β”‚ v"0.4.9"  β”‚ 19.1006 β”‚
β”‚ 12  β”‚ v"0.4.8"  β”‚ 18.8055 β”‚
β”‚ 13  β”‚ v"0.4.7"  β”‚ 18.7565 β”‚
β”‚ 14  β”‚ v"0.4.6"  β”‚ 18.612  β”‚
β”‚ 15  β”‚ v"0.4.5"  β”‚ 19.0294 β”‚

https://github.com/FluxML/Zygote.jl/pull/607 was released in 0.4.17, so for Zygote alone it improved load time (as to be expected, given less code run during init), but that might have a knock-on detrimental effect on Flux load times

Maybe this is a 1.4.1 issue. I'll try on 1.3.1 too

Script used:

function runjulia(e)
    run(`$(joinpath(Sys.BINDIR, Base.julia_exename())) --project=$(@__DIR__) --startup-file=no -e "$e"`)
end

using BenchmarkTools, Pkg, DataFrames
Pkg.activate(@__DIR__)
Pkg.up()
reg = "/home/leuko/.julia/registries/General/Z/Zygote/Versions.toml"
regdict = Pkg.TOML.parsefile(reg)
versions = reverse(sort(VersionNumber.(keys(regdict))))
df = DataFrame(version=VersionNumber[], seconds=Float64[])
for v in versions
    @show v
    Pkg.add(Pkg.PackageSpec(;name="Zygote", version=v))
    runjulia("using Zygote") #do any precomp before @benchmark evaluates how fast the function will run
    t = @benchmark runjulia("using Zygote")
    @show t.times ./ 1e9
    append!(df, DataFrame(version=v, seconds=median(t).time / 1e9))
end

Zygote load times on 1.3.1 on MacOS

β”‚ Row β”‚ version   β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 6.29911 β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 6.38034 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 7.0203  β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 19.6958 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 19.6893 β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 20.2707 β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 19.5318 β”‚
β”‚ 8   β”‚ v"0.4.12" β”‚ 17.4778 β”‚

Zygote load times on 1.3.1 on Ubuntu x86_64 with an NVIDIA gpu

β”‚ Row β”‚ version   β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 5.35842 β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 5.34299 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 5.33254 β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 5.35051 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 5.36513 β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 5.33799 β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 5.32486 β”‚
β”‚ 8   β”‚ v"0.4.12" β”‚ 5.522   β”‚
β”‚ 9   β”‚ v"0.4.11" β”‚ 5.38264 β”‚
β”‚ 10  β”‚ v"0.4.10" β”‚ 5.36026 β”‚
β”‚ 11  β”‚ v"0.4.9"  β”‚ 5.3674  β”‚
β”‚ 12  β”‚ v"0.4.8"  β”‚ 5.34033 β”‚
β”‚ 13  β”‚ v"0.4.7"  β”‚ 5.41108 β”‚
β”‚ 14  β”‚ v"0.4.6"  β”‚ 5.36357 β”‚
β”‚ 15  β”‚ v"0.4.5"  β”‚ 5.34436 β”‚
β”‚ 16  β”‚ v"0.4.4"  β”‚ 5.3155  β”‚
β”‚ 17  β”‚ v"0.4.3"  β”‚ 5.3329  β”‚
β”‚ 18  β”‚ v"0.4.2"  β”‚ 5.29466 β”‚
β”‚ 19  β”‚ v"0.4.1"  β”‚ 5.32087 β”‚
β”‚ 20  β”‚ v"0.4.0"  β”‚ 5.34381 β”‚
β”‚ 21  β”‚ v"0.3.4"  β”‚ 5.32319 β”‚
β”‚ 22  β”‚ v"0.3.3"  β”‚ 5.30647 β”‚
β”‚ 23  β”‚ v"0.3.2"  β”‚ 5.3183  β”‚

Julia v1.3.1

β”‚ Row β”‚ version   β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 4.32729 β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 4.17895 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 4.19689 β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 15.3199 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 14.2902 β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 14.4786 β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 14.2482 β”‚
β”‚ 8   β”‚ v"0.4.12" β”‚ 13.0433 β”‚

Julia v1.4.1

β”‚ Row β”‚ version   β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 2.2077  β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 2.20337 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 2.20106 β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 12.8356 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 12.8612 β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 12.9725 β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 13.0078 β”‚
β”‚ 8   β”‚ v"0.4.12" β”‚ 14.8832 β”‚
oliver@debian:~$ uname -a
Linux debian 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1+deb10u1 (2020-04-27) x86_64 GNU/Linux
oliver@debian:~$ nvidia-smi
Sat May  2 14:27:02 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.74       Driver Version: 418.74       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
oliver@debian:~$ julia
julia> versioninfo()
Julia Version 1.4.1
Commit 381693d3df* (2020-04-14 17:20 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-8.0.1 (ORCJIT, skylake)

I've been experiencing the same load time regression (also on Ubuntu/x64/NVIDIA GPU). Using @ianshmean's method, the sum of the median times for each dependency is around 12s. For reference, Flux takes ~16s.

β”‚ Row β”‚ name           β”‚ seconds   β”‚
β”‚     β”‚ String         β”‚ Float64   β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ AbstractTrees  β”‚ 0.156644  β”‚
β”‚ 2   β”‚ Adapt          β”‚ 0.131289  β”‚
β”‚ 3   β”‚ CodecZlib      β”‚ 0.318383  β”‚
β”‚ 4   β”‚ Colors         β”‚ 0.783943  β”‚
β”‚ 5   β”‚ CuArrays       β”‚ 2.91121   β”‚
β”‚ 6   β”‚ DelimitedFiles β”‚ 0.0923371 β”‚
β”‚ 7   β”‚ Juno           β”‚ 0.326367  β”‚
β”‚ 8   β”‚ MacroTools     β”‚ 0.238282  β”‚
β”‚ 9   β”‚ NNlib          β”‚ 0.315076  β”‚
β”‚ 10  β”‚ Pkg            β”‚ 0.0922212 β”‚
β”‚ 11  β”‚ Printf         β”‚ 0.0922388 β”‚
β”‚ 12  β”‚ Random         β”‚ 0.0922166 β”‚
β”‚ 13  β”‚ Reexport       β”‚ 0.0938258 β”‚
β”‚ 14  β”‚ SHA            β”‚ 0.0922245 β”‚
β”‚ 15  β”‚ Statistics     β”‚ 0.0922111 β”‚
β”‚ 16  β”‚ StatsBase      β”‚ 0.379776  β”‚
β”‚ 17  β”‚ Test           β”‚ 0.0923305 β”‚
β”‚ 18  β”‚ ZipFile        β”‚ 0.296342  β”‚
β”‚ 19  β”‚ Zygote         β”‚ 5.51149   β”‚

MacOS using Flux time for Flux 0.10.3, with all compatible Zygote versions:

β”‚ Row β”‚ zygote_v  β”‚ seconds β”‚
β”‚     β”‚ VersionN… β”‚ Float64 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ v"0.4.19" β”‚ 14.8627 β”‚
β”‚ 2   β”‚ v"0.4.18" β”‚ 13.8563 β”‚
β”‚ 3   β”‚ v"0.4.17" β”‚ 13.9715 β”‚
β”‚ 4   β”‚ v"0.4.16" β”‚ 27.2192 β”‚
β”‚ 5   β”‚ v"0.4.15" β”‚ 28.211  β”‚
β”‚ 6   β”‚ v"0.4.14" β”‚ 27.799  β”‚
β”‚ 7   β”‚ v"0.4.13" β”‚ 28.2803 β”‚

Attempting to find precompile invalidations, I tried setting JL_DEBUG_METHOD_INVALIDATION=1 in src/gf.c on julia master and rebuilding, then running using Flux

https://gist.github.com/ianshmean/3c4de43322d83e31961186ec5913538e

ctrl-f on the string ">>" through that file to see the sources of invalidation. The biggest by count of functions recompiled is:
>> ColorTypes.convert(...) Tuple{typeof(Base.convert), Type{C}, Any} where C<:(ColorTypes.Colorant{T, N} where N where T)

and a third of them are from StaticArrays

Interesting. First thing is that load time has come down a bit recently, which is great.

StaticArrays isn't a direct dependency, so something if off there. We could also remove the Colors dependency to investigate if that has any impact on load times.

Pinging @KristofferC for his thoughts, and @MikeInnes if he has thoughts about this

Work being done upstream that may impact this positively https://github.com/JuliaLang/julia/pull/35714

This is awesome!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

davidljung picture davidljung  Β·  4Comments

pylat picture pylat  Β·  3Comments

xiaodaigh picture xiaodaigh  Β·  5Comments

MikeInnes picture MikeInnes  Β·  3Comments

mkborregaard picture mkborregaard  Β·  5Comments