Skip to content
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

Reduce the time-to-first-request by adding pregenerated precompile statements #805

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
40 changes: 40 additions & 0 deletions .github/workflows/precompile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Regenerate precompile statements
on:
workflow_dispatch:
permissions: # Permissions for the `GITHUB_TOKEN` token
contents: write
pull-requests: write
concurrency:
# Skip intermediate builds: all builds except for builds on the `master` or `release-*` branches
# Cancel intermediate builds: only pull request builds
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref != 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || github.run_number }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
regenerate_precompile_statements:
name: Regenerate precompile statements
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
- uses: julia-actions/setup-julia@v1
with:
version: 'nightly' # TODO: delete this line once Julia 1.9 is releease
# version: '1' # TODO: uncomment this line once Julia 1.9 is releease
- name: Instantiate
run: julia --project -e 'import Pkg; Pkg.instantiate(); Pkg.update(); Pkg.precompile()'
- name: Run the `contrib/precompile_generate.jl` script
run: julia --project contrib/precompile_generate.jl
- name: Create (or update) the pull request
uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 # v3.14.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: "bot/regenerate-precompile-statements"
delete-branch: true
commit-message: "🤖 Regenerate the precompile statements"
title: "🤖 Regenerate the precompile statements"
body: |
This PR regenerates the precompile statements.
Note: CI will not automatically run on this PR. To run CI, please
close and reopen the PR.
96 changes: 96 additions & 0 deletions contrib/precompile_generate.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
function gen_single_tracefile(
code::AbstractString,
tracefile::AbstractString,
)
julia_binary = Base.julia_cmd().exec[1]
cmd = `$(julia_binary)`
push!(cmd.exec, "--compile=all")
push!(cmd.exec, "--trace-compile=$(tracefile)")
push!(cmd.exec, "-e $(code)")
splitter = Sys.iswindows() ? ';' : ':'
project = Base.active_project()
env2 = copy(ENV)
env2["JULIA_LOAD_PATH"] = "$(project)$(splitter)@stdlib"
env2["JULIA_PROJECT"] = "$(project)"
run(setenv(cmd, env2))
return nothing
end

function gen_single_precompile(code::AbstractString)
str = mktempdir() do dir
tracefile = joinpath(dir, "tracefile")
gen_single_tracefile(code, tracefile)
return read(tracefile, String)::String
end::String
lines = convert(
Vector{String},
strip.(split(strip(str), '\n')),
)::Vector{String}
filter!(line -> !isempty(line), lines)
return lines
end

function gen_all_precompile()
codes = String[
"import HTTP; HTTP.get(\"https://example.com/\")",
]
all_lines = String[]
for code in codes
lines = gen_single_precompile(code)
append!(all_lines, lines)
end
unique!(all_lines)
end

function write_all_precompile(io::IO, all_lines::Vector{String})
preamble_lines = String[
"import MbedTLS",
"const MbedTLS_jll = MbedTLS.MbedTLS_jll"
]
for line in preamble_lines
println(io, line)
end
println(io)
println(io, """
function _precompile()
""")
println(io, """
if ccall(:jl_generating_output, Cint, ()) != 1
return nothing
end
""")
println(io, """
@static if Base.VERSION < v"1.9-"
Copy link
Member

Choose a reason for hiding this comment

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

This is great, thanks for the PR @DilumAluthge ! I am surprised that this is only enabled for Julia 1.9 and up. We found that adding precompile calls also gave improvements in Julia 1.8.

Copy link
Member

Choose a reason for hiding this comment

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

@DilumAluthge why this is only enabled for 1.9 and later?

Copy link
Member Author

Choose a reason for hiding this comment

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

So, I was under the impression that without JuliaLang/julia#43990, we can't cache CodeInstances from other packages, and thus the benefit of this would be far less noticeable.

# We need https://github.com/JuliaLang/julia/pull/43990, otherwise this isn't worth doing.
return nothing
end
""")
for line in all_lines
println(io, " ", line)
end
println(io)
println(io, """
return nothing
end
""")
return nothing
end

function write_all_precompile(
output_file::AbstractString,
all_lines::Vector{String},
)
open(output_file, "w") do io
write_all_precompile(io, all_lines)
end
return nothing
end

function main()
output_file = joinpath(dirname(@__DIR__), "src", "precompile.jl")
all_lines = gen_all_precompile()
write_all_precompile(output_file, all_lines)
return nothing
end

