Comment éliminer la surcharge JIT dans un exécutable Julia (avec MWE)

9

J'utilise PackageCompiler dans l'espoir de créer un exécutable qui élimine la surcharge de compilation juste à temps.

La documentation explique que je dois définir une fonction julia_mainpour appeler la logique de mon programme, et écrire un "fichier snoop", un script qui appelle des fonctions que je souhaite précompiler. My julia_mainprend un seul argument, l'emplacement d'un fichier contenant les données d'entrée à analyser. Donc, pour garder les choses simples, mon fichier snoop fait simplement un appel à julia_mainun fichier d'entrée particulier. J'espère donc que l'exécutable généré fonctionnera bien et rapidement (pas de surcharge de compilation) lorsqu'il sera exécuté avec ce même fichier d'entrée.

Mais hélas, ce n'est pas ce que je vois. Dans une nouvelle instance de Julia, il julia_mainfaut environ 74 secondes pour la première exécution et environ 4,5 secondes pour les exécutions suivantes. Le fichier exécutable prend environ 50 secondes à chaque appel.

Mon utilisation de la build_executablefonction ressemble à ceci:

julia> using PackageCompiler

julia> build_executable("d:/philip/source/script/julia/jsource/SCRiPTMain.jl",
                        "testexecutable",
                        builddir = "d:/temp/builddir4",
                        snoopfile = "d:/philip/source/script/julia/jsource/snoop.jl",
                        compile = "all",
                        verbose = true)

Des questions:

  1. Les arguments ci-dessus sont-ils corrects pour atteindre mon objectif d'un exécutable sans surcharge JIT?
  2. Un autre conseil pour moi?

Voici ce qui se passe en réponse à cet appel à build_executable. Les lignes de Start of snoop file execution!à End of snoop file execution!sont émises par mon code.

Julia program file:
  "d:\philip\source\script\julia\jsource\SCRiPTMain.jl"
C program file:
  "C:\Users\Philip\.julia\packages\PackageCompiler\CJQcs\examples\program.c"
Build directory:
  "d:\temp\builddir4"
Executing snoopfile: "d:\philip\source\script\julia\jsource\snoop.jl"
Start of snoop file execution!
 Warning: The 'control file' contains the key 'InterpolateCovariance' with value 'true' but that is not supported. Pass a value of 'false' or omit the key altogether.
 @ ValidateInputs d:\Philip\Source\script\Julia\JSource\ValidateInputs.jl:685
Time to build model 20.058000087738037
Saving c:/temp/SCRiPT/SCRiPTModel.jls
Results written to c:/temp/SCRiPT/SCRiPTResultsJulia.json
Time to write file: 3620 milliseconds
Time in method runscript: 76899 milliseconds
End of snoop file execution!
[ Info: used 1313 out of 1320 precompile statements
Build static library "testexecutable.a":
  atexit_hook_copy = copy(Base.atexit_hooks) # make backup
# clean state so that any package we use can carelessly call atexit
empty!(Base.atexit_hooks)
Base.__init__()
Sys.__init__() #fix https://github.com/JuliaLang/julia/issues/30479
using REPL
Base.REPL_MODULE_REF[] = REPL
Mod = @eval module $(gensym("anon_module")) end
# Include into anonymous module to not polute namespace
Mod.include("d:\\\\temp\\\\builddir4\\\\julia_main.jl")
Base._atexit() # run all exit hooks we registered during precompile
empty!(Base.atexit_hooks) # don't serialize the exit hooks we run + added
# atexit_hook_copy should be empty, but who knows what base will do in the future
append!(Base.atexit_hooks, atexit_hook_copy)

Build shared library "testexecutable.dll":
  `'C:\Users\Philip\.julia\packages\WinRPM\Y9QdZ\deps\usr\x86_64-w64-mingw32\sys-root\mingw\bin\gcc.exe' --sysroot 'C:\Users\Philip\.julia\packages\WinRPM\Y9QdZ\deps\usr\x86_64-w64-mingw32\sys-root' -shared '-DJULIAC_PROGRAM_LIBNAME="testexecutable.dll"' -o testexecutable.dll -Wl,--whole-archive testexecutable.a -Wl,--no-whole-archive -std=gnu99 '-IC:\Users\philip\AppData\Local\Julia-1.2.0\include\julia' -DJULIA_ENABLE_THREADING=1 '-LC:\Users\philip\AppData\Local\Julia-1.2.0\bin' -Wl,--stack,8388608 -ljulia -lopenlibm -m64 -Wl,--export-all-symbols`
Build executable "testexecutable.exe":
  `'C:\Users\Philip\.julia\packages\WinRPM\Y9QdZ\deps\usr\x86_64-w64-mingw32\sys-root\mingw\bin\gcc.exe' --sysroot 'C:\Users\Philip\.julia\packages\WinRPM\Y9QdZ\deps\usr\x86_64-w64-mingw32\sys-root' '-DJULIAC_PROGRAM_LIBNAME="testexecutable.dll"' -o testexecutable.exe 'C:\Users\Philip\.julia\packages\PackageCompiler\CJQcs\examples\program.c' testexecutable.dll -std=gnu99 '-IC:\Users\philip\AppData\Local\Julia-1.2.0\include\julia' -DJULIA_ENABLE_THREADING=1 '-LC:\Users\philip\AppData\Local\Julia-1.2.0\bin' -Wl,--stack,8388608 -ljulia -lopenlibm -m64`
Copy Julia libraries to build directory:
  7z.dll
  BugpointPasses.dll
  libamd.2.4.6.dll
  libamd.2.dll
  libamd.dll
  libatomic-1.dll
  libbtf.1.2.6.dll
  libbtf.1.dll
  libbtf.dll
  libcamd.2.4.6.dll
  libcamd.2.dll
  libcamd.dll
  libccalltest.dll
  libccolamd.2.9.6.dll
  libccolamd.2.dll
  libccolamd.dll
  libcholmod.3.0.13.dll
  libcholmod.3.dll
  libcholmod.dll
  libclang.dll
  libcolamd.2.9.6.dll
  libcolamd.2.dll
  libcolamd.dll
  libdSFMT.dll
  libexpat-1.dll
  libgcc_s_seh-1.dll
  libgfortran-4.dll
  libgit2.dll
  libgmp.dll
  libjulia.dll
  libklu.1.3.8.dll
  libklu.1.dll
  libklu.dll
  libldl.2.2.6.dll
  libldl.2.dll
  libldl.dll
  libllvmcalltest.dll
  libmbedcrypto.dll
  libmbedtls.dll
  libmbedx509.dll
  libmpfr.dll
  libopenblas64_.dll
  libopenlibm.dll
  libpcre2-8-0.dll
  libpcre2-8.dll
  libpcre2-posix-2.dll
  libquadmath-0.dll
  librbio.2.2.6.dll
  librbio.2.dll
  librbio.dll
  libspqr.2.0.9.dll
  libspqr.2.dll
  libspqr.dll
  libssh2.dll
  libssp-0.dll
  libstdc++-6.dll
  libsuitesparseconfig.5.4.0.dll
  libsuitesparseconfig.5.dll
  libsuitesparseconfig.dll
  libsuitesparse_wrapper.dll
  libumfpack.5.7.8.dll
  libumfpack.5.dll
  libumfpack.dll
  libuv-2.dll
  libwinpthread-1.dll
  LLVM.dll
  LLVMHello.dll
  zlib1.dll
All done

julia> 

ÉDITER

J'avais peur que créer un exemple de travail minimal soit difficile, mais c'était simple:

TestBuildExecutable.jl contient:

module TestBuildExecutable

Base.@ccallable function julia_main(ARGS::Vector{String}=[""])::Cint
    @show sum(myarray())
    return 0
end

#Function which takes approx 8 seconds to compile. Returns a 500 x 20 array of 1s
function myarray()
    [1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1;
     1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1;

# PLEASE EDIT TO INSERT THE MISSING 496 LINES, EACH IDENTICAL TO THE LINE ABOVE!

     1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1;
     1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1]
end

end #module

SnoopFile.jl contient:

module SnoopFile

currentpath = dirname(@__FILE__)
push!(LOAD_PATH, currentpath)
unique!(LOAD_PATH)

using TestBuildExecutable

println("Start of snoop file execution!")
TestBuildExecutable.julia_main()
println("End of snoop file execution!")

end # module

Dans une nouvelle instance de Julia, cela julia_mainprend 8,3 secondes pour la première exécution et une demi-milliseconde pour la deuxième exécution:

julia> @time TestBuildExecutable.julia_main()
sum(myarray()) = 10000
  8.355108 seconds (425.36 k allocations: 25.831 MiB, 0.06% gc time)
0

julia> @time TestBuildExecutable.julia_main()
sum(myarray()) = 10000
  0.000537 seconds (25 allocations: 82.906 KiB)
0

Alors j'appelle ensuite build_executable:

julia> using PackageCompiler

julia> build_executable("d:/philip/source/script/julia/jsource/TestBuildExecutable.jl",
                       "testexecutable",
                       builddir = "d:/temp/builddir15",
                       snoopfile = "d:/philip/source/script/julia/jsource/SnoopFile.jl",
                       verbose = false)
Julia program file:
  "d:\philip\source\script\julia\jsource\TestBuildExecutable.jl"
C program file:
  "C:\Users\Philip\.julia\packages\PackageCompiler\CJQcs\examples\program.c"
Build directory:
  "d:\temp\builddir15"
Start of snoop file execution!
sum(myarray()) = 10000
End of snoop file execution!
[ Info: used 79 out of 79 precompile statements
All done

Enfin, dans une invite de commande Windows:

D:\temp\builddir15>testexecutable
sum(myarray()) = 1000

D:\temp\builddir15>

qui a pris (par mon chronomètre) 8 secondes pour s'exécuter, et il faut 8 secondes pour s'exécuter à chaque fois qu'il est exécuté, pas seulement la première fois. Ceci est cohérent avec l'exécutable effectuant une compilation JIT à chaque exécution, mais le fichier snoop est conçu pour éviter cela!

Information sur la version:

julia> versioninfo()
Julia Version 1.2.0
Commit c6da87ff4b (2019-08-20 00:03 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)
Environment:
  JULIA_NUM_THREADS = 8
  JULIA_EDITOR = "C:\Users\Philip\AppData\Local\Programs\Microsoft VS Code\Code.exe"
Philip Swannell
la source

Réponses:

1

On dirait que vous utilisez Windows.

À un certain point, PackageCompiler.jl sera mature pour Windows et vous pourrez l'essayer.

xiaodai
la source
Merci d'avoir fait remarquer cela. J'étais au courant de PackageCompilerX, et il est encourageant de noter qu'il est en cours de développement actif avec une sortie aujourd'hui (11 décembre 19). Je vais l'expérimenter, mais je devrai peut-être être patient!
Philip Swannell
1

La solution était en effet d'attendre les progrès PackageCompilerX, comme suggéré par @xiaodai.

Le 10 février 2020, ce qui était auparavant PackageCompilerXdevenu une nouvelle (version 1.0 de) PackageCompiler, avec une API considérablement modifiée et une documentation plus complète.

En particulier, le MWE ci-dessus ( muté pour la nouvelle API PackageCompiler) fonctionne désormais correctement sans surcharge JIT.

Philip Swannell
la source