Skip to content

Commit

Permalink
working on precompilation
Browse files Browse the repository at this point in the history
  • Loading branch information
onetonfoot committed Mar 28, 2023
1 parent bf9be1d commit 96e9eb8
Show file tree
Hide file tree
Showing 31 changed files with 532 additions and 272 deletions.
6 changes: 3 additions & 3 deletions examples/files.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ using HTTP

app = App()

app.get["/"] = function(stream)
app.get["/"] = function (stream)
html = Path(@__DIR__) / "html"
Bonsai.write(stream, Body(html / "index.html"))
end

app.get["/files/{path:.*}"] = function(stream)
(path,) = Bonsai.read(stream, Route(path=AbstractPath))
app.get["/files/{path:.*}"] = function (stream)
(path,) = Bonsai.read(stream, Route(path=AbstractPath))
file = Path(@__DIR__) / "data" / path
Bonsai.write(stream, Body(file))
end
Expand Down
52 changes: 52 additions & 0 deletions scripts/generate_precompile.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# generate_precompile.jl
using SnoopCompile, Bonsai, ProfileView
using Bonsai.HTTP, Bonsai.StructTypes, Bonsai.JSON3

function run_my_project()

paths = [
joinpath(@__DIR__, "../test/app.jl"),
joinpath(@__DIR__, "../test/middleware.jl"),
joinpath(@__DIR__, "../test/router.jl"),
joinpath(@__DIR__, "../test/io.jl"),
joinpath(@__DIR__, "../test/http.jl"),
joinpath(@__DIR__, "../test/handlers.jl"),
joinpath(@__DIR__, "../test/utils.jl"),
]

#=
ideally we would walk the expression and transform using
import statments from StructTypes to Bonsai.StructTypes
we could then use the test suite to generate precompile
statements without modifying the test src. But for now
well just to the dump thing a modify the src cos I cba
to get into ast right now.
If the approach works well it could be put in a package
=#

for path in paths
include(path)
end
end

tinf = @snoopi_deep run_my_project()
# tinf = @snoopi_deep include(joinpath(@__DIR__, "/test/app.jl"))

# ProfileView.@profview flamegraph(tinf)

# show(plot)
ttot, pcs = SnoopCompile.parcel(tinf);
SnoopCompile.write(joinpath(@__DIR__, "../src/precompile"), pcs)


# ProfileView.@profview plot


# using Base.Meta

# using StructTypes
# using StructTypes, JSON3

# import StructTypes: @Struct
# import StructTypes: @Struct, SomeOtherThing
79 changes: 2 additions & 77 deletions src/Bonsai.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,82 +25,7 @@ include("router.jl")
include("web_socket.jl")
include("server.jl")

@precompile_setup begin
# Putting some things in `setup` can reduce the size of the
# precompile file and potentially make loading faster.
@precompile_all_calls begin

req = Request()
res = Response()


l = [
"a",
2,
Dict(:x => 10),
[1, 2, 3],
["a", 2],
(:x, :y, :z)
]

for x in l
write(res, Body(x))
req.body = res.body
read(req, Body(typeof(x)))
end

function index(stream)
end


req.body = JSON3.write(Dict(:x => 10))
req.target = "/1/?color=blue&amount=10"
req.context[:params] = Dict(:id => string(10))

reads = [
Route(id=String),
Query(color=String, amount=Int),
Headers(content_type=Union{Nothing,String}),
Body(Dict)
]

for x in reads
try
read(req, x)
catch
end
end

app = App()
app.get["/{id}"] = index
app.middleware["**"] = [cors]

# we need to remove this call until we fix JET.jl
open_api!(app)

redirect_stderr(devnull) do
try
for i in 1:10
port = rand(1025:60000)
@async start(app)
sleep(1)
if isopen(app.cancel_token)
break
end
end
stop(app)
catch
finally
stop(app)
end
end

# all calls in this block will be precompiled, regardless of whether
# they belong to your package or not (on Julia 1.8 and higher)
# d = Dict(MyType(1) => list)
# x = get(d, MyType(2), nothing)
# last(d[MyType(1)])
end
end
precompile() = include("precompile.jl")
@precompile_all_calls precompile()

