@elrod and I are running into problems when trying to make LoopVectorization.jl relocatable. (for example, compiling a sysimage that includes LoopVectorization.jl on machine A, and then using that sysimage on machine B).
The basic idea is that we have global constants that depend on the specific CPU architecture.
As an example:
module Foo
import CpuId
struct IntelCpu end
struct OtherCpu end
const CPU_BRAND = if startswith(CpuId.cpubrand(), "Intel(R) ")
IntelCpu()
else
OtherCpu()
end
do_stuff() = do_stuff(CPU_BRAND)
do_stuff(::IntelCpu) = 1
do_stuff(::OtherCpu) = 1.0
end # module
Unfortunately, the above code is not relocatable. If I compile my package Foo.jl in a sysimage or app using a computer with an Intel CPU, and then I try to move my sysimage or app to a computer with a non-Intel CPU, bad things will happen.
So I figure that because the information I need about the CPU architecture is not available until runtime, I should move that logic into __init__
. So I try this instead:
module Foo
import CpuId
struct IntelCpu end
struct OtherCpu end
do_stuff() = do_stuff(CPU_BRAND)
do_stuff(::IntelCpu) = 1
do_stuff(::OtherCpu) = 1.0
function __init__()
if startswith(CpuId.cpubrand(), "Intel(R) ")
@eval const CPU_BRAND = IntelCpu()
else
@eval const CPU_BRAND = OtherCpu()
end
return nothing
end
end # module
Unfortunately, this will break precompilation. If I have a package Bar.jl that depends on Foo.jl, e.g. this:
module Bar
import Foo
end # module
When I try to do import Bar
, I get this error:
julia> import Bar
[ Info: Precompiling Bar [f4235cf3-1c45-4253-b7f4-6bb3fb59c5c4]
ERROR: LoadError: InitError: Evaluation into the closed module `Foo` breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating `Foo` with `eval` during precompilation - don't do this.
Stacktrace:
[1] eval
@ ./boot.jl:369 [inlined]
[2] __init__()
@ Foo ~/Downloads/MWE-eval/Foo.jl/src/Foo.jl:10
[3] _include_from_serialized(path::String, depmods::Vector{Any})
@ Base ./loading.jl:670
[4] _require_from_serialized(path::String)
@ Base ./loading.jl:723
[5] _require(pkg::Base.PkgId)
@ Base ./loading.jl:1027
[6] require(uuidkey::Base.PkgId)
@ Base ./loading.jl:910
[7] require(into::Module, mod::Symbol)
@ Base ./loading.jl:897
[8] include
@ ./Base.jl:386 [inlined]
[9] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
@ Base ./loading.jl:1209
[10] top-level scope
@ none:1
[11] eval
@ ./boot.jl:369 [inlined]
[12] eval(x::Expr)
@ Base.MainInclude ./client.jl:453
[13] top-level scope
@ none:1
during initialization of module Foo
in expression starting at /Users/dilum/Downloads/MWE-eval/Bar.jl/src/Bar.jl:1
ERROR: Failed to precompile Bar [f4235cf3-1c45-4253-b7f4-6bb3fb59c5c4] to /Users/dilum/.julia/compiled/v1.7/Bar/jl_Q6bC82.
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:33
[2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::Base.TTY, internal_stdout::Base.TTY)
@ Base ./loading.jl:1356
[3] compilecache(pkg::Base.PkgId, path::String)
@ Base ./loading.jl:1302
[4] _require(pkg::Base.PkgId)
@ Base ./loading.jl:1017
[5] require(uuidkey::Base.PkgId)
@ Base ./loading.jl:910
[6] require(into::Module, mod::Symbol)
@ Base ./loading.jl:897
Any ideas on how we might accomplish this?