[swift-server-dev] Existential - Thoughts on the current HTTP API

Johannes Weiß johannesweiss at apple.com
Fri Oct 13 10:13:20 CDT 2017



> On 13 Oct 2017, at 1:07 pm, Helge Heß via swift-server-dev <swift-server-dev at swift.org> wrote:
> 
> It is a little off-topic, but since you brought it up in here ;-) Thinking about the existential:
> 
> On 13. Oct 2017, at 12:39, Helge Heß via swift-server-dev <swift-server-dev at swift.org> wrote:
>>> `UnsafeHTTPResponseBody` is an existential (a protocol-typed variable). That means with this type signature, you'll do an allocation for every call, that's pretty bad.
> 
> Is that intended or a compiler bug? My assumption for passing something as a protocol would be that
> 
> writeBody(_ data: UnsafeHTTPResponseBody)
> writeBody(myImp)
> 
> Essentially becomes something like
> 
> writeBody(_ dataOwner: Any, _ data: vtable<UnsafeHTTPResponseBody>)
> writeBody(myImp, myImp.vtableForProtocol<UnsafeHTTPResponseBody>)
> 
> You are saying that it is more like this?:
> 
> struct existential {
>   let dataOwner : Any
>   let data      : vtable<UnsafeHTTPResponseBody>
> }
> let e = malloc(existential)
> writeBody(e)
> free(e)
> 
> That sounds a little crazy, doesn’t it? :-)

IIRC in Swift 3, that's exactly how it worked. In Swift 4, many of those existential boxes can indeed be put on the stack which is a massive improvement.
That is assuming your actual type is 3 words or less in size. If your object is larger than that, then you'll still get a heap allocation for the actual data.

Also, going through existentials makes many performance improvements impossible. So with the <Bytes: UnsafeHTTPResponseBody> we can safely assume that the specialisation for `[UInt8]` will literally just take Data's underlying pointer out as everything will be inlined. That won't be true for existentials.

I always find it quite hard to estimate how much the difference is in real life.


>>> A better option would be
>>> `func writeBody<Body: UnsafeHTTPResponseBody>(_ data: Body, …)`
> 
> But this is only better if the concrete `Body` type lives in the same module like the `writeBody` function, right? (so that WMO optimisation can kick in and inline the instantiation of the generic type?)

yes, unless you put an private API @_specialize(...) in there. However I'd assume that there's a few well known types that will always be used (`[UInt8]`, `Data`, ...) so you'd assume it's specialised for those anyway.


> In the HTTP module case `Body` would be in a different module and in my understanding would always use some dynamic ‘generics’ dispatcher? Which is slow? Is that really faster than a protocol type?

I _think_ that if in that module you use it for say `[UInt8]`, then external callers can also benefit from that specialisation.

-- Johannes

> 
> hh
> 
> _______________________________________________
> swift-server-dev mailing list
> swift-server-dev at swift.org
> https://lists.swift.org/mailman/listinfo/swift-server-dev



More information about the swift-server-dev mailing list