main()
3 changes: 3 additions & 0 deletions src/HTTP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -631,4 +631,7 @@ function Base.parse(::Type{T}, str::AbstractString)::T where T <: Message
return m
end

include("precompile.jl")
_precompile()

end # module
73 changes: 73 additions & 0 deletions src/precompile.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import MbedTLS
const MbedTLS_jll = MbedTLS.MbedTLS_jll

function _precompile()

if ccall(:jl_generating_output, Cint, ()) != 1
return nothing
end

@static if Base.VERSION < v"1.9-"
# We need https://github.com/JuliaLang/julia/pull/43990, otherwise this isn't worth doing.
return nothing
end

precompile(Tuple{typeof(Base.:(!=)), UInt64, UInt64})
precompile(Tuple{typeof(MbedTLS_jll.__init__)})
precompile(Tuple{typeof(MbedTLS.f_send), Ptr{Nothing}, Ptr{UInt8}, UInt64})
precompile(Tuple{typeof(MbedTLS.f_recv), Ptr{Nothing}, Ptr{UInt8}, UInt64})
precompile(Tuple{typeof(MbedTLS.__init__)})
precompile(Tuple{typeof(URIs.__init__)})
precompile(Tuple{typeof(HTTP.Parsers.__init__)})
precompile(Tuple{typeof(HTTP.CookieRequest.__init__)})
precompile(Tuple{typeof(HTTP.ConnectionRequest.__init__)})
precompile(Tuple{typeof(HTTP.Servers.__init__)})
precompile(Tuple{typeof(HTTP.MultiPartParsing.__init__)})
precompile(Tuple{typeof(HTTP.get), String})
precompile(Tuple{Type{NamedTuple{(:init,), T} where T<:Tuple}, Tuple{DataType}})
precompile(Tuple{Base.var"#reduce##kw", NamedTuple{(:init,), Tuple{DataType}}, typeof(Base.reduce), Function, Base.Set{Tuple{Union{Type{Union{}}, UnionAll}, UnionAll}}})
precompile(Tuple{typeof(Base.mapfoldl_impl), typeof(Base.identity), HTTP.var"#24#25", Type, Base.Set{Tuple{Union{Type{Union{}}, UnionAll}, UnionAll}}})
precompile(Tuple{typeof(HTTP.request), Type{HTTP.TopRequest.TopLayer{HTTP.RedirectRequest.RedirectLayer{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}}}, String, URIs.URI, Array{Pair{Base.SubString{String}, Base.SubString{String}}, 1}, Array{UInt8, 1}})
precompile(Tuple{HTTP.var"#request##kw", NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}, typeof(HTTP.request), Type{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}, URIs.URI, HTTP.Messages.Request, Array{UInt8, 1}})
precompile(Tuple{HTTP.var"#request##kw", NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}, typeof(HTTP.request), Type{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}, URIs.URI, HTTP.Messages.Request, Array{UInt8, 1}})
precompile(Tuple{typeof(Sockets.uv_getaddrinfocb), Ptr{Nothing}, Int32, Ptr{Nothing}})
precompile(Tuple{HTTP.ConnectionPool.var"#newconnection##kw", NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}, typeof(HTTP.ConnectionPool.newconnection), Type{MbedTLS.SSLContext}, Base.SubString{String}, Base.SubString{String}})
precompile(Tuple{typeof(Sockets.uv_connectcb), Ptr{Nothing}, Int32})
precompile(Tuple{typeof(Sockets.connect), Sockets.IPv4, UInt64})
precompile(Tuple{typeof(Base.setproperty!), Sockets.TCPSocket, Symbol, Int64})
precompile(Tuple{typeof(Base.notify), Base.GenericCondition{Base.Threads.SpinLock}})
precompile(Tuple{typeof(MbedTLS.f_rng), MbedTLS.CtrDrbg, Ptr{UInt8}, UInt64})
precompile(Tuple{typeof(Base.isopen), Sockets.TCPSocket})
precompile(Tuple{typeof(Base.getproperty), Sockets.TCPSocket, Symbol})
precompile(Tuple{typeof(Base.unsafe_write), Sockets.TCPSocket, Ptr{UInt8}, UInt64})
precompile(Tuple{typeof(Base.bytesavailable), Sockets.TCPSocket})
precompile(Tuple{typeof(Base.eof), Sockets.TCPSocket})
precompile(Tuple{typeof(Base.alloc_buf_hook), Sockets.TCPSocket, UInt64})
precompile(Tuple{Base.var"#readcb_specialized#671", Sockets.TCPSocket, Int64, UInt64})
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
precompile(Tuple{Base.var"#readcb_specialized#671", Sockets.TCPSocket, Int64, UInt64})