end
6 changes: 6 additions & 0 deletions src/precompile.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
try
for file in readdir("precompile")
include(file)
end
catch
end
7 changes: 7 additions & 0 deletions src/precompile/precompile_Base.Iterators.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(iterate),Zip{Tuple{Tuple{Symbol}, Tuple{DataType}}}}) # time: 0.005704592
Base.precompile(Tuple{typeof(iterate),Zip{Tuple{Tuple{Symbol, Symbol}, Tuple{DataType, Union}}},Tuple{Int64, Int64}}) # time: 0.005276247
Base.precompile(Tuple{typeof(iterate),Zip{Tuple{Tuple{Symbol, Symbol}, Tuple{DataType, Union}}}}) # time: 0.004584266
Base.precompile(Tuple{typeof(iterate),Zip{Tuple{Tuple{Symbol}, Tuple{DataType}}},Tuple{Int64, Int64}}) # time: 0.001829951
end
4 changes: 4 additions & 0 deletions src/precompile/precompile_Base.MainInclude.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(include),String}) # time: 0.004065219
end
4 changes: 4 additions & 0 deletions src/precompile/precompile_Base.Sort.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(sort!),Vector{Symbol}}) # time: 0.019723723
end
44 changes: 44 additions & 0 deletions src/precompile/precompile_Base.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(iterate),Tuple{String, Int64, String, DataType, String, Int64, String, String, String}}) # time: 0.048812304
Base.precompile(Tuple{typeof(convert),Type{Set{Type}},Set{DataType}}) # time: 0.045105405
Base.precompile(Tuple{typeof(_unique!),typeof(identity),Vector{Any},Set{DataType},Int64,Int64}) # time: 0.04072934
Base.precompile(Tuple{typeof(_str_sizehint),Any}) # time: 0.03480618
Base.precompile(Tuple{typeof(|>),Vector{Type},typeof(unique!)}) # time: 0.030403346
Base.precompile(Tuple{typeof(getindex),Vector{AbstractString},UnitRange{Int64}}) # time: 0.027484069
Base.precompile(Tuple{typeof(push!),Set{DataType},Type}) # time: 0.0239542
Base.precompile(Tuple{typeof(collect),Tuple{Symbol}}) # time: 0.022422347
Base.precompile(Tuple{typeof(print),IOBuffer,Int64}) # time: 0.020999374
Base.precompile(Tuple{typeof(setindex!),Dict{Symbol, Any},SubString{String},Symbol}) # time: 0.0163763
Base.precompile(Tuple{Type{IOBuffer}}) # time: 0.013749962
Base.precompile(Tuple{typeof(convert),Type{Vector{Pair{SubString{String}, SubString{String}}}},Vector{Pair{String, String}}}) # time: 0.012178809
Base.precompile(Tuple{typeof(vect),SubString{String},Vararg{Any}}) # time: 0.011558133
Base.precompile(Tuple{typeof(setindex_widen_up_to),Vector{LineNumberNode},Expr,Int64}) # time: 0.01036102
Base.precompile(Tuple{typeof(copyto!),Vector{Type},Int64,Vector{DataType},Int64,Int64}) # time: 0.009918058
Base.precompile(Tuple{typeof(==),NamedTuple{(:y,), Tuple{Union{Nothing, String}}},NamedTuple{(:y,), Tuple{Nothing}}}) # time: 0.006552657
Base.precompile(Tuple{typeof(setindex!),Dict{Any, Nothing},Nothing,Type{Symbol}}) # time: 0.005729434
Base.precompile(Tuple{typeof(fieldname),Type{NamedTuple{(:data,), Tuple{Vector{Float64}}}},Int64}) # time: 0.004009012
Base.precompile(Tuple{Type{Dict{Symbol, Any}}}) # time: 0.003830671
Base.precompile(Tuple{typeof(take!),IOBuffer}) # time: 0.003528405
Base.precompile(Tuple{typeof(_unique!),typeof(identity),Vector{Type},Set{Type},Int64,Int64}) # time: 0.003017
Base.precompile(Tuple{typeof(getindex),Type{Function},Function,Function}) # time: 0.002826991
Base.precompile(Tuple{typeof(setindex_widen_up_to),Vector{Expr},Float64,Int64}) # time: 0.002770563
Base.precompile(Tuple{Type{Dict{Any, Any}},Pair{Symbol, String}}) # time: 0.002733073
Base.precompile(Tuple{typeof(setindex!),Dict{Symbol, Any},Float64,Symbol}) # time: 0.002530869
Base.precompile(Tuple{typeof(getindex),Type{AbstractString},SubString{String},SubString{String},String}) # time: 0.002437909
Base.precompile(Tuple{typeof(),Type,Set{DataType}}) # time: 0.0020058
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:sizehint,), Tuple{Int64}},Type{IOBuffer}}) # time: 0.001724166
Base.precompile(Tuple{typeof(vect),Bool,Vararg{Bool}}) # time: 0.001600205
Base.precompile(Tuple{typeof(collect),Tuple{Symbol, Symbol}}) # time: 0.001600014
Base.precompile(Tuple{typeof(vect),Tuple{SubString{String}, String},Vararg{Tuple{SubString{String}, String}}}) # time: 0.001594634
Base.precompile(Tuple{typeof(_unique!),typeof(identity),Vector{Type},Set{DataType},Int64,Int64}) # time: 0.001579884
Base.precompile(Tuple{typeof(==),Dict{Symbol, String},Dict{Any, Any}}) # time: 0.001571646
Base.precompile(Tuple{typeof(setindex_widen_up_to),Vector{DataType},Any,Int64}) # time: 0.001530752
isdefined(Base, Symbol("#throw_need_pos_int#13")) && Base.precompile(Tuple{getfield(Base, Symbol("#throw_need_pos_int#13")),Int64}) # time: 0.001484336
Base.precompile(Tuple{typeof(getindex),Type{String},String,String,String,String,Vararg{String}}) # time: 0.001448045
Base.precompile(Tuple{Type{Set{DataType}}}) # time: 0.001227056
Base.precompile(Tuple{typeof(setindex!),Dict{Symbol, Any},String,Symbol}) # time: 0.001131049
Base.precompile(Tuple{RedirectStdStream,IOStream}) # time: 0.001113668
Base.precompile(Tuple{typeof(convert),Type{Union{Nothing, Float64}},Any}) # time: 0.00107563
Base.precompile(Tuple{typeof(vcat),String,String}) # time: 0.00106723
end
123 changes: 123 additions & 0 deletions src/precompile/precompile_Bonsai.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const __bodyfunction__ = Dict{Method,Any}()

