-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Precompiling and FunctionWrappers lead to extra runtime allocations #54832
Comments
Just FYI, this problem is reproduced by using Also, |
We now have PkgCacheInspector.jl. However, I could not find the cause. Edit: |
Despite the use of function pointers, this problem does not occur in the following simple case. function test_precomp()
ptr = @cfunction(cos, Float64, (Float64,))
ccall(ptr, Float64, (Float64,), 0.0)
end Edit: Edit2: |
In case it's a clue: I see allocations like this from FunctionWrappers used in precompiled code in a large codebase (the allocations go away if I don't precompile), but only on Apple Silicon (unlike the minimal case above). For some reason, the problem goes away on x86. Perhaps related to JuliaLang/FunctionWrappers.jl#30? |
This looks potentially related - i'm not sure if it's entirely the same. If not, we can extract it into a separate ticket, but it sounds very similar to what you're describing. Here is an MRE that somehow assigning the result of a module TestFP
mutable struct Record
x::Int
end
Base.@ccallable function update_record!(r::Record)::Int
r.x += 1
end
make_fp1() = @cfunction(update_record!, Int, (Any,))
make_fp2() = @cfunction(update_record!, Int, (Any,))
# Call both functions, so that the difference isn't in which ones are _called_:
@time make_fp1()
@time make_fp2()
mutable struct PointerHolder
ptr::Ptr{Cvoid}
end
# Somehow, assigning the @cfunction constructor at package precompile time to a const value
# has a destructive side-effect on the performance of calls through the pointer returned by
# make_fp2(). Calling through the result of make_fp2() causes dynamic dispatch, while
# make_fp1() does not.
const FP2 = PointerHolder(make_fp2()) # (Even though this will be 0'd out during serialization)
end # module TestFP julia> using TestFP, BenchmarkTools
Info Given TestFP was explicitly requested, output will be shown live
0.000000 seconds
0.000000 seconds
Precompiling TestFP finished.
1 dependency successfully precompiled in 1 seconds
1 dependency had output during precompilation:
┌ TestFP
│ [Output was shown above]
└
@
julia> @btime ccall($(TestFP.make_fp1()), Int, (Any,), $(TestFP.Record(0)))
4.708 ns (0 allocations: 0 bytes)
500502
julia> @btime ccall($(TestFP.make_fp2()), Int, (Any,), $(TestFP.Record(0)))
15.348 ns (1 allocation: 16 bytes)
500502 |
Might be worth checking for |
Oh, yes, I do see that! 👍 What do I take away from that? |
Discovering any sort of pattern about what is getting omitted might help. My first thought would be to try some of the debugging that starts here: #35972 (comment) |
Precompiling a function that constructs and calls FunctionWrappers seems to make each call to the FunctionWrapper allocate.
Here's my test package:
and here's what happens on Julia 1.10.4:
Also, is it odd that
test_precomp
needs to compile on first use, buttest_no_precomp
does not?Originally posted by @amilsted in #35972 (comment)
Confirmed on nightly by @kimikage: #35972 (comment)
The text was updated successfully, but these errors were encountered: