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

TTFX time of request is high. MVP #1194

Closed
Sixzero opened this issue Nov 18, 2024 · 17 comments · Fixed by #1195
Closed

TTFX time of request is high. MVP #1194

Sixzero opened this issue Nov 18, 2024 · 17 comments · Fixed by #1195

Comments

@Sixzero
Copy link

Sixzero commented Nov 18, 2024

I would think this should run in 0.1seconds even on first call (not 6 seconds), because it is one of the most common way we construct a POST request.

(method, url, serialized_body) = (:POST, "http://localhost:9000", "{\"content\":\"Hello\"}", )
@time HTTP.request(method, url; body=serialized_body)
@time HTTP.request(method, url; body=serialized_body);
  5.951766 seconds (7.45 M allocations: 502.251 MiB, 2.99% gc time, 115.65% compilation time)
  0.000566 seconds (153 allocations: 144.898 KiB)

An example server for testing:

using HTTP
using JSON3

function handle_request(req::HTTP.Request)
    try
        body = JSON3.read(String(req.body))
        response = Dict("content" => body.contents)
        return HTTP.Response(200, ["Content-Type" => "application/json"], 
                           JSON3.write(response))
    catch e
        return HTTP.Response(200, ["Content-Type" => "application/json"],
                           JSON3.write(Dict("error" => string(e))))
    end
end

HTTP.serve("0.0.0.0", 9000) do req
    handle_request(req)
end

What is going on ? Am I missing something? I see so many packages using this very same line, and literally everywhere these long compilations are happening!

<!--
Please specify the following versions when submitting a bug report:
 - Julia 1.10.6
 - HTTP.jl v1.10.8
 - MbedTLS.jl  not installed?
-->
@Sixzero
Copy link
Author

Sixzero commented Nov 18, 2024

I really think there needs to be a way to speed this up, because literally any POST request anyone make with HTTP.jl has a 6 seconds TTFX. 🤔

@Sixzero
Copy link
Author

Sixzero commented Nov 18, 2024

Just checked, literally calling curl (Cmd) to make the request has faster TTFX:

cmd = `curl -X POST http://localhost:9000 -H "Content-Type: application/json" -d '{"content":"Hello"}'`
@time run(pipeline(cmd, stdout=devnull, stderr=devnull))
@time run(pipeline(cmd, stdout=devnull, stderr=devnull))
  0.006593 seconds (44 allocations: 1.625 KiB)
  0.007562 seconds (42 allocations: 1.531 KiB)

🤔

@KristofferC
Copy link
Contributor

literally calling curl (Cmd) to make the request has faster TTFX:

That seems wholly unsurprising, or?

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

True. Both run and curl are fast. Actually not that surprising.

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

I wonder if we could add precompile step just to precompile this IMO quite common usecase of HTTP.jl.

Are there reasons we are not doing it already? Want to keep precompilation faster?

Are there too much edge cases? and it would blow up compilation too much?

I would prefer at least one way we could have low TTFX for a POST request with HTTP.jl.

@IanButterworth
Copy link
Member

I guess part of the challenge is that the precompilation should still be effective when done offline.

@JamesWrigley
Copy link
Contributor

One thing I've done in the past is run a package in SnoopCompile's @snoop_inference and add the most expensive calls explicitly with precompile(). It's not as exhaustive as a proper workload but it'll work anywhere.

@quinnj
Copy link
Member

quinnj commented Nov 19, 2024

Yeah this has been a notoriously tricky problem due to offline challenges. I think the approach @JamesWrigley mentioned is the right way to go, though it's still a bit of work and needs ongoing maintenance.

I'm actually a decent way through an entire rewrite of the package that uses the aws https://github.com/awslabs/aws-c-http library under the hood. I've been working through the challeneges of getting the C-interfacing right, as well as balancing what would end up being breaking vs. keeping API compatibility. Hopefully have something people can see/try soon.

But I mention because the rewrite is much more precompile friendly.

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

If you want @quinnj I can run some LLM calls just to make a list of functionality differences. :) Also maybe if you have in mind what should be done, we can actually run some agents to propose you some solutions, and then you only need to check if the changes are optimal.

In the current state of LLMs these agents won't invent new things, but can connect the dots pretty well.

@IanButterworth
Copy link
Member

For reference, with

julia> @trace_compile HTTP.request(:GET, "https://julialang.org")

Sorted by duration. Note a bunch are recompiles.

#= 1711.5 ms =# precompile(Tuple{HTTP.var"##request#32", Nothing, Array{UInt8, 1}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, typeof(HTTP.request), HTTP.MessageRequest.var"#makerequest#messagelayer##0"{HTTP.MessageRequest.var"#makerequest#1#messagelayer##1"{HTTP.RedirectRequest.var"#redirects#redirectlayer##0"{HTTP.RedirectRequest.var"#redirects#1#redirectlayer##1"{HTTP.HeadersRequest.var"#defaultheaders#headerslayer##0"{HTTP.HeadersRequest.var"#defaultheaders#1#headerslayer##1"{HTTP.CookieRequest.var"#managecookies#cookielayer##0"{HTTP.CookieRequest.var"#managecookies#1#cookielayer##1"{HTTP.RetryRequest.var"#manageretries#retrylayer##0"{HTTP.RetryRequest.var"#manageretries#1#retrylayer##1"{HTTP.ConnectionRequest.var"#connections#connectionlayer##0"{HTTP.ConnectionRequest.var"#connections#1#connectionlayer##1"{HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}}}}}}}}}}}}}, Symbol, String, Nothing, Array{UInt8, 1}, Nothing})
#=  451.5 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:readtimeout, :connect_timeout, :iofunction, :decompress, :verbose), Tuple{Int64, Int64, Nothing, Nothing, Int64}}, typeof(HTTP.Connections.newconnection), Type{OpenSSL.SSLStream}, Base.SubString{String}, Base.SubString{String}})
#=  403.9 ms =# precompile(Tuple{HTTP.StreamRequest.var"#6#7"{Nothing, HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}}, HTTP.Messages.Request, HTTP.Messages.Response, Float64, Base.ReentrantLock}})
#=  161.9 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:readtimeout, :logerrors, :logtag, :iofunction, :decompress, :verbose), Tuple{Int64, Bool, Nothing, Nothing, Nothing, Int64}}, HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}, HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}}})
#=  102.4 ms =# precompile(Tuple{HTTP.StreamRequest.var"#readbody##0#readbody##1"{HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}}, TranscodingStreams.TranscodingStream{CodecZlib.GzipDecompressor, SimpleBufferStream.BufferStream}}})
#=   49.3 ms =# precompile(Tuple{Base.Compiler.var"#inferiterate_2arg#abstract_iteration##1"{Base.Compiler.InferenceLattice{Base.Compiler.ConditionalsLattice{Base.Compiler.PartialsLattice{Base.Compiler.ConstsLattice}}}, Array{Any, 1}, Array{Base.Compiler.CallMeta, 1}, Type{Base.Generator{Base.UnitRange{Int64}, F} where F<:(Base.var"#191#192"{_A, NamedTuple{(), Tuple{}}} where _A)}, Base.Compiler.Future{Base.Compiler.AbstractIterationResult}}, Base.Compiler.NativeInterpreter, Base.Compiler.InferenceState})
#=   31.8 ms =# precompile(Tuple{Dates.var"##s55#36", Vararg{Any, 6}})
#=   30.1 ms =# precompile(Tuple{Type{HTTP.Connections.Connection{IO_t} where IO_t<:IO}, Base.SubString{String}, Base.SubString{String}, Int64, Bool, Bool, OpenSSL.SSLStream})
#=   28.8 ms =# precompile(Tuple{Dates.var"##s54#40", Vararg{Any, 8}})
#=   25.5 ms =# precompile(Tuple{ConcurrentUtilities.var"#try_with_timeout##2#try_with_timeout##3"{Any, Base.Timer, ConcurrentUtilities.TimedOut{Any}, Base.Channel{Any}, HTTP.Connections.var"#13#14"{OpenSSL.SSLStream, Bool, Bool, Base.Pairs{Symbol, Union{Nothing, Int64}, NTuple{4, Symbol}, NamedTuple{(:readtimeout, :iofunction, :decompress, :verbose), Tuple{Int64, Nothing, Nothing, Int64}}}, Base.SubString{String}, Base.SubString{String}}}})
#=   24.7 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:iofunction, :decompress, :verbose), Tuple{Nothing, Nothing, Int64}}, Base.var"#46#47"{Base.var"#48#49"{Base.ExponentialBackOff, HTTP.RetryRequest.var"#retrylayer##2#retrylayer##3"{Int64, typeof(HTTP.RetryRequest.FALSE), HTTP.Messages.Request, Base.RefValue{Int64}}, HTTP.ConnectionRequest.var"#connections#connectionlayer##0"{HTTP.ConnectionRequest.var"#connections#1#connectionlayer##1"{HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}}}}}, HTTP.Messages.Request}) # recompile
#=   24.3 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:iofunction, :decompress, :verbose), Tuple{Nothing, Nothing, Int64}}, typeof(HTTP.Connections.releaseconnection), HTTP.Connections.Connection{OpenSSL.SSLStream}, Bool})
#=   22.8 ms =# precompile(Tuple{HTTP.StreamRequest.var"#4#5"{HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}}, Float64, HTTP.Messages.Request, Base.ReentrantLock}})
#=   14.0 ms =# precompile(Tuple{typeof(Sockets.connect), Sockets.IPv4, UInt64})
#=   12.6 ms =# precompile(Tuple{Base.Compiler.var"#inferiterate#abstract_iteration##0"{Type{Base.Generator{Base.UnitRange{Int64}, F} where F<:(Base.var"#191#192"{_A, NamedTuple{(), Tuple{}}} where _A)}, Base.Compiler.Future{Base.Compiler.CallMeta}, Base.Compiler.Future{Base.Compiler.AbstractIterationResult}}, Base.Compiler.NativeInterpreter, Base.Compiler.InferenceState})
#=   11.9 ms =# precompile(Tuple{typeof(Base.append!), Array{UInt8, 1}, Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}})
#=   10.7 ms =# precompile(Tuple{typeof(Base.setindex_widen_up_to), Array{Int64, 1}, Dates.AMPM, Int64})
#=    9.6 ms =# precompile(Tuple{typeof(Base.foldl_impl), Base.BottomRF{Base.FlipArgs{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}}}, Function, Base.Iterators.Reverse{Array{Function, 1}}})
#=    9.3 ms =# precompile(Tuple{typeof(Base.foldl_impl), Base.BottomRF{Base.FlipArgs{HTTP.var"#stack##2#stack##3"{typeof(Base.identity)}}}, Function, Base.Iterators.Reverse{Array{Function, 1}}})
#=    7.9 ms =# precompile(Tuple{HTTP.Connections.var"#3#4"{HTTP.Connections.Connection{OpenSSL.SSLStream}}})
#=    7.5 ms =# precompile(Tuple{typeof(Base.collect_to_with_first!), Array{Int64, 1}, Int64, Base.Generator{Array{Type, 1}, Dates.var"#38#39"}, Int64})
#=    6.9 ms =# precompile(Tuple{typeof(Base.collect_to_with_first!), Array{Int64, 1}, Int64, Base.Generator{NTuple{8, DataType}, Dates.var"#42#43"}, Int64})
#=    6.2 ms =# precompile(Tuple{Base.var"#readcb_specialized#uv_readcb##0", Sockets.TCPSocket, Int64, UInt64})
#=    4.8 ms =# precompile(Tuple{typeof(Base.write), HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}}, Array{UInt8, 1}})
#=    4.7 ms =# precompile(Tuple{Type{Tuple}, Base.Generator{NTuple{8, DataType}, Dates.var"#42#43"}})
#=    4.6 ms =# precompile(Tuple{typeof(Base.string), Int64}) # recompile
#=    4.6 ms =# precompile(Tuple{Base.var"#625#626"{ConcurrentUtilities.var"#try_with_timeout##0#try_with_timeout##1"{Int64, Base.Channel{Any}}, Base.Timer}})
#=    4.2 ms =# precompile(Tuple{typeof(Base.isequal), Tuple{String, String, Bool, Bool, Bool}, Tuple{Base.SubString{String}, Base.SubString{String}, Bool, Bool, Bool}})
#=    4.2 ms =# precompile(Tuple{typeof(Base.getindex), Array{UInt8, 1}, Base.UnitRange{Int64}}) # recompile
#=    4.2 ms =# precompile(Tuple{typeof(Base.collect_to!), Array{Any, 1}, Base.Generator{NTuple{8, DataType}, Dates.var"#42#43"}, Int64, Int64})
#=    4.1 ms =# precompile(Tuple{typeof(Base.iterate), Base.Generator{Base.UnitRange{Int64}, Base.var"#189#190"{Type{NamedTuple{(), Tuple{}}}, Array{Int64, 1}}}})
#=    3.9 ms =# precompile(Tuple{typeof(Base.getproperty), URIs.URI, Symbol}) # recompile
#=    3.7 ms =# precompile(Tuple{typeof(Base.Iterators._zip_iterate_all), Tuple{Array{Symbol, 1}, Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Dates.AMPM}}, Tuple{Tuple{Int64}, Tuple{Int64}}})
#=    3.6 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:init,), Tuple{typeof(HTTP.StreamRequest.streamlayer)}}, typeof(Base.foldr), Function, Array{Function, 1}})
#=    3.6 ms =# precompile(Tuple{Type{UInt64}, Char}) # recompile
#=    3.5 ms =# precompile(Tuple{typeof(Dates.daysinmonth), Int64, Int64}) # recompile
#=    3.4 ms =# precompile(Tuple{typeof(Dates.totaldays), Int64, Int64, Int64}) # recompile
#=    3.4 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:n, :factor), Tuple{Int64, Float64}}, Type{Base.ExponentialBackOff}}) # recompile
#=    3.4 ms =# precompile(Tuple{typeof(Base.write), Base.GenericIOBuffer{Memory{UInt8}}, HTTP.Strings.HTTPVersion}) # recompile
#=    3.3 ms =# precompile(Tuple{typeof(Base.in), Base.SubString{String}, Tuple{String, String}}) # recompile
#=    3.2 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:init,), Tuple{typeof(HTTP.StreamRequest.streamlayer)}}, typeof(Base.foldr), Function, Tuple{}}) # recompile
#=    2.8 ms =# precompile(Tuple{typeof(Base.getproperty), Base.SplitIterator{String, Base.Fix{2, typeof(Base.isequal), Char}}, Symbol}) # recompile
#=    2.7 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:limit, :keepempty), Tuple{Int64, Bool}}, typeof(Base.eachsplit), String, Function})
#=    2.7 ms =# precompile(Tuple{typeof(Base.Iterators._zip_iterate_all), Tuple{Array{Symbol, 1}, Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Dates.AMPM}}, Tuple{Tuple{}, Tuple{}}})
#=    2.7 ms =# precompile(Tuple{typeof(Base._array_for), Type{Expr}, Base.Iterators.Zip{Tuple{Array{Symbol, 1}, Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Dates.AMPM}}}, Base.HasLength})
#=    2.6 ms =# precompile(Tuple{typeof(HTTP.IOExtras.nbytes), Array{UInt8, 1}})
#=    2.6 ms =# precompile(Tuple{Base.var"##eachsplit#420", Int64, Bool, typeof(Base.eachsplit), String, Function}) # recompile
#=    2.5 ms =# precompile(Tuple{Type{NamedTuple{(:finalize,), T} where T<:Tuple}, Tuple{Bool}}) # recompile
#=    2.4 ms =# precompile(Tuple{typeof(OpenSSL.free), OpenSSL.X509Certificate}) # recompile
#=    2.3 ms =# precompile(Tuple{typeof(Base.view), Array{UInt8, 1}, Base.UnitRange{Int64}}) # recompile
#=    2.3 ms =# precompile(Tuple{typeof(Base.unlock), Base.GenericCondition{Base.Threads.SpinLock}}) # recompile
#=    2.2 ms =# precompile(Tuple{Type{NamedTuple{(:port,), T} where T<:Tuple}, Tuple{Int64}}) # recompile
#=    2.2 ms =# precompile(Tuple{Type{NamedTuple{(:forcenew, :isvalid), T} where T<:Tuple}, Tuple{Bool, HTTP.Connections.var"#15#16"{Int64}}}) # recompile
#=    2.0 ms =# precompile(Tuple{typeof(HTTP.Conditions.precondition_error), String, Symbol}) # recompile
#=    2.0 ms =# precompile(Tuple{typeof(Core.memoryref), Memory{HTTP.Messages.Response}}) # recompile
#=    2.0 ms =# precompile(Tuple{typeof(Core.memoryref), Memory{HTTP.Connections.Connection{OpenSSL.SSLStream}}}) # recompile
#=    2.0 ms =# precompile(Tuple{typeof(Base.first), Core.SimpleVector})
#=    2.0 ms =# precompile(Tuple{Type{Pair{A, B} where B where A}, String, Nothing}) # recompile
#=    2.0 ms =# precompile(Tuple{Dates.var"##s55#34", Vararg{Any, 5}})
#=    1.9 ms =# precompile(Tuple{typeof(Core.memoryref), Memory{AbstractArray{UInt8, 1}}}) # recompile
#=    1.9 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:read, :write, :append, :truncate), Tuple{Bool, Bool, Nothing, Nothing}}, typeof(Base.open_flags)}) # recompile
#=    1.9 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:limit, :keepempty), Tuple{Int64, Bool}}, typeof(Base.eachsplit), String, Char}) # recompile
#=    1.9 ms =# precompile(Tuple{typeof(Base.setindex!), Array{Expr, 1}, Expr, Int64}) # recompile
#=    1.9 ms =# precompile(Tuple{typeof(Base.getproperty), Base.Set{UInt8}, Symbol}) # recompile
#=    1.9 ms =# precompile(Tuple{HTTP.var"#stack##2#stack##3"{typeof(Base.identity)}, typeof(HTTP.ExceptionRequest.exceptionlayer), Function})
#=    1.8 ms =# precompile(Tuple{typeof(Core.memoryref), Memory{HTTP.Cookies.Cookie}}) # recompile
#=    1.8 ms =# precompile(Tuple{typeof(Base.write), Base.GenericIOBuffer{Memory{UInt8}}, Base.SubString{String}}) # recompile
#=    1.8 ms =# precompile(Tuple{typeof(Base._array_for), Type{Symbol}, NTuple{8, DataType}, Base.HasLength})
#=    1.8 ms =# precompile(Tuple{typeof(Base.:(==)), Nothing, Symbol}) # recompile
#=    1.8 ms =# precompile(Tuple{Type{NamedTuple{(:transcode,), T} where T<:Tuple}, Tuple{Bool}}) # recompile
#=    1.8 ms =# precompile(Tuple{Type{HTTP.Streams.Stream{M, S} where S<:IO where M<:HTTP.Messages.Message}, HTTP.Messages.Response, HTTP.Connections.Connection{OpenSSL.SSLStream}})
#=    1.8 ms =# precompile(Tuple{Type{Base.Generator{I, F} where F where I}, Base.var"#189#190"{Type{NamedTuple{(), Tuple{}}}, Array{Int64, 1}}, Base.UnitRange{Int64}})
#=    1.8 ms =# precompile(Tuple{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}, typeof(HTTP.RetryRequest.retrylayer), Function})
#=    1.8 ms =# precompile(Tuple{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}, typeof(HTTP.RedirectRequest.redirectlayer), Function})
#=    1.8 ms =# precompile(Tuple{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}, typeof(HTTP.HeadersRequest.headerslayer), Function})
#=    1.8 ms =# precompile(Tuple{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}, typeof(HTTP.CookieRequest.cookielayer), Function})
#=    1.7 ms =# precompile(Tuple{typeof(Dates.character_codes), Type{Dates.DateFormat{:var"e, dd-uuu-yyyy HH:MM:SS G\\MT", Tuple{Dates.DatePart{Char(0x65000000)}, Dates.Delim{String, 2}, Dates.DatePart{Char(0x64000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x75000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x79000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x48000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x4d000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x53000000)}, Dates.Delim{String, 4}}}}})
#=    1.7 ms =# precompile(Tuple{typeof(Dates.adjusthour), Int64, Dates.AMPM}) # recompile
#=    1.7 ms =# precompile(Tuple{typeof(Base.indexed_iterate), Tuple{NamedTuple{(), Tuple{}}, NamedTuple{(), Tuple{}}}, Int64}) # recompile
#=    1.7 ms =# precompile(Tuple{Type{OpenSSL.OpenSSLError}, String}) # recompile
#=    1.7 ms =# precompile(Tuple{Type{NamedTuple{(:strict,), T} where T<:Tuple}, Tuple{Bool}}) # recompile
#=    1.7 ms =# precompile(Tuple{Type{NamedTuple{(:head, :tail), T} where T<:Tuple}, Tuple{Int64, Int64}}) # recompile
#=    1.7 ms =# precompile(Tuple{Type{NamedTuple{(:forcenew,), T} where T<:Tuple}, Tuple{Bool}}) # recompile
#=    1.7 ms =# precompile(Tuple{Type{Base.Iterators.Zip{Is} where Is<:Tuple}, Tuple{Array{Symbol, 1}, Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Dates.AMPM}}})
#=    1.7 ms =# precompile(Tuple{Type{Base.Iterators.Zip{Is} where Is<:Tuple}, Tuple{Array{Symbol, 1}, NTuple{7, Int64}}})
#=    1.7 ms =# precompile(Tuple{HTTP.var"#stack##2#stack##3"{typeof(Base.identity)}, typeof(HTTP.TimeoutRequest.timeoutlayer), Function})
#=    1.7 ms =# precompile(Tuple{Base.Iterators.var"#IteratorSize##0#IteratorSize##1"{Tuple{Array{Symbol, 1}, Tuple{Int64, Int64, Int64, Int64, Int64, Int64, Int64, Dates.AMPM}}}, Int64}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(HTTP.Messages.isredirect), HTTP.Messages.Response})
#=    1.6 ms =# precompile(Tuple{typeof(Core.memoryref), Memory{Array{String, 1}}}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(Base.something), Nothing, Int64, Vararg{Int64}})
#=    1.6 ms =# precompile(Tuple{typeof(Base.setindex!), Array{Symbol, 1}, Symbol, Int64}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(Base.isequal), Symbol, Symbol}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(Base.getproperty), Base.Order.Lt{HTTP.Cookies.var"#getcookies!##0#getcookies!##1"}, Symbol}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(Base._xfadjoint), Base.BottomRF{Base.FlipArgs{HTTP.var"#stack##8#stack##9"{typeof(Base.identity)}}}, Base.Generator{Array{Function, 1}, typeof(Base.identity)}}) # recompile
#=    1.6 ms =# precompile(Tuple{typeof(Base.:(+)), Vararg{Int64, 4}}) # recompile
#=    1.6 ms =# precompile(Tuple{Type{NamedTuple{(:read, :write, :append, :truncate), T} where T<:Tuple}, Tuple{Bool, Bool, Nothing, Nothing}}) # recompile
#=    1.6 ms =# precompile(Tuple{Type{NamedTuple{(:n, :factor), T} where T<:Tuple}, Tuple{Int64, Float64}}) # recompile
#=    1.6 ms =# precompile(Tuple{Type{Base.SplitIterator{S, F} where F where S<:AbstractString}, String, Base.Fix{2, typeof(Base.isequal), Char}, Int64, Bool}) # recompile
#=    1.6 ms =# precompile(Tuple{Type{Base.Generator{I, F} where F where I}, Dates.var"#42#43", NTuple{8, DataType}})
#=    1.6 ms =# precompile(Tuple{Base.var"##eachsplit#422", Int64, Bool, typeof(Base.eachsplit), String, Char}) # recompile
#=    1.6 ms =# precompile(Tuple{Base.Colon, UInt8, UInt8}) # recompile
#=    1.5 ms =# precompile(Tuple{typeof(Base.Iterators.zip), Array{Symbol, 1}, Vararg{Any}})
#=    1.5 ms =# precompile(Tuple{typeof(Base.isempty), Base.SubString{String}}) # recompile
#=    1.5 ms =# precompile(Tuple{typeof(Base.indexed_iterate), Tuple{NamedTuple{(), Tuple{}}, NamedTuple{(), Tuple{}}}, Int64, Int64}) # recompile
#=    1.5 ms =# precompile(Tuple{typeof(Base.getproperty), HTTP.Messages.Response, Symbol}) # recompile
#=    1.5 ms =# precompile(Tuple{typeof(Base._xfadjoint), Base.BottomRF{Base.FlipArgs{HTTP.var"#stack##2#stack##3"{typeof(Base.identity)}}}, Base.Generator{Array{Function, 1}, typeof(Base.identity)}}) # recompile
#=    1.5 ms =# precompile(Tuple{Type{Base.LinearIndices{N, R} where R<:Tuple{Vararg{Base.AbstractUnitRange{Int64}, N}} where N}, Array{Expr, 1}}) # recompile
#=    1.5 ms =# precompile(Tuple{Type{Base.Generator{I, F} where F where I}, typeof(Base.identity), Array{Function, 1}}) # recompile
#=    1.4 ms =# precompile(Tuple{typeof(Dates.UTM), Int64}) # recompile
#=    1.4 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:init,), Tuple{HTTP.ConnectionRequest.var"#connections#connectionlayer##0"{HTTP.ConnectionRequest.var"#connections#1#connectionlayer##1"{HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}}}}}, typeof(Base.foldr), Function, Array{Function, 1}})
#=    1.4 ms =# precompile(Tuple{typeof(Base.getproperty), Base.Set{String}, Symbol}) # recompile
#=    1.4 ms =# precompile(Tuple{typeof(Base.getproperty), Base.Order.Lt{Base.Sort.var"#_sort!##10#_sort!##11"{Base.Order.Lt{HTTP.Cookies.var"#getcookies!##0#getcookies!##1"}}}, Symbol}) # recompile
#=    1.4 ms =# precompile(Tuple{typeof(Base.contains), Base.Regex}) # recompile
#=    1.4 ms =# precompile(Tuple{typeof(Base.:(==)), Dates.AMPM, Dates.AMPM}) # recompile
#=    1.4 ms =# precompile(Tuple{Type{Base.LinearIndices{N, R} where R<:Tuple{Vararg{Base.AbstractUnitRange{Int64}, N}} where N}, Array{Symbol, 1}}) # recompile
#=    1.3 ms =# precompile(Tuple{typeof(Dates.character_codes), Type{Dates.DateFormat{:var"e, dd u yyyy HH:MM:SS G\\MT", Tuple{Dates.DatePart{Char(0x65000000)}, Dates.Delim{String, 2}, Dates.DatePart{Char(0x64000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x75000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x79000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x48000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x4d000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x53000000)}, Dates.Delim{String, 4}}}}})
#=    1.3 ms =# precompile(Tuple{typeof(Core.kwcall), NamedTuple{(:init,), Tuple{HTTP.ConnectionRequest.var"#connections#connectionlayer##0"{HTTP.ConnectionRequest.var"#connections#1#connectionlayer##1"{HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}}}}}, typeof(Base.foldr), Function, Tuple{}})
#=    1.3 ms =# precompile(Tuple{typeof(Base.notify), Base.GenericCondition{Base.Threads.SpinLock}}) # recompile
#=    1.3 ms =# precompile(Tuple{typeof(Base.lock), Base.GenericCondition{Base.Threads.SpinLock}}) # recompile
#=    1.3 ms =# precompile(Tuple{typeof(Base.argtail), String, String, Vararg{Any}})
#=    1.3 ms =# precompile(Tuple{typeof(Base.:(<=)), Int64, Int32}) # recompile
#=    1.3 ms =# precompile(Tuple{Type{UInt8}, UInt8}) # recompile
#=    1.3 ms =# precompile(Tuple{Type{Ptr{UInt8}}, Ptr{Nothing}}) # recompile
#=    1.3 ms =# precompile(Tuple{Type{Dates.DateTime}, Dates.UTInstant{Dates.Millisecond}}) # recompile
#=    1.3 ms =# precompile(Tuple{Type{Base.Pairs{Symbol, V, I, A} where A where I where V}, NamedTuple{(:init,), Tuple{HTTP.ConnectionRequest.var"#connections#connectionlayer##0"{HTTP.ConnectionRequest.var"#connections#1#connectionlayer##1"{HTTP.TimeoutRequest.var"#timeouts#timeoutlayer##0"{HTTP.TimeoutRequest.var"#timeouts#1#timeoutlayer##1"{HTTP.ExceptionRequest.var"#exceptions#exceptionlayer##0"{HTTP.ExceptionRequest.var"#exceptions#1#exceptionlayer##1"{typeof(HTTP.StreamRequest.streamlayer)}}}}}}}}, Tuple{Symbol}}) # recompile
#=    1.2 ms =# precompile(Tuple{typeof(Dates._directives), Type{Dates.DateFormat{:var"e, dd-uuu-yyyy HH:MM:SS G\\MT", Tuple{Dates.DatePart{Char(0x65000000)}, Dates.Delim{String, 2}, Dates.DatePart{Char(0x64000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x75000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x79000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x48000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x4d000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x53000000)}, Dates.Delim{String, 4}}}}}) # recompile
#=    1.2 ms =# precompile(Tuple{typeof(Dates._directives), Type{Dates.DateFormat{:var"e, dd u yyyy HH:MM:SS G\\MT", Tuple{Dates.DatePart{Char(0x65000000)}, Dates.Delim{String, 2}, Dates.DatePart{Char(0x64000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x75000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x79000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x48000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x4d000000)}, Dates.Delim{Char, 1}, Dates.DatePart{Char(0x53000000)}, Dates.Delim{String, 4}}}}}) # recompile
#=    1.2 ms =# precompile(Tuple{typeof(Base.first), Pair{String, String}}) # recompile
#=    1.2 ms =# precompile(Tuple{typeof(Base.first), Base.UnitRange{UInt8}}) # recompile
#=    1.1 ms =# precompile(Tuple{typeof(Base.something), Int64}) # recompile
#=    1.1 ms =# precompile(Tuple{typeof(Base.last), Base.UnitRange{UInt8}}) # recompile

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

