[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