Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "PrecompileTools"
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
version = "1.3.1"
version = "1.3.2"
authors = ["Tim Holy <[email protected]>", "t-bltg <[email protected]>", "and contributors"]

[deps]
Expand Down
8 changes: 6 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ end
end
```

!!! warning
`@compile_workload` should go at "top level," not compiled into a function. A pattern like `withenv(...) do @compile_workload begin ... end end`,
where `@compile_workload` appears in the anonymous function passed to `withenv`, may defeat some of the value of PrecompileTools.

When you build `MyPackage`, it will precompile the following, *including all their callees*:

- `Pair(::MyPackage.MyType, ::Vector{MyPackage.OtherType})`
Expand Down Expand Up @@ -270,14 +274,14 @@ You can also specify additional packages (e.g., dependencies of `MyPackage`) if
from an external environment. This will also keep the `precompile_workload` setting independent and avoid needless recompilation
of large environments.

Finally, it is possible to fully disable PrecompileTools.jl for all packages with
Finally, it is possible to fully disable PrecompileTools.jl for all packages with

```julia
using PrecompileTools, Preferences
set_preferences!(PrecompileTools, "precompile_workloads" => false; force=true)
```

This can be helpful to reduce the system image size generated when using PackageCompiler.jl by only compiling calls made in a precompilation script.
This can be helpful to reduce the system image size generated when using PackageCompiler.jl by only compiling calls made in a precompilation script.

## Seeing what got precompiled

Expand Down
7 changes: 6 additions & 1 deletion src/workloads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ end

@noinline is_generating_output() = ccall(:jl_generating_output, Cint, ()) == 1

macro latestworld_if_toplevel()
Expr(Symbol("latestworld-if-toplevel"))
end

function tag_newly_inferred_enable()
ccall(:jl_tag_newly_inferred_enable, Cvoid, ())
if !Base.generating_output() # for verbose[]
Expand Down Expand Up @@ -65,7 +69,7 @@ macro compile_workload(ex::Expr)
local iscompiling = :($PrecompileTools.is_generating_output() && $PrecompileTools.workload_enabled(@__MODULE__))
ex = quote
begin
Core.@latestworld # block inference from proceeding beyond this point (xref https://github.com/JuliaLang/julia/issues/57957)
$PrecompileTools.@latestworld_if_toplevel # block inference from proceeding beyond this point (xref https://github.com/JuliaLang/julia/issues/57957)
$(esc(ex))
end
end
Expand Down Expand Up @@ -113,6 +117,7 @@ macro setup_workload(ex::Expr)
return quote
if $iscompiling || $PrecompileTools.verbose[]
let
$PrecompileTools.@latestworld_if_toplevel # block inference from proceeding beyond this point (xref https://github.com/JuliaLang/julia/issues/57957)
$(esc(ex))
end
end
Expand Down
4 changes: 4 additions & 0 deletions test/NotPrecompiled/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name = "NotPrecompiled"
uuid = "82bf3039-9474-4881-94bd-824dbad9b14d"
version = "0.1.0"
authors = ["Tim Holy <[email protected]>"]
12 changes: 12 additions & 0 deletions test/NotPrecompiled/src/NotPrecompiled.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module NotPrecompiled

# This module can be a dependency of others to check
# caching: call `call_isa_bool(x::T)` for some `T` and
# then check for `isa_bool(::T)` specializations.

isa_bool(x) = isa(x, Bool)
call_isa_bool(x) = isa_bool(Base.inferencebarrier(x))

const themethod = only(methods(isa_bool))

end # module NotPrecompiled
47 changes: 47 additions & 0 deletions test/NotToplevel/Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# This file is machine-generated - editing it directly is not advised

julia_version = "1.12.0-beta1"
manifest_format = "2.0"
project_hash = "e8122786ed34aaf26503bdeb405954ce2c258c3a"

[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
version = "1.11.0"

[[deps.NotPrecompiled]]
path = "../NotPrecompiled"
uuid = "82bf3039-9474-4881-94bd-824dbad9b14d"
version = "0.1.0"

[[deps.NotToplevel]]
deps = ["PrecompileTools"]
path = "."
uuid = "3879f6fb-0a74-4dcf-89ef-07d77fe6cb90"
version = "0.1.0"

[[deps.PrecompileTools]]
deps = ["Preferences"]
path = "../.."
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
version = "1.3.1"

[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.4.3"

[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
version = "1.11.0"

[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"

[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
version = "1.11.0"
8 changes: 8 additions & 0 deletions test/NotToplevel/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name = "NotToplevel"
uuid = "3879f6fb-0a74-4dcf-89ef-07d77fe6cb90"
version = "0.1.0"
authors = ["Tim Holy <[email protected]>"]

[deps]
NotPrecompiled = "82bf3039-9474-4881-94bd-824dbad9b14d"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
19 changes: 19 additions & 0 deletions test/NotToplevel/src/NotToplevel.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module NotToplevel

using NotPrecompiled
using PrecompileTools: @setup_workload, @compile_workload

hello(who::AbstractString) = "Hello, $who"

NotPrecompiled.call_isa_bool(1.0)

@setup_workload begin
withenv() do
@compile_workload begin
hello("x")
NotPrecompiled.call_isa_bool('x')
end
end
end

end # module NotToplevel
12 changes: 12 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ using Base: specializations
end
PrecompileTools.verbose[] = oldval

using NotToplevel
# Check that calls inside @setup_workload are not precompiled
m = NotToplevel.NotPrecompiled.themethod
have_char = have_float = false
for mi in specializations(m)
mi === nothing && continue
have_char |= mi.specTypes.parameters[2] === Char
have_float |= mi.specTypes.parameters[2] === Float64
end
@test !have_float # not wrapped inside `@compile_workload`
@test_broken have_char # is wrapped
Copy link
Member Author

@timholy timholy Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mortenpi @benlorenz note that the pattern used in your reports blocks effective precompilation. If you haven't followed, here's the issue: if you compile external (outside of the package, e.g., Base or whatever) code before you reach @compile_workload, it will not be cached. The problem is that your construct forces compilation of the anonymous function before @compile_workload turns on. Thus, any code not "owned" by your package that can't be inferrably traced back to a caller in your package will be dropped from the precompile cache.

As a guide to understanding the test, know that Base.inferencebarrier is used in NotPrecompiled to force runtime dispatch, so that the isa_bool specializations cannot be traced back to NotToplevel.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also CC @jpthiele


## @recompile_invalidations

# Mimic the format written to `_jl_debug_method_invalidation`
Expand Down
Loading