9 ms·
Chris Lattner on Swift and dynamic dispatch
- pbreit 11y agoI'm definitely a novice programmer but that entire post is a foreign language to me. On what level do these concepts impact me? Performance? Managing large code base? Easy, hard, impossible to do something?
- mikko-apo 11y agoIt's about the performance of selecting which function to call: https://en.wikipedia.org/wiki/Dynamic_dispatch https://en.wikipedia.org/wiki/Dynamic_dispatch
- nemothekid 11y agoChris Lattner is a compiler author (LLVM/Clang, Swift), so this post is most definitely talking about compiler semantics that is pretty low level stuff that isn't really visible to the end-user (the programmer of the language), except for in C++. When a compiler is compiling a program, and you make a function call (lets say `int x = obj.compute()`), the compiler has to know where the code for that function is. In C (and C++ to an extent), this is easy, functions aren't very fancy and compiler can just go to that place in memory which doesn't change during runtime. These types of functions are called "static functions" and the compilers method of calling these functions is called "single dispatch". Since the code that runs is very predictable, its easy for the compiler to optimize the function call. In other languages (like Ruby, Java, Swift), a statement like `int x = obj.compute()` has multiple meanings if `obj` can be subclassed. For example, if you have an inheritance structure like (Dog, Cat) > Mammal > Animal, `obj.compute` could mean the compute function on Animal, Mammal, Dog or Cat. At compile time, the compiler may have no idea of knowing which definition of `compute()` to call. So during runtime, it will analyze the type information and call the correct function. These are called "virtual functions" and they are called with a "dynamic dispatch". Because there are multiple functions that could run, actually predicting what will run on the machine is hard. Dynamic Dispatch function calls are generally slower than static ones, and a compiler would prefer static calls. Chris goes over the many different methods languages attempt to make dynamic calls faster - and Swift, thanks to its simple programming model, (AFAIK) gets "smart" about function calls and can make single dispatch calls when needed (Java can do this to, but what makes Swift "cool" is that it doesn't need a Just In-time Compiler (JIT) to do so). What all this means to you (the end user) is that you can use all these fancy functions in Swift without having to worry about performance. The overhead of a function call may be something you have never considered (I certainly don't think about it), but I'm sure its something compiler geeks obsess over. I may be 100% wrong here, I'm not a compiler author, can only make a guess from C/C++ experience, and I've never used Swift before.
- dawkins 11y agogreat explanation, thank you
- krat0sprakhar 11y agoThank you for the explanation!
- coldtea 11y agoNovice or not, the post shouldn't be foreign language to you. I mean, if you just start out it's ok if it is, but eventually you should learn about that stuff. Now, if you only program with languages like Javascript or Python, then you can maybe ignore it, since those have just one method to do those things. Otherwise, there's nothing that low level in 80% of the post. It's not like low level intricacies of IEEE floating point, or heavy internal compiler workings. Stuff like static or dynamic dispatch are things that all programmers can benefit of knowing, especially how it's handled in the language that they use.
- stephen_g 11y agoIt's talking about the trade-off between dynamic dispatch and more static constructs. Basically it has to do with when and how overridden functions, class methods, struct members etc. are chosen. Generally, static dispatch is a lot faster, but dynamic dispatch makes polymorphism a lot easier, which is an important feature of object oriented programming. Really, the details of the different language designs are mostly only important if what you are doing is highly performance critical or if you are limited by your runtime (I.e writing a bootloader or bare-metal OS code/firmware).
- tel 11y agoWhen you call a function f(x) it may, broadly, be the case that either the compiler knows exactly which function f is at compile time or f may be determined using information such as which x is being passed in—something only known during runtime execution. Requiring that the compiler be able to determine what f is at compile time is "static dispatch" and it enables powerful compilers to work hard to vastly improve program efficiency Ahead-of-Time. Without that information, a program either has to look up which f to call every time during execution (dynamic dispatch overhead) or using a Just-in-Time compiler to optimize out this lookup when, at runtime, it has the information required to know that f is always going to be looked up the same way. Notice that this is still runtime overhead as you have to bundle a compiler alongside every program, but it's runtime overhead only once. Java is famous for this JIT behavior: expensive runtime calls have to be called enough times to pass the "burn in" period where they get optimized away and then run quickly. Languages can choose from various points on this spectrum. Lattner, the designer of Swift, is reviewing the choice he made in Swift which pairs some static features against some dynamic features. It's a fine balance to make and he claims that Swift's choice is particularly nice since it provides user-predictable performance without (a) completely eliminating all dynamic dispatch and (b) without requiring a Just-in-Time compiler.
- twoodfin 11y agoHow does the presence of C++'s placement new() affect devirtualization opportunities? I'm sure Chris knows what he's talking about, so I'd like to hear more.
- chadaustin 11y agoI was going to write a response but bdash over on reddit did a great job, so I'll just link that: https://www.reddit.com/r/programming/comments/3wla9f/chris_lattner_author_of_the_swift_programming/cxx7k0m https://www.reddit.com/r/programming/comments/3wla9f/chris_l... One interesting note is that, the last time I looked into this, the compiler _is_ allowed to assume that multiple sequential vtable calls do not replace the vtable, allowing the vtable lookup to be cached. But that is not true for some arbitrary external-to-the-compilation-unit function that.
- bdash 11y agoIn practice neither GCC nor Clang seem to be willing to believe that the vtable is immutable. See https://goo.gl/y0cx1r https://goo.gl/y0cx1r, for example, which shows only the first of two calls being devirtualized. The call to fprintf within B::work is sufficient to cause to both compilers to reload the vtable pointer, preventing devirtualization of the second call. Interestingly enough, when a use of placement new is visible to the compiler it can prevent the conservative behavior mentioned above. In https://goo.gl/Y8rwYG https://goo.gl/Y8rwYG the vtable store that placement new conceptually generates allows Clang to devirtualize the second virtual call. GCC doesn't appear to catch this case.
- chadaustin 11y agoThanks for digging in! I saw the same thing with clang last year, in the context of Emscripten, where we were hoping to eliminate the redundant vtable loads as a JS code size optimization.
- tomcam 11y agoGood writeup. What is a checked downcast?
- twoodfin 11y agoIn this context, it's a runtime check inserted by the compiler to be sure that an object retrieved via a generic interface matches the more specific type the caller expects.
- masklinn 11y agoA call from an abstract interface to a concrete type which signals if the concrete type is wrong. In C++, `dynamic_cast` is checked (it returns a null if the cast is incorrect) while the others are unchecked (at runtime, they do have some static checking/limitations, except for C-style casts where there's no check whatsoever)
- golergka 11y agoCan someone elaborate on what methods JIT uses to boost perfomance as compared with AOT compilation? Do modern JIT compilers use statistics about what code actually got called to optimize call dispatch? Where can I find more info about this topic?
- wolf550e 11y agoRead about polymorphic inline caches which Java and Javascript JITs do.
- xxs 11y agoJITs do know the call target. This is a quote from "A JVM does that?"[0]: C++ avoids virtual calls – because they are slow ● Java embraces them – and makes them fast ● Well, mostly fast – JIT's do Class Hierarchy Analysis ● CHA turns most virtual calls into static calls ● JVM detects new classes loaded, adjusts CHA – May need to re-JIT ● When CHA fails to make the call static, inline caches ● When IC's fail, virtual calls are back to being slow ---- There are several misconception about Java in the write-up. The real reason there won't be a bootloader is the need for the Java runtime and the need to GC. Unoptimized virtual calls won't affect any bootloader for that matter (and you can/should use static and/or private methods for the inner loops) [0]:https://www.youtube.com/watch?v=uL2D3qzHtqY https://www.youtube.com/watch?v=uL2D3qzHtqY
- pvg 11y agoHe doesn't say you won't see a Java bootloader because of unoptimized virtual calls, he says it's because the runtime is too big. CL: It also means that Java doesn’t “scale down” well to small embedded systems that can’t support a JIT, like a bootloader.
- xxs 11y agoOTOH, 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. This the quote. He talks about the JIT only, no runtime mentioned.
- ghshephard 11y agoThe part of this (really excellent) writeup that caught my attention was this: someone writing a bootloader or firmware can stick to using Swift structs and have a simple guarantee of no dynamic overhead or runtime dependence There seems to be a pretty strong implication here that Swift could be used to write firmware/bootloaders, and other low level constructs - including operating systems. Has anyone worked with Swift yet on that type of project?
- sandij 11y ago‘It’s designed to scale from “hello, world” to an entire operating system.’ https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ https://developer.apple.com/library/ios/documentation/Swift/...
- pjmlp 11y ago"Swift is a successor to both the C and Objective-C languages." https://developer.apple.com/swift/ https://developer.apple.com/swift/ It is only a matter of trying and seeing what might still be missing in terms of toolchain.
- mpweiher 11y agoExcept for the minor part that it's not true. The performance variations in Swift are (as of now) much larger and less predictable than the overhead of dynamic messaging in Objective-C, and the latter can always be removed by using IMP-caching or converting to a C function.
- Someone 11y agoThe claim is not about Swift in general, but of a limited subset of the language: "someone writing a boot loader or firmware can stick to using Swift structs and have a simple guarantee of no dynamic overhead or runtime dependence" Based on what I know about Swift (reading Apple's manual, a tiny bit of experimenting, and decent knowledge about how compilers work), I would expect Swift to compile purely statically, like C, for this case. Performance may still be lower because the compiler is newer, but I wouldn't expect performance variations. And of course, the standard library may not be optimal for this use case. The implementation of strings, for example, may be too dynamic for embedded work.
- nielsbot 11y agoI feel like Swift reduces the runtime availability of dynamic dispatch by default... so for things like live programming or runtime hacking Swift is worse than Obj-C. I envision a future similar to the "Smalltalk dream" where one can build apps live, with minimal recompilation and where dynamic dispatch should be the default. I think this scenario is less ideal for "systems programming" but for areas when one is snapping together UI components and data sources I think it's ideal. Perhaps someone here can come up with good counter examples why this isn't the case? Finally, I think static dispatch, as is favored by Swift, tends to paint one into a corner down the road... but it's possible Swift has enough leeway so that this isn't the case?
- mnem 11y agoSnapping together UI components and data sources has existed for a long time, but it always seems to fail to be popular outside of niche uses. That's often due to the overhead of runtimes and the unsuitability of generic UI components when you get into the nitty gritty of the application being designed. Of course, that doesn't mean it can't be done, just an observation that the attempts I've seen over the past 20 years or so haven't been very good, ultimately. I've often been curious why programming by manipulating metaphors on screen is seen as an ultimate goal in the evolution of programming languages. It seems a bit like creating a book by sticking pre-generated paragraphs together. On your final point - static dispatch doesn't prevent you from making your own dynamic dispatch mechanism in the language.
- andybak 11y ago> It seems a bit like creating a book by sticking pre-generated paragraphs together. A poor metaphor. Maybe more like "creating a book by joining high-level expressive words together instead of having to define every concept from scratch for every book"
- Razengan 11y ago> It seems a bit like creating a book by sticking pre-generated paragraphs together. But books are not tools or machines, whereas software applications are (not counting art & entertainment, generally.) Nobody creates every screw and electrical component from scratch when designing and assembling other tools and machines. "Programming by manipulating metaphors on screen" would be more akin to using predefined mathematical symbols and formulas, and just putting in the numbers and variables related to the problem you want to solve or the task you want to perform. Even most popular genres of games could be made entirely from wiring up predesigned components together in a visual environment, without writing any code at all. You would just supply your own graphics and sound and other content.
- T3RMINATED 11y agohe needs to shut up
- Symmetry 11y agoDoes this really buy you that much? For most programs the runtime is going to be dominated by loop execution. If you're in a situation where the all the objects have the same type and the compiler could switch in static dispatch the computer's branch predictor should catch on very quickly and the performance gains won't be that high. In cases where you have heterogeneous objects the branch predictor won't be able to catch on but the compiler will be forced to stick with static dispatch anyways. I suppose there is a bigger gain where you can inline static functions or optimize the calling convention.
- Rusky 11y agoInlining is often called the mother of all optimizations. On its own, it doesn't gain you much compared to any other optimization, but once it's done it enables much, much more. There's a reason languages like C that default to static dispatch on value types are so much faster without much effort.
- mbrubeck 11y agoPeople interested in Swift at this level should also read Lattner’s comment on /r/rust about the possibility of extending Swift to include Rust’s statically checked ownership and borrowing concepts: https://www.reddit.com/r/rust/comments/3vadg8/swift_is_open_source/cxnu2kk https://www.reddit.com/r/rust/comments/3vadg8/swift_is_open_...
- bluejekyll 11y agoIf you read that though, it is not something that's a high priority for the team. Basically, 90% of devs won't need or want some of these features. Unless Apple decides to have a focused effort around recoding the core of OS X into Swift which would require these changes, it's hard to see when the language would gain these options. Don't get me wrong, Swift is an awesome language, but I see Rust as maintaining a position as a systems language equivalent to C. Where Swift will possibly start supplanting Java/Go/C++/Python use cases.
- pjmlp 11y agoAs someone that would like to see C replaced by something better and has always been a fan for Wirth influenced languages, Swift has actually a better position to achieve that than Rust. The reason being that Apple, like it happened to OS vendors that adopted C and later C++, can allow themselves to push the language into the developers regardless of what they think they should use. All the systems languages that have survived in the mainstream, have done so by being adopted by an OS vendor.
- lobster_johnson 11y agoOut of interest, in what way do you consider Swift a Wirth-influenced language? Go seems a closer cousin to Wirth's languages. Its strict type coercion, value copying semantics, package system, lack of OO, use of ":=", type declarations with "type", "var", GC, colons, etc. are all reminiscent of Wirth's languages, especially Oberon. (Robert Griesemer worked with Oberon under Wirth at ETH Zürick, so there's a clear relationship.) Another highly wirthian language is Nim, though its influence comes via Delphi/ObjectPascal.