Skip to content

Commit fdf49e2

Browse files
committed
Move the details in fake-julia/README.md [ci skip]
1 parent 04e3360 commit fdf49e2

File tree

2 files changed

+35
-45
lines changed

2 files changed

+35
-45
lines changed

README.md

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -290,30 +290,9 @@ example, the Julia method `sum!` can be called in PyJulia using
290290
### Pre-compilation mechanism in Julia 1.0
291291

292292
There was a major overhaul in the module loading system between Julia
293-
0.6 and 1.0. As a result,
294-
[the hack](https://github.com/JuliaPy/pyjulia/tree/master/julia/fake-julia)
295-
supporting the PyJulia to load PyCall stopped working.
296-
297-
To understand the issue, you need to understand a bit of details in
298-
PyCall implementation. PyCall uses Julia's precompilation mechanism
299-
to reduce JIT compilation required while Julia is loading it. This
300-
results in embedding the path to libpython used by PyCall to its
301-
precompilation cache. Furthermore, libpython ABI such as C struct
302-
layout varies across Python versions. Currently, this is determined
303-
while precompiling PyJulia and cannot be changed at run-time.
304-
Consequently, PyJulia can use the precompilation cache of PyCall
305-
created by standard Julia module loader only if the PyCall cache is
306-
compiled with the libjulia used by the current Python process. This
307-
is why PyJulia has to be imported in a Python executable dynamically
308-
linked to libpython.
309-
310-
The aforementioned hack worked by monkey-patching Julia's
311-
precompilation mechanism to emit the precompilation cache file to
312-
other directory when PyCall is used via PyJulia. However, as Juila's
313-
internal for module loading was changed after Juila 0.6, this
314-
monkey-patch does not work anymore. Similar monkey-patch in Julia 1.0
315-
can be done by using `Base.DEPOT_PATH` although it would waste more
316-
disk space than the similar hack for Julia 0.6.
293+
0.6 and 1.0. As a result, the "hack" supporting the PyJulia to load
294+
PyCall stopped working. For the implementation detail of the hack,
295+
see: https://github.com/JuliaPy/pyjulia/tree/master/julia/fake-julia
317296

318297
For the update on this problem, see:
319298
https://github.com/JuliaLang/julia/issues/28518

julia/fake-julia/README.md

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
This directory contains a python script that pretends to be the julia executable
22
and 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
55
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.
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
2732
into launching `fake-julia/julia` instead of the "real" `julia` process during precompilation. `fake-julia/julia`
2833
is actually a Python script, but it links `libjulia` and uses `libjulia` to process the command-line arguments,
2934
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
3439
should be identical to the normal Julia cache, so as an optimization `fake-julia/julia` shares the same cache
3540
file 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

Comments
 (0)