preface
For compiled languages, there are three main types of function distribution, which are:
- Direct Dispatch: Direct Dispatch
- Table Dispatch: Function Table Dispatch
- Message Dispatch: Sends messages
Analysis of three methods for distributed mainly from two aspects of dynamic performance and discussion, the two characteristics is contradictory, relatively high performance requirements, is dynamic, and vice versa, which is also known as static begin directly, distributed function table and distributed message is called dynamic dispatch, most languages support distributed way above one to more. Such as
- C Use direct distribution;
- By default, Java uses function table dispatches, which can be changed to direct dispatches with the final modifier.
- C++ uses direct dispatches by default, but can be changed to function table dispatches by adding the virtual modifier;
- OC uses direct distribution and message mechanism distribution. (The common method uses the message distribution mode, and the load method uses the direct distribution mode)
Direct distribution
Direct dispatch is the fastest of the three forms. At compile time, the address of the method is determined. In assembly code, the execution of the method directly jumps to the address and generates the least assembly instructions. Advantages: The compiler can do more to optimize this distribution, such as function inlining. Disadvantages: lack of dynamic, unable to achieve inheritance;
Function table distribution
Function tables, a common distribution method in compiled languages, use arrays to store Pointers to each function declared in a class. For this table, most languages call it Virtual Table. According to the analysis of THE SIL file generated by the compilation of Swift, there are two kinds of function tables in Swift. The protocol is WITNess_TABLE (sil_WITNess_TABLE in the SIL file). The class uses virtual_Table (sil_vtable in the SIL file).
Each class maintains a table of all its functions. If a parent function is override, the table holds only the function after the override. Any new function added by a subclass is inserted at the end of this array. The runtime uses this table to determine which functions are actually called;
When a function is called, it reads the object’s function table (read first), adds the function’s address to the class’s address (read second), and jumps to that address (jump once). The whole process is two reads and one jump, which is slower than direct dispatch.
Messages distributed
Message distribution is the most dynamic distribution mode, but also one of the worst performance. Method calls into the message, sent to the runtime (broker), run time find the class object, the object will store data information to classes, or via the parent class, until the hit, if can’t find method, an exception is thrown, the runtime provides a lot of dynamic method is used to change the behavior provided by the news. Compared with the dynamics of distributed function table there is a strong, Because the runtime supports many functions, the method lookup process is long, so the performance is low; The OC news distribution process will be discussed here, and there will be a subsequent blog devoted to this.
Function dispatch in Swift
By analyzing THE SIL file, we can analyze the rules of the distribution mode in Swift. For the SIL related knowledge, please refer to the article iOS compilation brief analysis. This article only gives key commands swiftc main. Swift – emit – sil | xcrun swift – demangle > main. Sil.
Correspondence between distribution mode and key instruction in SIL file
- Sil_witness_table/SIL_VTABLE: Function table distribution
- Objc_method: message distribution mechanism
- Those not within the above scope are distributed directly;
The Swift language supports three distribution modes. The approach is related to four factors:
- Declared location
- Reference types
- Specify the behavior
- Optimize explicitly
Direct distribution | Function table distribution | Messages distributed | |
---|---|---|---|
NSObject | @nonobjc or final modifier | Declare scoped methods | Extension methods and methods modified by dynamic |
Class | Extension methods that are not modified by @objc and methods that are modified by final | Declare scoped methods | Dynamic modified methods or @objc modified extension methods |
Protocol | Extension methods | Declare scoped methods | @objc modified methods or all methods in objc modified protocols |
Value Type | All the methods | There is no | There is no |
other | Global method, staic modified method; All methods ina class declared using final; Methods and properties that are declared private are implicitly final; |
This table gives you an idea of some of the limitations of the Swift language:
- Methods defined in Extension that want to overRite need the @objc modifier on the method; Because if you don’t put @objc, you’re just going to be sending, you can’t override methods.
Swift distribution optimization
Inline optimization
Swift compilation can also be optimized on the basis of direct dispatch, such as function inlining.
The main principle of inlining is to compile the implementation of some functions directly into the position of the calling function, reduce the stack call of function pointer, and improve the operation efficiency. When Optimization Level is turned on, the compiler optimizes the function inline based on the actual situation in the direct dispatch mode. The compiler defaults to inlining optimizations in the following cases:
- Function body is too long (increasing package size and repeating code);
- Functions include dynamic dispatches;
- Functions contain recursive calls;
An explicit inline optimization modifier in Swift
@inline(never)
Declare that this function is never compiled inline, even with compiler optimization turned on;@inline(__always)
Declare that the function is always compiled inline. This modifier only applies if the body of the function is too long to be inline optimized.
In addition to the benefit of improved efficiency, inlining optimization of some key functions makes it more difficult to reverse.
Distribute as directly as possible
Swift will optimize the distribution mode as much as possible, and some function table distribution methods will be optimized to direct distribution. The compiler can check for inheritance through Whole Module Optimization, evaluate certain classes that are not marked final, and use direct dispatch if it can determine which method to execute at compile time. For example, if a function does not override, Swift might use the direct dispatch method.
It’s important to have a tech community and a group of like-minded people, so come to my tech account and blog, and just talk about tech stuff.
- Wechat official account: CoderStar
- Blog: CoderStar’s Blog