# Find keyword "body functions" (the function that contains the body
# as written by the developer, called after all missing keyword-arguments
# have been assigned values), in a manner that doesn't depend on
# gensymmed names.
# `mnokw` is the method that gets called when you invoke it without
# supplying any keywords.
function __lookup_kwbody__(mnokw::Method)
function getsym(ast, arg)
isa(arg, Symbol) && return arg
isa(arg, GlobalRef) && return arg.name
if isa(arg, Core.SSAValue)
arg = ast.code[arg.id]
return getsym(ast, arg)
end
end

f = get(__bodyfunction__, mnokw, nothing)
if f === nothing
fmod = mnokw.module
# The lowered code for `mnokw` should look like
# %1 = mkw(kwvalues..., #self#, args...)
# return %1
# where `mkw` is the name of the "active" keyword body-function.
ast = Base.uncompressed_ast(mnokw)
if isa(ast, Core.CodeInfo) && length(ast.code) >= 2
callexpr = ast.code[end-1]
if isa(callexpr, Expr) && callexpr.head == :call
fsym = callexpr.args[1]
if isa(fsym, Symbol)
f = getfield(fmod, fsym)
elseif isa(fsym, GlobalRef)
if fsym.mod === Core && fsym.name === :_apply
f = getfield(mnokw.module, getsym(ast, callexpr.args[2]))
elseif fsym.mod === Core && fsym.name === :_apply_iterate
f = getfield(mnokw.module, getsym(ast, callexpr.args[3]))
else
f = getfield(fsym.mod, fsym.name)
end
else
f = missing
end
else
f = missing
end
else
f = missing
end
__bodyfunction__[mnokw] = f
end
return f
end

