[swift-evolution] Proposal: Universal dynamic dispatch for method calls
Slava Pestov
spestov at apple.com
Sat Dec 12 17:36:45 CST 2015
> On Dec 11, 2015, at 11:45 PM, Chris Lattner via swift-evolution <swift-evolution at swift.org> wrote:
>
> 3. C++: C++ is a step up from C in terms of introducing dynamism into the model with virtual functions. Sadly, C++ also provides a hostile model for static optimizability - the existence of placement new prevents a lot of interesting devirtualization opportunities, and generally makes the compiler’s life difficult.
Interesting, I haven’t heard that placement new is problematic before. What are the problems involved (feel free to reply off-list). Is it legal to use placement new to replace an existing instance and change its vtable pointer or do aliasing rules prohibit that?
> Swift is another case of a hybrid model: its semantics provide predictability between obviously static (structs, enums, and global funcs) and obviously dynamic (classes, protocols, and closures) constructs.
Yeah. It’s also pretty cool how a consequence of the runtime generics model is that all value types can be manipulated reflectively, without penalizing static code. I think the key concept here is separating in-memory data from metadata needed for runtime manipulation (value witness tables, protocol conformances etc). I hope more languages design around this in the future.
> A focus of Swift (like Java and Javascript) is to provide an apparently simple programming model. However, Swift also intentionally "cheats" in its global design by mixing in a few tricks to make the dynamic parts of the language optimizable by a static compiler in many common cases, without requiring profiling or other dynamic information.. For example, the Swift compiler can tell if methods in non-public classes are never overridden (and non-public is the default, for a lot of good reasons) - thus treating them as final. This allows eliminating much of the overhead of dynamic dispatch without requiring a JIT. Consider an “app”: because it never needs to have non-public classes, this is incredibly powerful - the design of the swift package manager extends this even further (in principle, not done yet) to external libraries. Further, Swift’s generics provide an a static performance model similar to C++ templates in release builds (though I agree we need to do more to really follow through on this) -- while Swift existentials (values of protocol type) provide a balance by giving a highly dynamic model.
>
> The upshot of this is that Swift isn’t squarely in either of the static or dynamic camps: it aims to provide a very predictable performance model (someone writing a bootloader or firmware can stick to using Swift structs and have a simple guarantee of no dynamic overhead or runtime dependence)
To be fair, we still emit implicit heap allocations for value types whose size isn’t known. So your boot loader will have to avoid generics (and non- at noescape closures), at least :-)
Actually, for code within a single module, are we always able to fully instantiate all generics?
> Finally, while it is possible that a JIT compiler might be interesting someday in the Swift space, if we do things right, it will never be “worth it” because programmers will have enough ability to reason about performance at their fingertips. This means that there should be no Java or Javascript-magnitude "performance delta" sitting on the table waiting for a JIT to scoop up. We’ll see how it works out long term, but I think we’re doing pretty well so far.
JITs can teach us a lot about optimizing for compile time though, which would help REPL usage, tooling and scripting.
Slava
More information about the swift-evolution
mailing list