Question

In swift the compilation time is really slow the amount of code in your project increases. So i was looking for ways to reduce that time. One approach maybe is to use language keywords like final or static to change the way the compiler handles the code in this case using static and dynamic dispatch.

But as far i read is better to avoid runtime overhead reducing dynamic dispatch

So my first doubt is if doing all i can in runtime using more dynamic dispatch reduce compile times at cost of the runtime overhead.

My second doubt is runtime overhead is so bad? that you could sacrifice compile time in order to reduce the overhead?

Was it helpful?

Solution

There is no noticeable difference in compile times for statically dispatched functions and dynamically dispatched functions. While dynamic dispatch does have extra overhead at runtime, this is not noticeable in many cases – you'll have to measure that.

The problem with incremental builds is this: when I edit a file, I'm potentially changing the binary compatibility of the things declared in a file. E.g. when I change the return type of a method, all code using that method will need to be recompiled. So our source files have a dependency graph of who uses whom. When we change a file that sits at the top of this dependency graph, all the files below it have to be recompiled as well, and all files that use those files and so on. This can be a huge cascade.

So the secret of quick compiles is being aware of these dependencies, and breaking the dependencies. Ideally, your dependency graph is very shallow.

C++ programmers know this very well, but they can track dependencies very precisely by seeing which header files are included. When I edit a header file, all files that use that header must be recompiled as well. So the header files should only contain general declarations that are supposed to be stable, and not any implementation details that would be likely to change.

This is where dynamic dispatch comes into play: When I depend on some concrete implementation directly, I accept that I have to recompile when that implementation changes, even if the change is only about an implementation detail. But when I depend only on an abstract interface, then I only have to recompile when the interface changes. The concrete implementation inherits from the interface, and implements any methods required by that interface. So dynamic dispatch can be used to decouple a client from its dependencies:

The client is coupled to its volatile dependency:

+------+    +------------------+
|Client|--->|VolatileDependency|
+------+    +------------------+

The client is decoupled from the volatile dependency through a stable interface:

+------+    +---------------+
|Client|--->|StableInterface|
+------+    +---------------+
                     ^
                     |
            +------------------+
            |VolatileDependency|
            +------------------+

By decoupling compilation units and keeping each file small and focussed, the average time for an incremental compilation can be reduced. However, adding extra abstractions makes the project in its whole more complicated. For Swift, this also requires extra vigilance on the part of the programmer to not accidentally use volatile dependencies, since there doesn't seem to be an explicit per-class import mechanism to declare dependencies.

Licensed under: CC-BY-SA with attribution
scroll top