function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(write),Stream,Headers{NamedTuple{(:header,), Tuple{String}}}}) # time: 2.2988083
Base.precompile(Tuple{typeof(write),Response,Body{NamedTuple{(:x,), Tuple{Int64}}}}) # time: 2.1992393
Base.precompile(Tuple{typeof(headerize),Symbol}) # time: 2.164023
Base.precompile(Tuple{typeof(write),Response,Headers{NamedTuple{(:content_type,), Tuple{String}}}}) # time: 2.1166892
Base.precompile(Tuple{typeof(write),Response,Headers{NamedTuple{(:header,), Tuple{String}}}}) # time: 2.0835173
Base.precompile(Tuple{typeof(read),HTTP.Streams.Stream{A<:HTTP.Messages.Request, B<:Core.IO},Query}) # time: 0.96762437
Base.precompile(Tuple{typeof(handler_writes),Any}) # time: 0.69667375
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:id, :color), Tuple{DataType, DataType}},typeof(kw_constructor),Type}) # time: 0.11900312
Base.precompile(Tuple{typeof(getmiddleware),App,Request}) # time: 0.10577414
Base.precompile(Tuple{typeof(write),Stream,Body{NamedTuple{(:x,), Tuple{Int64}}}}) # time: 0.09205892
Base.precompile(Tuple{typeof(write),Response,Body{String},Status{201}}) # time: 0.06865481
Base.precompile(Tuple{typeof(read),Dict{Any, Any},DataType}) # time: 0.031720627
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:header,), Tuple{String}},Type{Headers}}) # time: 0.02950376
Base.precompile(Tuple{typeof(construct_error),DataType,JSON3.Object{Vector{UInt8}, Vector{UInt64}}}) # time: 0.02322583
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x,), Tuple{Int64}},Type{Body}}) # time: 0.022858528
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:color,), Tuple{DataType}},Type{Query}}) # time: 0.021564905
Base.precompile(Tuple{typeof(setindex!),CreateMiddleware,Vector{Function},String}) # time: 0.01607091
Base.precompile(Tuple{typeof(read),Request,Route{NamedTuple{(:id,), Tuple{Int64}}}}) # time: 0.016059436
Base.precompile(Tuple{typeof(read),Request,Headers{NamedTuple{(:x_next,), Tuple{String}}}}) # time: 0.015223521
Base.precompile(Tuple{typeof(write),Response,Body{PosixPath}}) # time: 0.014884812
Base.precompile(Tuple{typeof(read),Request,Query{NamedTuple{(:y,), Tuple{Union{Nothing, String}}}}}) # time: 0.014723092
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:id,), Tuple{DataType}},Type{Route}}) # time: 0.014646934
Base.precompile(Tuple{typeof(read),Request,Route{NamedTuple{(:x,), Tuple{String}}}}) # time: 0.014077245
isdefined(Bonsai, Symbol("#_field_name#5")) && Base.precompile(Tuple{getfield(Bonsai, Symbol("#_field_name#5")),Expr}) # time: 0.011492283
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x, :y), Tuple{Int64, Float64}},Type{Body}}) # time: 0.01000522
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x, :y), Tuple{DataType, DataType}},Type{Body}}) # time: 0.008596082
Base.precompile(Tuple{typeof(handler_reads),Any}) # time: 0.00729835
Base.precompile(Tuple{typeof(read),NamedTuple{(:a, :b), Tuple{Int64, Float64}},DataType}) # time: 0.006895304
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:error, :message), Tuple{String, String}},Type{Body}}) # time: 0.006244926
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:content_type,), Tuple{String}},Type{Headers}}) # time: 0.005384445
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:y,), Tuple{Union}},Type{Query}}) # time: 0.005269121
Base.precompile(Tuple{typeof(spliturl),String}) # time: 0.005232004
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x,), Tuple{DataType}},Type{Route}}) # time: 0.00513121
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x_next,), Tuple{DataType}},Type{Headers}}) # time: 0.004608333
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:x,), Tuple{DataType}},Type{Query}}) # time: 0.003575695
Base.precompile(Tuple{typeof(convert_numbers!),Dict{Any, Any},Type}) # time: 0.003463286
Base.precompile(Tuple{typeof(gethandlers),App,Request}) # time: 0.003460286
isdefined(Bonsai, Symbol("#_field_kw#6")) && Base.precompile(Tuple{getfield(Bonsai, Symbol("#_field_kw#6")),Any}) # time: 0.00333146
Base.precompile(Tuple{Type{Status},Int64}) # time: 0.003187899
Base.precompile(Tuple{typeof(write),Stream,Body{String},Status{201}}) # time: 0.003142989
Base.precompile(Tuple{typeof(combine_middleware),Vector{Function}}) # time: 0.002716805
Base.precompile(Tuple{typeof(getindex),CreateMiddleware,String}) # time: 0.002696335
Base.precompile(Tuple{typeof(gethandler),App,Request}) # time: 0.002680445
Base.precompile(Tuple{typeof(convert_numbers!),Dict{Symbol, Any},Type}) # time: 0.002601102
Base.precompile(Tuple{typeof(combine_middleware),Vector{Any}}) # time: 0.002584136
Base.precompile(Tuple{typeof(read),HTTP.Streams.Stream{A<:HTTP.Messages.Request, B<:Core.IO},Route}) # time: 0.002568127
Base.precompile(Tuple{Type{Query},DataType}) # time: 0.002217689
Base.precompile(Tuple{typeof(write),Response,Status{201}}) # time: 0.002194738
Base.precompile(Tuple{Type{Headers},DataType}) # time: 0.002117839
Base.precompile(Tuple{typeof(setindex!),CreateMiddleware,Function,String}) # time: 0.00199732
Base.precompile(Tuple{typeof(construct_error),DataType,JSON3.Object{Base.CodeUnits{UInt8, String}, Vector{UInt64}}}) # time: 0.001952182
Base.precompile(Tuple{Type{Route},Type{NamedTuple{(:id,), Tuple{Int64}}},Nothing}) # time: 0.001862422
Base.precompile(Tuple{Type{Query},Type{NamedTuple{(:y,), Tuple{Union{Nothing, String}}}},Nothing}) # time: 0.001759281
Base.precompile(Tuple{Type{Route},Type{NamedTuple{(:id, :color), Tuple{Int64, String}}},Nothing}) # time: 0.001602885
let fbody = try __lookup_kwbody__(which(kw_constructor, (Type{Route},))) catch missing end
if !ismissing(fbody)
precompile(fbody, (Base.Pairs{Symbol, DataType, Tuple{Symbol, Symbol}, NamedTuple{(:id, :color), Tuple{DataType, DataType}}},typeof(kw_constructor),Type{Route},))
end
end # time: 0.001553336
Base.precompile(Tuple{Type{Headers},Type{NamedTuple{(:x_next,), Tuple{String}}},Nothing}) # time: 0.001450776
Base.precompile(Tuple{Type{Route},Type{NamedTuple{(:x,), Tuple{String}}},Nothing}) # time: 0.001449037
Base.precompile(Tuple{Type{Body},Type{NamedTuple{(:x, :y), Tuple{String, Float64}}},Nothing}) # time: 0.001426166
Base.precompile(Tuple{Type{Route},Type{NamedTuple{(:x,), Tuple{Int64}}},Nothing}) # time: 0.001367136
Base.precompile(Tuple{typeof(get_default),Expr}) # time: 0.001248698
isdefined(Bonsai, Symbol("#_field_kw#6")) && Base.precompile(Tuple{getfield(Bonsai, Symbol("#_field_kw#6")),Expr}) # time: 0.001074699
Base.precompile(Tuple{Type{Body},PosixPath}) # time: 0.001045749
end
4 changes: 4 additions & 0 deletions src/precompile/precompile_Core.Compiler.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(istopfunction),Any,Symbol}) # time: 0.03126976
end
8 changes: 8 additions & 0 deletions src/precompile/precompile_ExproniconLite.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{Type{JLKwStruct},Expr}) # time: 0.14900176
Base.precompile(Tuple{typeof(split_field_if_match),Symbol,Expr,Bool}) # time: 0.015824387
Base.precompile(Tuple{typeof(flatten_blocks),Expr}) # time: 0.005438406
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:name, :type, :isconst, :default, :doc, :line), Tuple{Symbol, Symbol, Bool, Float64, Nothing, LineNumberNode}},Type{JLKwField}}) # time: 0.001612535
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:name, :type, :isconst, :default, :doc, :line), Tuple{Symbol, Symbol, Bool, NoDefault, Nothing, LineNumberNode}},Type{JLKwField}}) # time: 0.001531295
end
6 changes: 6 additions & 0 deletions src/precompile/precompile_FilePathsBase.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
function _precompile_()
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
Base.precompile(Tuple{typeof(joinpath),PosixPath,String}) # time: 0.06556307
Base.precompile(Tuple{typeof(Core.kwcall),NamedTuple{(:segments,), Tuple{NTuple{8, String}}},typeof(Path),PosixPath}) # time: 0.006528197
Base.precompile(Tuple{typeof(join),NTuple{8, String},String}) # time: 0.00205056
end
Loading

0 comments on commit 96e9eb8

Please sign in to comment.