`Final` Optimization Recommendations

Hi there,

I’m just looking into recommendations for our team around optimizations.

In this optimization guide it mentions the recommendation to add the final keyword to reduce dynamic dispatch. Through other talks and threads, it has also been mentioned that final is inferred during Whole Module Optimization builds for non-open classes as there is no possibility of overriding these classes after the fact.

Considering that WMO builds are the default for Xcode release builds now, should this guidance be updated to reflect that this is unnecessary in these circumstances?

I’ve noticed many workplaces I’ve written Swift code require prepending all classes with final as a matter of policy for the optimization potential. Doesn’t this setting, however, make such requirements redundant? Also, doesn’t this negate the power of the keyword in actually enforcing at compile time a restriction when it’s actually important that a class, property or method should not be overridden by subclasses?

I’m curious what those here who work on the language think the best recommendations are, and whether we could update documentation to clear this up.

1 Like

If preserving this optimization is important, then it is important that a class not be overridden. If a co-worker subclasses your class in a different part of the project, then you lose any inferred optimization with no warning. This would be suboptimal if you were relying on it.

Absolutely. This would be another good example of using this keyword to ensure a component is not overridden when it must not be for specific reason.

I think this recommendation however has been misunderstood and led to many organizations instituting a global “tag everything as final for great optimisation”. This then reduces the value of the keyword in these cases.

I would say you should be using final regardless of the optimizations it enables. Generally it’s always best to avoid subclassing in Swift unless you really need it. A lot of tasks that normally would be expressed by subclassing can be better represented through protocols.

Having a mindset of classes should be final first makes it very clear you’re using classes for their reference semantics and not their inheritance capabilities.

1 Like

That’s a highly opinionated programming style that tends not to scale well when utilizing existing frameworks. Also, this doesn't delineate between "we discourage subclassing as a design paradigm" and "this must not be subclassed for performance or other reasons". One clearly should have compiler assistance. The other might better be served by linting with something like SwiftLint.

That said, if this is the default for Swift, we should make final the default, and subclassable the option, not overburden everyone’s code with tons of annotations as “policy”, especially considering we get the optimization either way with WMO.

1 Like

It is opinionated but it is not accurate to say it doesn’t scale well when utilizing existing frameworks. It is accurate to say that subclassing is required, but only subclassing of framework classes, not your own classes. There is rarely a need to write your own superclasses.

There many of us who wish final was the default and argued for exactly that in the early days of Swift Evolution, but I think that ship has sailed at this point.

I personally disagree with this on a philosophical level, but I see the point.

Nevertheless, could we look to clarify the documentation around this to make it clear WMO also gives you these optimizations? It can be a little confusing. Most developers I've quizzed about their "tag everything as final" rule say they do so for the optimisation benefit, and are confused when I say "that comes for free anyway with the default optimisation."

That will then leave it more fairly for the developer's team to create policy with a more clear viewpoint of the reasons one might use this keyword.

Note the preface to that document "The intended audience of this document is compiler and standard library developers."

Since the standard library is not a standalone binary like an app is, even WMO can't give the compiler full visibility into the set of types. For a concrete example, I've recently been looking into overhead of Dictionary bridging in JSON decoding, and was able to get a significant performance win by forcing the compiler to generate a specialization of the bridging function for the [String:Any] case. If this code was inlined into WMO-compiled apps, that would have been unnecessary, but in non-inline library code it was worthwhile.

4 Likes

Thanks for the pickup. That's a really good point that I didn't realise.

Terms of Service

Privacy Policy

Cookie Policy