1
1
This directory contains a python script that pretends to be the julia executable
2
2
and is used as such to allow julia precompilation to happen in the same environment.
3
3
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
5
5
a 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.
10
19
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.
19
30
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
27
32
into launching ` fake-julia/julia ` instead of the "real" ` julia ` process during precompilation. ` fake-julia/julia `
28
33
is actually a Python script, but it links ` libjulia ` and uses ` libjulia ` to process the command-line arguments,
29
34
so 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
34
39
should be identical to the normal Julia cache, so as an optimization ` fake-julia/julia ` shares the same cache
35
40
file with the real ` julia ` in that case.)
36
41
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