where is @trace_compile defined, I couldn't really find it.

@IanButterworth
Copy link
Member

julia master

@KristofferC
Copy link
Contributor

There are some recompiles sure but the main stuff is just the big HTTP functions?

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

Just checked the suggested CurlHTTP, also switched to julia 1.11 (previously was on 1.10.6):

curl_post(url, body, method=CurlHTTP.POST) = begin
  curl = CurlEasy(; url, method)
  databuffer = UInt8[]
  response, status, error = curl_execute(curl, body, )  do d
    isa(d, Array{UInt8}) && append!(databuffer, d)
  end
  response_body = String(databuffer)
end
(method, url, serialized_body) = (:POST, "http://localhost:9000", "{\"content\":\"Hello\"}", )
@time response = curl_post(url, serialized_body)
@time response = curl_post(url, serialized_body)
@time response = curl_post(url, serialized_body)
@time response = curl_post(url, serialized_body)
  0.262022 seconds (294.45 k allocations: 15.009 MiB, 99.40% compilation time)
  0.000631 seconds (4.80 k allocations: 270.227 KiB)
  0.000456 seconds (475 allocations: 22.867 KiB)
  0.000409 seconds (80 allocations: 3.570 KiB)

@time HTTP.request(method, url; body=serialized_body)
@time HTTP.request(method, url; body=serialized_body)
  7.492825 seconds (13.31 M allocations: 664.436 MiB, 1.76% gc time, 113.52% compilation time)
  0.000679 seconds (209 allocations: 10.148 KiB)

Warm run timings are probably shouldn't be compared, I think even localhost might have some kind of latency. I am a little bit amazed, by how fast CurlHTTP.jl TTFX is. I am guessing HTTP.jl has a lot more features, although for a simple usecase it should be more lightweight and extras should be more of an optional thing?

I hope if I used CurlHTTP in its simplest form.

@KristofferC
Copy link
Contributor

KristofferC commented Nov 19, 2024

I am a little bit amazed, by how fast CurlHTTP.jl TTFX

Why? It just calls into a statically compiled library. Why would there be any significant latency in that?

@Sixzero
Copy link
Author

Sixzero commented Nov 19, 2024

Yeah, true. A lot less challenges need to be solved for precompilation. I wonder how much it loses with needing to call a lib. 🤔

@IanButterworth
Copy link
Member

I made a proposal at #1195

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 a pull request may close this issue.

5 participants