This line causes

(HTTP) pkg> precompile
Precompiling project...
  ✗ HTTP
  0 dependencies successfully precompiled in 4 seconds. 4 already precompiled.

ERROR: The following 1 direct dependency failed to precompile:

HTTP [cd3eb016-35fb-5094-929b-558a96fad6f3]

Failed to precompile HTTP [cd3eb016-35fb-5094-929b-558a96fad6f3] to /home/rik/.julia/compiled/v1.9/HTTP/jl_pwtQbZ.
ERROR: LoadError: UndefVarError: #readcb_specialized#671 not defined
Stacktrace:
 [1] _precompile()
   @ HTTP ~/git/HTTPDilum/src/precompile.jl:46
 [2] top-level scope
   @ ~/git/HTTPDilum/src/HTTP.jl:627
 [3] include
   @ ./Base.jl:427 [inlined]
 [4] 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:1423
 [5] top-level scope
   @ stdin:1
in expression starting at /home/rik/git/HTTPDilum/src/HTTP.jl:1
in expression starting at stdin:1

on Julia 1.9.0-DEV.428.

precompile(Tuple{typeof(Base.min), UInt64, Int64})
precompile(Tuple{typeof(Base.unsafe_read), Sockets.TCPSocket, Ptr{UInt8}, UInt64})
precompile(Tuple{Type{Int32}, UInt64})
precompile(Tuple{typeof(HTTP.Messages.hasheader), HTTP.Messages.Request, String})
precompile(Tuple{typeof(HTTP.Messages.ischunked), HTTP.Messages.Request})
precompile(Tuple{typeof(HTTP.Messages.writeheaders), Base.GenericIOBuffer{Array{UInt8, 1}}, HTTP.Messages.Request})
precompile(Tuple{typeof(Base.unsafe_write), MbedTLS.SSLContext, Ptr{UInt8}, UInt64})
precompile(Tuple{typeof(Base.check_open), Sockets.TCPSocket})
precompile(Tuple{MbedTLS.var"#25#26"{MbedTLS.SSLContext}})
precompile(Tuple{typeof(Base.eof), MbedTLS.SSLContext})
precompile(Tuple{HTTP.StreamRequest.var"#2#3"{HTTP.ConnectionPool.Connection, HTTP.Messages.Request, Array{UInt8, 1}, HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.ConnectionPool.Connection}}})
precompile(Tuple{typeof(Base.bytesavailable), MbedTLS.SSLContext})
precompile(Tuple{typeof(Base.unsafe_read), MbedTLS.SSLContext, Ptr{UInt8}, Int64})
precompile(Tuple{typeof(Base.readuntil), Base.GenericIOBuffer{Array{UInt8, 1}}, typeof(HTTP.Parsers.find_end_of_header)})
precompile(Tuple{typeof(Base.release), HTTP.ConnectionPool.ConnectionPools.Pool{HTTP.ConnectionPool.Connection}, Tuple{DataType, String, String, Bool, Bool}, HTTP.ConnectionPool.Connection})
precompile(Tuple{typeof(Base.isequal), Tuple{DataType, String, String, Bool, Bool}, Tuple{DataType, Base.SubString{String}, Base.SubString{String}, Bool, Bool}})
precompile(Tuple{typeof(Base.isopen), MbedTLS.SSLContext})
precompile(Tuple{MbedTLS.var"#21#23"{MbedTLS.SSLContext}, MbedTLS.SSLContext})
precompile(Tuple{MbedTLS.var"#15#16", MbedTLS.CRT})
precompile(Tuple{MbedTLS.var"#10#11", MbedTLS.CtrDrbg})
precompile(Tuple{MbedTLS.var"#8#9", MbedTLS.Entropy})
precompile(Tuple{MbedTLS.var"#17#19", MbedTLS.SSLConfig})
precompile(Tuple{typeof(Base.uvfinalize), Sockets.TCPSocket})

return nothing
end

Loading