11This directory contains a python script that pretends to be the julia executable
22and is used as such to allow julia precompilation to happen in the same environment.
33
4- When a Julia module Foo marked with ` __precompile__(true) ` is imported in Julia, it gets "precompiled" to
4+ When a Julia module ` Foo ` is imported in Julia, it gets "precompiled" to
55a Foo.ji cache file that speeds up subsequent loads. See:
6- https://docs.julialang.org/en/stable/manual/modules/#Module-initialization-and-precompilation-1
7- A key thing to understand is that this precompilation works by * launching a new Julia process*
8- that loads the module in a special "output-ji" mode (by running ` julia --output-ji ` ) that creates
9- the cache file.
6+ [ Module initialization and precompilation] ( https://docs.julialang.org/en/stable/manual/modules/#Module-initialization-and-precompilation-1 )
7+ in Julia manual. PyCall uses this precompilation mechanism to reduce
8+ JIT compilation required during its initialization. This results in
9+ embedding the path to ` libpython ` used by PyCall to its precompilation
10+ cache. Furthermore, ` libpython ` ABI such as C struct layout varies
11+ across Python versions. Currently, this is determined while
12+ precompiling PyJulia and cannot be changed at run-time. Consequently,
13+ PyJulia can use the precompilation cache of PyCall created by standard
14+ Julia module loader only if the PyCall cache is compiled with the
15+ ` libpython ` used by the current Python process. This, of course,
16+ requires the Python executable to be dynamically linked to
17+ ` libpython ` in the first place. Furthermore, it also applies to any
18+ Julia packages using PyCall.
1019
11- A second key thing to understand is that pyjulia is using PyCall configured in a different way than
12- when PyCall is called from with a ` julia ` process. Within a ` julia ` process, PyCall works by loading
13- ` libpython ` to call the CPython API. Within a ` python ` process (for ` pyjulia ` ), at least if
14- ` python ` is statically linked to ` libpython ` , PyCall works instead by loading CPython API symbols from
15- the ` python ` process itself. This difference affects how PyCall functions are compiled, which means
16- that * pyjulia cannot use the same PyCall.ji cache file* as julia. This extends to any Julia module
17- * using* PyCall: every such module needs to have a precompiled cache file that is different from the ordinary
18- Julia module cache.
20+ If ` python ` is statically linked to ` libpython ` , PyJulia has to use
21+ PyCall in a mode that loads CPython API symbols from the ` python `
22+ process itself. Generating a precompilation cache compatible with
23+ this mode requires to do it within a _ ` python ` _ process. A key thing
24+ to notice here is that the precompilation in Julia works by * launching
25+ a new process* that loads the module in a special "output-ji" mode (by
26+ running ` julia --output-ji ` ) that creates the cache file. Thus, we
27+ need to configure Julia in such a way that it uses our custom
28+ executable script that behaves like ` julia ` program for the
29+ precompilation.
1930
20- The combination of these two facts mean that when PyCall, or any Julia module that uses PyCall,
21- is loaded from pyjulia with a statically linked ` python ` , we have to precompile a separate version of it.
22- Since "normal" precompilation launches a new ` julia ` process, this process would create the wrong
23- (` libpython ` ) version of the PyCall cache file. So, we have to force precompilation to launch
24- a ` python ` process, not a ` julia ` process, so that PyCall is compiled correctly for running inside ` python ` .
25-
26- That is what ` fake-julia ` does. By changing the ` JULIA_HOME ` (v0.6) or ` JULIA_BINDIR ` (v0.7+) environment variable, we trick Julia
31+ That is what ` fake-julia ` does. By changing the ` JULIA_HOME ` (v0.6) we trick Julia
2732into launching ` fake-julia/julia ` instead of the "real" ` julia ` process during precompilation. ` fake-julia/julia `
2833is actually a Python script, but it links ` libjulia ` and uses ` libjulia ` to process the command-line arguments,
2934so it mimics the behavior of the ` julia ` process. Since ` fake-julia/julia ` is running from within the ` python `
@@ -34,4 +39,10 @@ compiling PyCall and other Julia modules that use PyCall. For other Julia modu
3439should be identical to the normal Julia cache, so as an optimization ` fake-julia/julia ` shares the same cache
3540file with the real ` julia ` in that case.)
3641
37- See also the discussion in https://github.com/JuliaPy/PyCall.jl/pull/293 and https://github.com/JuliaPy/pyjulia/pull/54
42+ Unfortunately, this "hack" does not work for Julia 0.7 and above due
43+ to the change in the module loading system. For ongoing discussion,
44+ see: https://github.com/JuliaLang/julia/issues/28518
45+
46+ For the discussion during the initial implementation, see also:
47+ https://github.com/JuliaPy/PyCall.jl/pull/293 and
48+ https://github.com/JuliaPy/pyjulia/pull/54
0 commit comments