Skip to content

Conversation

@timholy
Copy link
Member

@timholy timholy commented Jul 19, 2022

This small package makes it easy to exhaustively precompile on
Julia 1.8 and higher. In brief, your package should look like

module MyPackage

using SnoopPrecompile

# method definitions etc

let
    @precompile_calls begin
        # execute the code you want precompiled
    end
end

end # module MyPackage

There are also facilities for "setup" code, see the documentation
for details.

This small package makes it easy to exhaustively precompile on
Julia 1.8 and higher. In brief, your package should look like

```
module MyPackage

using SnoopPrecompile

# method definitions etc

let
    @precompile_calls begin
        # code you want precompiled
    end
end

end # module MyPackage
```

There are also facilities for "setup" code, see the documentation
for details.
@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

CC @ChrisRackauckas, @SimonDanisch, @rikhuijzer, @LilithHafner, @IanButterworth. This gets both the inferrable and runtime-dispatched calls. It also bundles in the if ccall(:jl_generating_output, Cint, ()) == 1.

@codecov
Copy link

codecov bot commented Jul 19, 2022

Codecov Report

Merging #282 (3d34c0c) into master (644081f) will decrease coverage by 3.92%.
The diff coverage is 82.75%.

@@            Coverage Diff             @@
##           master     #282      +/-   ##
==========================================
- Coverage   86.44%   82.52%   -3.93%     
==========================================
  Files          16       17       +1     
  Lines        2022     1946      -76     
==========================================
- Hits         1748     1606     -142     
- Misses        274      340      +66     
Impacted Files Coverage Δ
SnoopPrecompile/src/SnoopPrecompile.jl 82.75% <82.75%> (ø)
src/invalidation_and_inference.jl 0.00% <0.00%> (-87.94%) ⬇️
SnoopCompileCore/src/snoopi.jl 55.73% <0.00%> (-6.09%) ⬇️
src/invalidations.jl 86.11% <0.00%> (-2.38%) ⬇️
src/parcel_snoopi_deep.jl 88.21% <0.00%> (-1.35%) ⬇️
src/deep_demos.jl 57.89% <0.00%> (-1.08%) ⬇️
src/write.jl 91.30% <0.00%> (-1.01%) ⬇️
src/parcel_snoopl.jl 89.47% <0.00%> (-0.53%) ⬇️
SnoopCompileCore/src/snoopi_deep.jl 91.66% <0.00%> (-0.34%) ⬇️
src/parcel_snoopi.jl 93.83% <0.00%> (-0.31%) ⬇️
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 644081f...3d34c0c. Read the comment docs.

@rikhuijzer
Copy link
Contributor

So basically this approach falls in between running a workload and calling precompile manually? Compared to the workload, it doesn't cause side-effects and compared to calling precompile it will also call runtime dispatches?

Why is SnoopPrecompile located inside the SnoopCompile repo and not in a separate one?

@LilithHafner
Copy link

LilithHafner commented Jul 19, 2022

let
    @precompile_calls begin
        # execute the code you want precompiled
    end
end

Wouldn't it be easier for the macro to introduce the let block?

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

So basically this approach falls in between running a workload and calling precompile manually? Compared to the workload, it doesn't cause side-effects and compared to calling precompile it will also call runtime dispatches?

More precisely, it supplements "running a workload" with interception of runtime-dispatched calls. Even on Julia 1.8, those are not precompiled if the method called belongs to another package. Precompilation requires a chain of inference back to the package being precompiled, unless there's an explicit precompile directive.

Compared to the workload, it doesn't cause side-effects

it does cause the same side effects (the workload actually runs)

compared to calling precompile it will also call runtime dispatches?

exactly.

Why is SnoopPrecompile located inside the SnoopCompile repo and not in a separate one?

No major reason but there's no disadvantage. (It does have code in common with SnoopCompileCore so it may be slightly easier to maintain this way.) It will be an independently-registered package in its own right.

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

Wouldn't it be easier for the macro to introduce the let block?

Handling :setup code that way would have required

@precompile_calls :setup begin
    vars = ...
    @precompile_calls begin
        somecall(vars...)
    end
end

since vars needs to be in scope for somecall(vars...). I got the impression you didn't like the nested approach. But I can go that way if you prefer it.

@LilithHafner
Copy link

Using that approach, @precompile_calls without :setup could still let-fence itself.

Another approach uses only a single macro call, following the style of BenchmarkTools.@benchmark:

@precompile_calls begin
    somecalls(vars...)
end setup = (vars = ...)

@rikhuijzer
Copy link
Contributor

Precompilation requires a chain of inference back to the package being precompiled, unless there's an explicit precompile directive.

Check, so that is done by the precompile_roots it seems. And what does the @force_compile do in this context? It isn't clear to me based on the PR at Julia base.

For the macro name @precompile_calls, I would suggest starting with a verb since most functions and macro's either start with a verb (e.g., @time, @benchmark and @debug) or state the output (e.g., @code_llvm and @lazy_str). Maybe @call_precompile?

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

For the macro name @precompile_calls, I would suggest starting with a verb since most functions and macro's either start with a verb (e.g.,@time, @benchmark and @debug) or state the output (e.g., @code_llvm and @lazy_str). Maybe @call_precompile?

It does 🙂 . I'm precompileing the calls in that expression!

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

But of course happy to accept other names, given that it's obviously confusing!

@LilithHafner
Copy link

It does 🙂 . I'm precompileing the calls in that expression!

That is 100% obvious now that you say it, but I also didn't realize that precompile was a verb in that context.

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

If a name change doesn't seem in the offing, clearly it needs at least a docstring.

@timholy
Copy link
Member Author

timholy commented Jul 19, 2022

I think I've addressed most of the feedback. Some comments:

  • I was worried that there could be a large amount of setup code, so the BenchmarkTools setup = (...) syntax began to seem a little less appealing. I went with the nested approach. Alternatively we could have @precompile_setup and @precompile_calls.
  • Sadly, we seem to have a bug in our ability to disable the interpreter, so automatic let-fencing is not yet possible. Until then, you can use manual fencing at top level, but be sure to check results with verbose[] = true.
  • I still use the same name but I'm happy to change it if there is a better one. I did add a docstring which may help explain the rationale better.

@timholy
Copy link
Member Author

timholy commented Jul 20, 2022

I'm beginning to lean towards @precompile_setup and @precompile_all, since people found @precompile_calls confusing. Thoughts?

@rikhuijzer
Copy link
Contributor

+1 for @precompile_all

@jkrumbiegel
Copy link

How about @precompile_all_calls, it's longer but clear in that calls are the object not the verb, and otherwise you don't know from reading what's all.

@timholy
Copy link
Member Author

timholy commented Jul 21, 2022

I went with @precompile_setup and @precompile_all_calls. Barring any additional comments I will merge and register the package.

@timholy timholy deleted the teh/snoopprecompile branch July 22, 2022 09:13
@timholy
Copy link
Member Author

timholy commented Jul 22, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants