Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Protocol Performance

This question concerns the efficiency of functions written as protocol extensions in Swift 2.2. Does anyone know of a way to speed this runtime up?

Suppose I have a protocol Number of which Int conforms

protocol Number: Equatable, IntegerLiteralConvertible {
    init(_ int: Int)
    init(_ number: Self)
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
}
extension Int: Number { }

Now I want to write a factorial function as an extension of Number

extension Number {
    func factorialNumber() -> Self {
       if self == 0 { return 1 }
       return self * (self - 1).factorialNumber()
    }
}

I also write the same function as an extension of Int

extension Int {
    func factorialInt() -> Int {
        if self == 0 { return 1 }
        return self * (self - 1).factorialInt()
    }
}

When I measure the runtime of each of these functions there is a drastic difference.

Runtime Screenshot

This screenshot is with Whole Module Optimization enabled.

I would guess there is some overhead at runtime from generics. Is there a better way to do this? Does it make sense to just write the same functions over as extensions of Int, Double, Float instead of trying to write one protocol function.

Thanks

like image 225
Mr Flynn Avatar asked Jul 01 '16 19:07

Mr Flynn


People also ask

What are the advantages of protocols in Swift?

An advantage of protocols in Swift is that objects can conform to multiple protocols. When writing an app this way, your code becomes more modular. Think of protocols as building blocks of functionality. When you add new functionality by conforming an object to a protocol, you don't build a whole new object.

What is protocol conformance in Swift?

Any type that satisfies the requirements of a protocol is said to conform to that protocol. In addition to specifying requirements that conforming types must implement, you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.

What is difference between Objective-C protocol and Swift protocol?

In Objective-C, protocols are declared with the “@protocol” keyword. Below is an example of declaring a protocol containing one required method. In Swift, the syntax is a little different but the idea is the same. In Objective-C, you add the protocol name in angle brackets beside the class interface declaration.

Why Swift is protocol oriented medium?

Protocol Oriented Programming or POP is a special feature in Swift as an answer to legacy inheritance and subclassing in other programming languages. POP helps to add features in various classes of different types using protocols.


1 Answers

If we look at the disassembled functions we'll see the cause of the performance penalty - factorialInt has way less instructions than factorialNumber.

Here's how the two methods look when built with Swift 3, Whole Module Optimization enabled (the performance measurements gave similar outputs as the ones from the question which were in Swift 2):

factorialInt:

0000000000001e80         push       rbp                                         ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+27, __TFE8TestFMWKSi19factorialIntWithMulfT_Si+22
0000000000001e81         mov        rbp, rsp
0000000000001e84         push       rbx
0000000000001e85         push       rax
0000000000001e86         mov        rbx, rdi
0000000000001e89         mov        eax, 0x1
0000000000001e8e         test       rbx, rbx
0000000000001e91         je         0x1ea9

0000000000001e93         mov        rdi, rbx
0000000000001e96         dec        rdi
0000000000001e99         jo         0x1eb0

0000000000001e9b         call       __TFE8TestFMWKSi12factorialIntfT_Si
0000000000001ea0         imul       rbx, rax
0000000000001ea4         mov        rax, rbx
0000000000001ea7         jo         0x1eb2

0000000000001ea9         add        rsp, 0x8                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+17
0000000000001ead         pop        rbx
0000000000001eae         pop        rbp
0000000000001eaf         ret        

0000000000001eb0         ud2                                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+25

0000000000001eb2         ud2                                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+39
                        ; endp
0000000000001eb4         nop        word [cs:rax+rax]

factorialNumber:

0000000000001770         push       rbp                                         ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+1648
0000000000001771         mov        rbp, rsp
0000000000001774         push       r15
0000000000001776         push       r14
0000000000001778         push       r13
000000000000177a         push       r12
000000000000177c         push       rbx
000000000000177d         sub        rsp, 0x208
0000000000001784         mov        qword [ss:rbp+var_128], rcx
000000000000178b         mov        rbx, rdx
000000000000178e         mov        qword [ss:rbp+var_F8], rbx
0000000000001795         mov        r14, rsi
0000000000001798         mov        qword [ss:rbp+var_C8], rdi
000000000000179f         mov        rax, qword [ds:rbx]
00000000000017a2         mov        qword [ss:rbp+var_110], rax
00000000000017a9         mov        rdx, qword [ds:rax]
00000000000017ac         mov        qword [ss:rbp+var_E8], rdx
00000000000017b3         mov        rax, qword [ds:r14-8]
00000000000017b7         mov        qword [ss:rbp+var_C0], rax
00000000000017be         mov        rax, qword [ds:rax+0x28]
00000000000017c2         mov        qword [ss:rbp+var_130], rax
00000000000017c9         lea        rdi, qword [ss:rbp+var_40]
00000000000017cd         mov        rsi, rcx
00000000000017d0         mov        rdx, r14
00000000000017d3         call       rax
00000000000017d5         mov        qword [ss:rbp+var_118], rax
00000000000017dc         mov        r15, qword [ds:rbx+8]
00000000000017e0         mov        qword [ss:rbp+var_E0], r15
00000000000017e7         mov        rax, qword [ds:r15+0x10]
00000000000017eb         mov        qword [ss:rbp+var_D0], rax
00000000000017f2         mov        rdi, r14
00000000000017f5         mov        rsi, r15
00000000000017f8         call       qword [ds:r15]
00000000000017fb         mov        r13, rax
00000000000017fe         mov        qword [ss:rbp+var_D8], r13
0000000000001805         mov        rdi, r13
0000000000001808         mov        rsi, r14
000000000000180b         mov        rdx, r15
000000000000180e         call       qword [ds:r15+8]
0000000000001812         mov        rbx, rax
0000000000001815         mov        qword [ss:rbp+var_100], rbx
000000000000181c         mov        r12, qword [ds:rbx]
000000000000181f         mov        qword [ss:rbp+var_F0], r12
0000000000001826         mov        rax, qword [ds:r13-8]
000000000000182a         mov        qword [ss:rbp+var_120], rax
0000000000001831         mov        rax, qword [ds:rax+0x58]
0000000000001835         mov        qword [ss:rbp+var_108], rax
000000000000183c         lea        rdi, qword [ss:rbp+var_58]
0000000000001840         mov        rsi, r13
0000000000001843         call       rax
0000000000001845         mov        qword [ss:rsp+0x230+var_148], rbx
000000000000184d         mov        qword [ss:rsp+0x230+var_150], r13
0000000000001855         mov        qword [ss:rsp+0x230+var_158], r13
000000000000185d         mov        qword [ss:rsp+0x230+var_160], 0x0
0000000000001869         mov        qword [ss:rsp+0x230+var_168], 0x0
0000000000001875         mov        qword [ss:rsp+0x230+var_170], 0x0
0000000000001881         mov        qword [ss:rsp+0x230+var_178], 0x0
000000000000188d         mov        qword [ss:rsp+0x230+var_180], 0x0
0000000000001899         mov        qword [ss:rsp+0x230+var_188], 0x0
00000000000018a5         mov        qword [ss:rsp+0x230+var_190], 0x0
00000000000018b1         mov        qword [ss:rsp+0x230+var_198], 0x0
00000000000018bd         mov        qword [ss:rsp+0x230+var_1A0], 0x0
00000000000018c9         mov        qword [ss:rsp+0x230+var_1A8], 0x0
00000000000018d5         mov        qword [ss:rsp+0x230+var_1B0], 0x0
00000000000018e1         mov        qword [ss:rsp+0x230+var_1B8], 0x0
00000000000018ea         mov        qword [ss:rsp+0x230+var_1C0], 0x0
00000000000018f3         mov        qword [ss:rsp+0x230+var_1C8], 0x0
00000000000018fc         mov        qword [ss:rsp+0x230+var_1D0], 0x0
0000000000001905         mov        qword [ss:rsp+0x230+var_1D8], 0x0
000000000000190e         mov        qword [ss:rsp+0x230+var_1E0], 0x0
0000000000001917         mov        qword [ss:rsp+0x230+var_1E8], 0x0
0000000000001920         mov        qword [ss:rsp+0x230+var_1F0], 0x0
0000000000001929         mov        qword [ss:rsp+0x230+var_1F8], 0x0
0000000000001932         mov        qword [ss:rsp+0x230+var_200], 0x0
000000000000193b         mov        qword [ss:rsp+0x230+var_208], 0x0
0000000000001944         mov        qword [ss:rsp+0x230+var_210], 0x0
000000000000194d         mov        qword [ss:rsp+0x230+var_218], 0x0
0000000000001956         mov        qword [ss:rsp+0x230+var_220], 0x0
000000000000195f         mov        qword [ss:rsp+0x230+var_228], 0x0
0000000000001968         mov        qword [ss:rsp+0x230+var_230], 0x0
0000000000001970         xor        esi, esi
0000000000001972         xor        edx, edx
0000000000001974         xor        ecx, ecx
0000000000001976         xor        r8d, r8d
0000000000001979         xor        r9d, r9d
000000000000197c         mov        rbx, rax
000000000000197f         mov        rdi, rbx
0000000000001982         call       r12
0000000000001985         mov        rax, qword [ss:rbp+var_C0]
000000000000198c         mov        rax, qword [ds:rax+0x58]
0000000000001990         mov        qword [ss:rbp+var_138], rax
0000000000001997         lea        rdi, qword [ss:rbp+var_70]
000000000000199b         mov        rsi, r14
000000000000199e         call       rax
00000000000019a0         mov        r12, rax
00000000000019a3         mov        rdi, r12
00000000000019a6         mov        rsi, rbx
00000000000019a9         mov        rdx, r14
00000000000019ac         mov        rcx, r14
00000000000019af         mov        r8, r15
00000000000019b2         mov        rbx, r13
00000000000019b5         call       qword [ss:rbp+var_D0]
00000000000019bb         mov        rdi, qword [ss:rbp+var_118]
00000000000019c2         mov        rsi, r12
00000000000019c5         mov        rdx, r14
00000000000019c8         mov        rcx, r14
00000000000019cb         mov        r8, qword [ss:rbp+var_110]
00000000000019d2         call       qword [ss:rbp+var_E8]
00000000000019d8         mov        r12b, al
00000000000019db         mov        rax, qword [ss:rbp+var_C0]
00000000000019e2         mov        r13, qword [ds:rax+0x18]
00000000000019e6         lea        rdi, qword [ss:rbp+var_70]
00000000000019ea         mov        rsi, r14
00000000000019ed         call       r13
00000000000019f0         mov        rax, qword [ss:rbp+var_120]
00000000000019f7         mov        rax, qword [ds:rax+0x18]
00000000000019fb         mov        qword [ss:rbp+var_E8], rax
0000000000001a02         lea        rdi, qword [ss:rbp+var_58]
0000000000001a06         mov        rsi, rbx
0000000000001a09         call       rax
0000000000001a0b         lea        rdi, qword [ss:rbp+var_40]
0000000000001a0f         mov        rsi, r14
0000000000001a12         call       r13
0000000000001a15         test       r12b, 0x1
0000000000001a19         je         0x1bb1

