> On Jun 28, 2016, at 4:01 PM, Michael Peternell <michael.petern...@gmx.at> 
> wrote:
>> Am 29.06.2016 um 00:32 schrieb John McCall <rjmcc...@apple.com>:
>> 
>> The decision to make class methods polymorphic by default was always 
>> premised on being able to undo that in obvious cases where methods are never 
>> overridden.  Making a class public so that clients can use it shouldn't 
>> cause significant performance degradations just because you forgot to also 
>> write "final".
>> 
>> John.
> 
> I do care about performance. For this reason I don't want a fully dynamic 
> language. I disagree about the "significant performance degradations just 
> because you forgot to also write `final`". I mentioned "performance" in my 
> original post only because it would be the only convincing argument - if 
> there were indeed superior performance when using `final`.
> 
> Of course, dynamic dispatch is much slower than static dispatch. But 
> optimized code does not spend much time dispatching. If a method takes 3 
> seconds to complete, and from that 2 seconds are used for dynamically 
> dispatching method calls, then I would say that it has not been optimized for 
> performance yet. How would such a function look like? The function being 
> dynamically dispatched should probably not be statically dispatched but 
> inlined completely. And for the rare case that the dispatch type really makes 
> all the difference, it's always possible to `final`ize (or `private`ize) some 
> of the used methods.

Getters and setters are major examples of methods that (1) are very likely to 
be made public on a public class, (2) are very, very profitable to devirtualize 
and inline within the class's implementation, and (3) most programmers will 
never think to mark final (and are very annoying to "inline").  Now, if the 
program contains some cubic algorithm, then of course this level of micro 
performance doesn't matter much; it's at best a constant factor on the Nested 
Loop Of Doom.  However, as a language implementor, it is not fair for me to 
assume that the program contains brazen inefficiencies that will swamp the 
impact of any decision I make; it is often the case that, after crossing off a 
few prominent hot-spots, programmers find themselves staring at profiles that 
are disconcertingly flat.  And a lot of programmers feel quite rightly that 
they shouldn't have to massively uglify their source code with annotations just 
to get a little bit of performance, especially if it feels like something the 
language ought to just know.

I also have some responsibility to the system to try to run code as efficiently 
as possible, even if the programmer (be it through laziness, ignorance, or — 
more likely — just being over-worked) didn't put much conscious effort into 
optimizing it.

Anyway, I think you're underselling the advantages of devirtualization.

First, modern systems do a *lot* of indirect calls.  On a typical iOS device, 
the indirect branch predictor is not very effective, not because its prediction 
algorithm is in any way inadequate but because its cache is just not large 
enough to deal with all the indirect branches being executed by (mostly) 
objc_msgSend.  (I am not a hardware expert, but as I understand it, it is not 
easy to just make these caches bigger.)  Just making a bunch of calls direct 
instead means those calls (1) won't miss in the cache and (2) won't contribute 
to thrashing the cache for other calls.

Second, devirtualization is a major enabling optimization.  Many methods are 
quite small, especially the ones that you probably don't think of as methods, 
like the implicit accessors for stored properties.  Being able to inline them, 
or even just reason about whether they change memory, can have a big impact.

Finally, the specific form of devirtualization in question here, where a method 
is proven to never be overridden (again, very common for accessors!), can have 
a significant impact separate from any calls because the method essentially no 
longer needs to be virtual.  That is, it can be removed from the virtual method 
tables completely, and we may be able to completely avoid emitting it.  That 
shrinks the size of global memory (and the binary), decrease the amount of work 
that has to be done at load-time, and improves locality within the virtual 
table.

I also think this is the right thing to do for library evolution, but yes, it 
is a very valuable optimization that we would otherwise struggle to do for 
public classes.

John.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to