[swift-evolution] Static Dispatch Pitfalls

Dave Abrahams dabrahams at apple.com
Fri May 20 12:51:11 CDT 2016

on Fri May 20 2016, Fabian Ehrentraud <swift-evolution at swift.org> wrote:

> Hi,
> there's been a little discussion about static vs. dynamic dispatch on
> this mailing list, and there is a good post about the pitfalls when
> using attributes defined in extensions [1].
> Having run into this myself during development, is there a plan on how
> to reduce the pitfalls in future versions of Swift?
> - Fabian
> [1] https://developer.ibm.com/swift/2016/01/27/seven-swift-snares-how-to-avoid-them/
> Sorry, I understand and appreciate your pragmatism. Right now it feels
> very much like a fight to the ideological death between POP and OOP
> and it may get really bad results this way.

Being in the run-up to WWDC, I don't really have the time to participate
in this discussion right now, although I think it is important and
interesting.  However, this last sentence caught my eye and I thought I
should clarify something: protocols unify static and dynamic dispatch,
and one is not inherently better than the other.  They have different
strengths.  Protocol-oriented programming is about leveraging those
strengths appropriately, not about a mandate to choose one or the other
form of dispatch.

> Sent from my iPhone
> On 4 Mar 2016, at 08:58, Brent Royal-Gordon <brent at
> architechies.com<https://lists.swift.org/mailman/listinfo/swift-evolution>>
> wrote:
>>> Brent, why is dynamic dispatching for protocol extension default
>>> implementations wrong in your mind? Wouldn't you agree that when
>>> static dispatching introduces such a side effect that it should not
>>> be automatically applied and perhaps a keyword should be added if
>>> you really wanted static dispatching nonetheless?
>>> I think that code execution should not be affected by type casting,
>>> it feels like a very confusing part of the language.
>> I don't think dynamic dispatch is wrong; I think it's a large and
>> technically challenging change. So in the spirit of incrementalism,
>> I was trying to make cautious proposals which kept existing
>> semantics intact but made them clearer, in preparation for perhaps
>> eventually introducing dynamic dispatch. (Basically, I suggested
>> that non-overridable protocol extension members should be marked
>> `final` and it should be illegal to shadow them.)
>> But the feedback I got indicated that most people wanted a more
>> aggressive proposal which introduced dynamic dispatch
>> immediately. That's much harder to propose because it touches on all
>> sorts of runtime implementation details I know nothing about, so I
>> didn't try to draft a proposal.
>> (You are, perhaps inadvertently, currently demonstrating exactly what happened in those previous threads!)
>> --
>> Brent Royal-Gordon
>> Architechies
>> On Dec 11, 2015, at 8:56 PM, Kevin Ballard via swift-evolution
>> <swift-evolution at
>> swift.org<https://lists.swift.org/mailman/listinfo/swift-evolution>>
>> wrote:
>> You think that Swift prefers virtual dispatch. I think it prefers static.
>> I think what's really going on here is that _in most cases_ there's
>> no observable difference between static dispatch and virtual
>> dispatch. If you think of Swift as an OOP language with a powerful
>> value-typed system added on, then you'll probably think Swift
>> prefers virtual dispatch. If you think of Swift as a value-typed
>> language with an OOP layer added, then you'll probably think Swift
>> prefers static dispatch. In reality, Swift is a hybrid language and
>> it uses different types of dispatch in different situations as
>> appropriate.
> (emphasis mine)
> I know that this is a bit philosophical, but let me suggest a “next
> level down” way to look at this.  Static and dynamic are *both* great
> after all, and if you’re looking to type-cast languages, you need to
> consider them both in light of their semantics, but also factor in
> their compilation strategy and the programmer model that they all
> provide.  Let me give you some examples, but keep in mind that this is
> a narrow view and just MHO:
> 1. C: Static compilation model, static semantics.  While it does
> provide indirect function pointers, C does everything possible to
> punish their use (ever see the non-typedef'd prototype for
> signal(3/7)?), and is almost always statically compiled.  It provides
> a very “static centric” programming model.  This is great in terms of
> predictability - it makes it trivial to “predict” what your code will
> look like at a machine level.
> 2. Javascript: Completely dynamic compilation model, completely
> dynamic semantics.  No one talks about statically compiling
> javascript, because the result of doing so would be a really really
> slow executable.  Javascript performance hinges on dynamic profile
> information to be able to efficiently execute a program.  This
> provides a very “dynamic centric” programming model, with no ability
> to understand how your code executes at a machine level.
> 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.  OTOH, like C, C++
> provides a very predictable model: C++ programmers assume that C
> constructs are static, but virtual methods will be dynamically
> dispatched.  This is correct because (except for narrow cases) the
> compiler has to use dynamic dispatch for C++ virtual methods.  The
> good news here is that its dynamism is completely opt in, so C++
> preserves all of the predictability, performance, and
> static-compilability of C while providing a higher level programming
> model.  If virtual methods are ever actually a performance problem, a
> C++ programmer has ways to deal with that, directly in their code.
> 4. Java: Java makes nearly "everything" an object (no structs or other
> non-primitive value types), and all methods default to being “virtual”
> (in the C++ sense).  Java also introduces interfaces, which offer an
> added dimension on dynamic dispatch.  To cope with this, Java assumes
> a JIT compilation model, which can use dynamic behavior to
> de-virtualize the (almost always) monomorphic calls into checked
> direct calls.  This works out really well in practice, because JIT
> compilers are great at telling when a program with apparently very
> dynamic semantics actually have static semantics in practice (e.g. a
> dynamic call has a single receiver).  OTOH, since the compilation
> model assumes a JIT, this means that purely “AOT” static compilers
> (which have no profile information, no knowledge of class loaders,
> etc) necessarily produce inferior code.  It also means that Java
> doesn’t “scale down” well to small embedded systems that can’t support
> a JIT, like a bootloader.
> 5) Objective-C: Objective-C provides a hybrid model which favors
> predictability due to its static compilation model (similar in some
> ways to C++).  The C-like constructs provide C-like performance, and
> the “messaging” constructs are never “devirtualized”, so they provide
> very predictable performance characteristics.  Because it is
> predictable, if the cost of a message send ever becomes an issue in
> practice, the programmer has many patterns to deal with it (including
> "imp caching", and also including the ability to define the problem
> away by rewriting code in terms of C constructs).  The end result of
> this is that programmers write code which use C-level features where
> performance matters and dynamicism doesn’t, but use ObjC features
> where dynamicism is important or where performance doesn’t matter.
> While it would be possible to implement a JIT compiler for ObjC, I’d
> expect the wins to be low, because the “hot” code which may be hinging
> on these dynamic features is likely to already be optimized by hand.
> 6) GoLang: From this narrow discussion and perspective, Go has a
> hybrid model that has similar characteristics to Objective-C 2013
> (which introduced modules, but didn’t yet have generics).  It assumes
> static compilation and provides a very predictable hybrid programming
> model.  Its func’s are statically dispatched, but its interfaces are
> dynamically dispatched.  It doesn’t provide guaranteed dynamic
> dispatch (or “classes") like ObjC, but it provides even more dynamic
> feautres in other areas (e.g. it requires a cycle-collecting garbage
> collector).  Its "interface{}” type is pretty equivalent to “id”
> (e.g. all uses of it are dynamically dispatched or must be
> downcasted), and it encourages use of it in the same places that
> Objective-C does.  Go introduces checked downcasts, which introduce
> some run-time overhead, but also provide safety compared to
> Objective-C. Go thankfully introduces a replacement for the imperative
> constructs in C, which defines away a bunch of C problems that
> Objective-C inherited, and it certainly is prettier!
> … I can go on about other languages, but I have probably already gotten myself into enough trouble. :-)
> With this as context, lets talk about Swift:
> 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.  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) while also providing an expressive and
> clean high level programming model - simplifying learning and the
> common case where programmers don’t care to count cycles.  If
> anything, I’d say that Swift is an “opportunistic” language, in that
> it provides a very dynamic “default" programming model, where you
> don’t have to think about the fact that a static compiler is able to
> transparently provide great performance - without needing the overhead
> of a JIT.
> 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.
> TL;DR: What I’m really getting at is that the old static vs dynamic
> trope is at the very least only half of the story.  You really need to
> include the compilation model and thus the resultant programmer model
> into the story, and the programmer model is what really matters, IMHO.
> -Chris
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list