0000000000001a1f         lea        r15, qword [ss:rbp+var_40]
0000000000001a23         mov        rdi, r15
0000000000001a26         mov        rbx, qword [ss:rbp+var_D8]
0000000000001a2d         mov        rsi, rbx
0000000000001a30         call       qword [ss:rbp+var_108]
0000000000001a36         mov        r13, rax
0000000000001a39         mov        rax, qword [ss:rbp+var_100]
0000000000001a40         mov        qword [ss:rsp+0x230+var_148], rax
0000000000001a48         mov        qword [ss:rsp+0x230+var_150], rbx
0000000000001a50         mov        qword [ss:rsp+0x230+var_158], rbx
0000000000001a58         mov        qword [ss:rsp+0x230+var_160], 0x0
0000000000001a64         mov        qword [ss:rsp+0x230+var_168], 0x0
0000000000001a70         mov        qword [ss:rsp+0x230+var_170], 0x0
0000000000001a7c         mov        qword [ss:rsp+0x230+var_178], 0x0
0000000000001a88         mov        qword [ss:rsp+0x230+var_180], 0x0
0000000000001a94         mov        qword [ss:rsp+0x230+var_188], 0x0
0000000000001aa0         mov        qword [ss:rsp+0x230+var_190], 0x0
0000000000001aac         mov        qword [ss:rsp+0x230+var_198], 0x0
0000000000001ab8         mov        qword [ss:rsp+0x230+var_1A0], 0x0
0000000000001ac4         mov        qword [ss:rsp+0x230+var_1A8], 0x0
0000000000001ad0         mov        qword [ss:rsp+0x230+var_1B0], 0x0
0000000000001adc         mov        qword [ss:rsp+0x230+var_1B8], 0x0
0000000000001ae5         mov        qword [ss:rsp+0x230+var_1C0], 0x0
0000000000001aee         mov        qword [ss:rsp+0x230+var_1C8], 0x0
0000000000001af7         mov        qword [ss:rsp+0x230+var_1D0], 0x0
0000000000001b00         mov        qword [ss:rsp+0x230+var_1D8], 0x0
0000000000001b09         mov        qword [ss:rsp+0x230+var_1E0], 0x0
0000000000001b12         mov        qword [ss:rsp+0x230+var_1E8], 0x0
0000000000001b1b         mov        qword [ss:rsp+0x230+var_1F0], 0x0
0000000000001b24         mov        qword [ss:rsp+0x230+var_1F8], 0x0
0000000000001b2d         mov        qword [ss:rsp+0x230+var_200], 0x0
0000000000001b36         mov        qword [ss:rsp+0x230+var_208], 0x0
0000000000001b3f         mov        qword [ss:rsp+0x230+var_210], 0x0
0000000000001b48         mov        qword [ss:rsp+0x230+var_218], 0x0
0000000000001b51         mov        qword [ss:rsp+0x230+var_220], 0x0
0000000000001b5a         mov        qword [ss:rsp+0x230+var_228], 0x0
0000000000001b63         mov        qword [ss:rsp+0x230+var_230], 0x0
0000000000001b6b         mov        esi, 0x1
0000000000001b70         xor        edx, edx
0000000000001b72         xor        ecx, ecx
0000000000001b74         xor        r8d, r8d
0000000000001b77         xor        r9d, r9d
0000000000001b7a         mov        rdi, r13
0000000000001b7d         call       qword [ss:rbp+var_F0]
0000000000001b83         mov        rdi, qword [ss:rbp+var_C8]
0000000000001b8a         mov        rsi, r13
0000000000001b8d         mov        rdx, r14
0000000000001b90         mov        rcx, r14
0000000000001b93         mov        r8, qword [ss:rbp+var_E0]
0000000000001b9a         call       qword [ss:rbp+var_D0]
0000000000001ba0         mov        rdi, r15
0000000000001ba3         mov        rsi, rbx
0000000000001ba6         call       qword [ss:rbp+var_E8]
0000000000001bac         jmp        0x1e5a

0000000000001bb1         mov        rbx, qword [ss:rbp+var_F8]                  ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+681
0000000000001bb8         mov        rax, qword [ds:rbx+0x28]
0000000000001bbc         mov        qword [ss:rbp+var_110], rax
0000000000001bc3         lea        rdi, qword [ss:rbp+var_40]
0000000000001bc7         mov        r12, qword [ss:rbp+var_128]
0000000000001bce         mov        rsi, r12
0000000000001bd1         mov        rdx, r14
0000000000001bd4         mov        r15, qword [ss:rbp+var_130]
0000000000001bdb         call       r15
0000000000001bde         mov        qword [ss:rbp+var_118], rax
0000000000001be5         mov        rax, qword [ds:rbx+0x30]
0000000000001be9         mov        qword [ss:rbp+var_120], rax
0000000000001bf0         lea        rdi, qword [ss:rbp+var_58]
0000000000001bf4         mov        rsi, r12
0000000000001bf7         mov        rdx, r14
0000000000001bfa         call       r15
0000000000001bfd         mov        r15, rax
0000000000001c00         lea        rdi, qword [ss:rbp+var_70]
0000000000001c04         mov        rbx, qword [ss:rbp+var_D8]
0000000000001c0b         mov        rsi, rbx
0000000000001c0e         call       qword [ss:rbp+var_108]
0000000000001c14         mov        qword [ss:rbp+var_108], r13
0000000000001c1b         mov        r13, rax
0000000000001c1e         mov        rax, qword [ss:rbp+var_100]
0000000000001c25         mov        qword [ss:rsp+0x230+var_148], rax
0000000000001c2d         mov        qword [ss:rsp+0x230+var_150], rbx
0000000000001c35         mov        qword [ss:rsp+0x230+var_158], rbx
0000000000001c3d         mov        qword [ss:rsp+0x230+var_160], 0x0
0000000000001c49         mov        qword [ss:rsp+0x230+var_168], 0x0
0000000000001c55         mov        qword [ss:rsp+0x230+var_170], 0x0
0000000000001c61         mov        qword [ss:rsp+0x230+var_178], 0x0
0000000000001c6d         mov        qword [ss:rsp+0x230+var_180], 0x0
0000000000001c79         mov        qword [ss:rsp+0x230+var_188], 0x0
0000000000001c85         mov        qword [ss:rsp+0x230+var_190], 0x0
0000000000001c91         mov        qword [ss:rsp+0x230+var_198], 0x0
0000000000001c9d         mov        qword [ss:rsp+0x230+var_1A0], 0x0
0000000000001ca9         mov        qword [ss:rsp+0x230+var_1A8], 0x0
0000000000001cb5         mov        qword [ss:rsp+0x230+var_1B0], 0x0
0000000000001cc1         mov        qword [ss:rsp+0x230+var_1B8], 0x0
0000000000001cca         mov        qword [ss:rsp+0x230+var_1C0], 0x0
0000000000001cd3         mov        qword [ss:rsp+0x230+var_1C8], 0x0
0000000000001cdc         mov        qword [ss:rsp+0x230+var_1D0], 0x0
0000000000001ce5         mov        qword [ss:rsp+0x230+var_1D8], 0x0
0000000000001cee         mov        qword [ss:rsp+0x230+var_1E0], 0x0
0000000000001cf7         mov        qword [ss:rsp+0x230+var_1E8], 0x0
0000000000001d00         mov        qword [ss:rsp+0x230+var_1F0], 0x0
0000000000001d09         mov        qword [ss:rsp+0x230+var_1F8], 0x0
0000000000001d12         mov        qword [ss:rsp+0x230+var_200], 0x0
0000000000001d1b         mov        qword [ss:rsp+0x230+var_208], 0x0
0000000000001d24         mov        qword [ss:rsp+0x230+var_210], 0x0
0000000000001d2d         mov        qword [ss:rsp+0x230+var_218], 0x0
0000000000001d36         mov        qword [ss:rsp+0x230+var_220], 0x0
0000000000001d3f         mov        qword [ss:rsp+0x230+var_228], 0x0
0000000000001d48         mov        qword [ss:rsp+0x230+var_230], 0x0
0000000000001d50         mov        esi, 0x1
0000000000001d55         xor        edx, edx
0000000000001d57         xor        ecx, ecx
0000000000001d59         xor        r8d, r8d
0000000000001d5c         xor        r9d, r9d
0000000000001d5f         mov        rdi, r13
0000000000001d62         call       qword [ss:rbp+var_F0]
0000000000001d68         lea        rdi, qword [ss:rbp+var_88]
0000000000001d6f         mov        rsi, r14
0000000000001d72         mov        rbx, qword [ss:rbp+var_138]
0000000000001d79         call       rbx
0000000000001d7b         mov        r12, rax
0000000000001d7e         mov        rdi, r12
0000000000001d81         mov        rsi, r13
0000000000001d84         mov        rdx, r14
0000000000001d87         mov        rcx, r14
0000000000001d8a         mov        r8, qword [ss:rbp+var_E0]
0000000000001d91         call       qword [ss:rbp+var_D0]
0000000000001d97         lea        rdi, qword [ss:rbp+var_A0]
0000000000001d9e         mov        rsi, r14
0000000000001da1         call       rbx
0000000000001da3         mov        r13, rax
0000000000001da6         mov        rdi, r13
0000000000001da9         mov        rsi, r15
0000000000001dac         mov        rdx, r12
0000000000001daf         mov        rcx, r14
0000000000001db2         mov        r8, r14
0000000000001db5         mov        r15, qword [ss:rbp+var_F8]
0000000000001dbc         mov        r9, r15
0000000000001dbf         call       qword [ss:rbp+var_120]
0000000000001dc5         lea        rdi, qword [ss:rbp+var_B8]
0000000000001dcc         mov        rsi, r14
0000000000001dcf         call       rbx
0000000000001dd1         mov        r12, rax
0000000000001dd4         mov        rdi, r12                                    ; argument #1 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x
0000000000001dd7         mov        rsi, r14                                    ; argument #2 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x
0000000000001dda         mov        rdx, r15                                    ; argument #3 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x
0000000000001ddd         mov        rcx, r13                                    ; argument #4 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x
0000000000001de0         call       __TFE8TestFMWKPS_6Number15factorialNumberfT_x
0000000000001de5         mov        rdi, qword [ss:rbp+var_C8]
0000000000001dec         mov        rsi, qword [ss:rbp+var_118]
0000000000001df3         mov        rdx, r12
0000000000001df6         mov        rcx, r14
0000000000001df9         mov        r8, r14
0000000000001dfc         mov        r9, r15
0000000000001dff         call       qword [ss:rbp+var_110]
0000000000001e05         lea        rdi, qword [ss:rbp+var_B8]
0000000000001e0c         mov        rsi, r14
0000000000001e0f         mov        rbx, qword [ss:rbp+var_108]
0000000000001e16         call       rbx
0000000000001e18         lea        rdi, qword [ss:rbp+var_A0]
0000000000001e1f         mov        rsi, r14
0000000000001e22         mov        rax, qword [ss:rbp+var_C0]
0000000000001e29         call       qword [ds:rax]
0000000000001e2b         lea        rdi, qword [ss:rbp+var_88]
0000000000001e32         mov        rsi, r14
0000000000001e35         call       rbx
0000000000001e37         lea        rdi, qword [ss:rbp+var_70]
0000000000001e3b         mov        rsi, qword [ss:rbp+var_D8]
0000000000001e42         call       qword [ss:rbp+var_E8]
0000000000001e48         lea        rdi, qword [ss:rbp+var_58]
0000000000001e4c         mov        rsi, r14
0000000000001e4f         call       rbx
0000000000001e51         lea        rdi, qword [ss:rbp+var_40]
0000000000001e55         mov        rsi, r14
0000000000001e58         call       rbx

0000000000001e5a         mov        rax, qword [ss:rbp+var_C8]                  ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+1084
0000000000001e61         add        rsp, 0x208
0000000000001e68         pop        rbx
0000000000001e69         pop        r12
0000000000001e6b         pop        r13
0000000000001e6d         pop        r14
0000000000001e6f         pop        r15
0000000000001e71         pop        rbp
0000000000001e72         ret        
                        ; endp
0000000000001e73         nop        word [cs:rax+rax]

Dispatching via protocols comes with a cost - methods are no longer statically dispatched and have to be looked-up within the dispatch table of the data-type the receiver belongs to.

Turns out that this lookup requires a few instructions to be done, leading to the performance difference you noticed.

like image 80
Cristik Avatar answered Sep 28 '22 01:09

Cristik