Swift Protocol Performance Swift Protocol Performance swift swift

Swift Protocol Performance


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+220000000000001e81         mov        rbp, rsp0000000000001e84         push       rbx0000000000001e85         push       rax0000000000001e86         mov        rbx, rdi0000000000001e89         mov        eax, 0x10000000000001e8e         test       rbx, rbx0000000000001e91         je         0x1ea90000000000001e93         mov        rdi, rbx0000000000001e96         dec        rdi0000000000001e99         jo         0x1eb00000000000001e9b         call       __TFE8TestFMWKSi12factorialIntfT_Si0000000000001ea0         imul       rbx, rax0000000000001ea4         mov        rax, rbx0000000000001ea7         jo         0x1eb20000000000001ea9         add        rsp, 0x8                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+170000000000001ead         pop        rbx0000000000001eae         pop        rbp0000000000001eaf         ret        0000000000001eb0         ud2                                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+250000000000001eb2         ud2                                                    ; XREF=__TFE8TestFMWKSi12factorialIntfT_Si+39                        ; endp0000000000001eb4         nop        word [cs:rax+rax]

factorialNumber:

0000000000001770         push       rbp                                         ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+16480000000000001771         mov        rbp, rsp0000000000001774         push       r150000000000001776         push       r140000000000001778         push       r13000000000000177a         push       r12000000000000177c         push       rbx000000000000177d         sub        rsp, 0x2080000000000001784         mov        qword [ss:rbp+var_128], rcx000000000000178b         mov        rbx, rdx000000000000178e         mov        qword [ss:rbp+var_F8], rbx0000000000001795         mov        r14, rsi0000000000001798         mov        qword [ss:rbp+var_C8], rdi000000000000179f         mov        rax, qword [ds:rbx]00000000000017a2         mov        qword [ss:rbp+var_110], rax00000000000017a9         mov        rdx, qword [ds:rax]00000000000017ac         mov        qword [ss:rbp+var_E8], rdx00000000000017b3         mov        rax, qword [ds:r14-8]00000000000017b7         mov        qword [ss:rbp+var_C0], rax00000000000017be         mov        rax, qword [ds:rax+0x28]00000000000017c2         mov        qword [ss:rbp+var_130], rax00000000000017c9         lea        rdi, qword [ss:rbp+var_40]00000000000017cd         mov        rsi, rcx00000000000017d0         mov        rdx, r1400000000000017d3         call       rax00000000000017d5         mov        qword [ss:rbp+var_118], rax00000000000017dc         mov        r15, qword [ds:rbx+8]00000000000017e0         mov        qword [ss:rbp+var_E0], r1500000000000017e7         mov        rax, qword [ds:r15+0x10]00000000000017eb         mov        qword [ss:rbp+var_D0], rax00000000000017f2         mov        rdi, r1400000000000017f5         mov        rsi, r1500000000000017f8         call       qword [ds:r15]00000000000017fb         mov        r13, rax00000000000017fe         mov        qword [ss:rbp+var_D8], r130000000000001805         mov        rdi, r130000000000001808         mov        rsi, r14000000000000180b         mov        rdx, r15000000000000180e         call       qword [ds:r15+8]0000000000001812         mov        rbx, rax0000000000001815         mov        qword [ss:rbp+var_100], rbx000000000000181c         mov        r12, qword [ds:rbx]000000000000181f         mov        qword [ss:rbp+var_F0], r120000000000001826         mov        rax, qword [ds:r13-8]000000000000182a         mov        qword [ss:rbp+var_120], rax0000000000001831         mov        rax, qword [ds:rax+0x58]0000000000001835         mov        qword [ss:rbp+var_108], rax000000000000183c         lea        rdi, qword [ss:rbp+var_58]0000000000001840         mov        rsi, r130000000000001843         call       rax0000000000001845         mov        qword [ss:rsp+0x230+var_148], rbx000000000000184d         mov        qword [ss:rsp+0x230+var_150], r130000000000001855         mov        qword [ss:rsp+0x230+var_158], r13000000000000185d         mov        qword [ss:rsp+0x230+var_160], 0x00000000000001869         mov        qword [ss:rsp+0x230+var_168], 0x00000000000001875         mov        qword [ss:rsp+0x230+var_170], 0x00000000000001881         mov        qword [ss:rsp+0x230+var_178], 0x0000000000000188d         mov        qword [ss:rsp+0x230+var_180], 0x00000000000001899         mov        qword [ss:rsp+0x230+var_188], 0x000000000000018a5         mov        qword [ss:rsp+0x230+var_190], 0x000000000000018b1         mov        qword [ss:rsp+0x230+var_198], 0x000000000000018bd         mov        qword [ss:rsp+0x230+var_1A0], 0x000000000000018c9         mov        qword [ss:rsp+0x230+var_1A8], 0x000000000000018d5         mov        qword [ss:rsp+0x230+var_1B0], 0x000000000000018e1         mov        qword [ss:rsp+0x230+var_1B8], 0x000000000000018ea         mov        qword [ss:rsp+0x230+var_1C0], 0x000000000000018f3         mov        qword [ss:rsp+0x230+var_1C8], 0x000000000000018fc         mov        qword [ss:rsp+0x230+var_1D0], 0x00000000000001905         mov        qword [ss:rsp+0x230+var_1D8], 0x0000000000000190e         mov        qword [ss:rsp+0x230+var_1E0], 0x00000000000001917         mov        qword [ss:rsp+0x230+var_1E8], 0x00000000000001920         mov        qword [ss:rsp+0x230+var_1F0], 0x00000000000001929         mov        qword [ss:rsp+0x230+var_1F8], 0x00000000000001932         mov        qword [ss:rsp+0x230+var_200], 0x0000000000000193b         mov        qword [ss:rsp+0x230+var_208], 0x00000000000001944         mov        qword [ss:rsp+0x230+var_210], 0x0000000000000194d         mov        qword [ss:rsp+0x230+var_218], 0x00000000000001956         mov        qword [ss:rsp+0x230+var_220], 0x0000000000000195f         mov        qword [ss:rsp+0x230+var_228], 0x00000000000001968         mov        qword [ss:rsp+0x230+var_230], 0x00000000000001970         xor        esi, esi0000000000001972         xor        edx, edx0000000000001974         xor        ecx, ecx0000000000001976         xor        r8d, r8d0000000000001979         xor        r9d, r9d000000000000197c         mov        rbx, rax000000000000197f         mov        rdi, rbx0000000000001982         call       r120000000000001985         mov        rax, qword [ss:rbp+var_C0]000000000000198c         mov        rax, qword [ds:rax+0x58]0000000000001990         mov        qword [ss:rbp+var_138], rax0000000000001997         lea        rdi, qword [ss:rbp+var_70]000000000000199b         mov        rsi, r14000000000000199e         call       rax00000000000019a0         mov        r12, rax00000000000019a3         mov        rdi, r1200000000000019a6         mov        rsi, rbx00000000000019a9         mov        rdx, r1400000000000019ac         mov        rcx, r1400000000000019af         mov        r8, r1500000000000019b2         mov        rbx, r1300000000000019b5         call       qword [ss:rbp+var_D0]00000000000019bb         mov        rdi, qword [ss:rbp+var_118]00000000000019c2         mov        rsi, r1200000000000019c5         mov        rdx, r1400000000000019c8         mov        rcx, r1400000000000019cb         mov        r8, qword [ss:rbp+var_110]00000000000019d2         call       qword [ss:rbp+var_E8]00000000000019d8         mov        r12b, al00000000000019db         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, r1400000000000019ed         call       r1300000000000019f0         mov        rax, qword [ss:rbp+var_120]00000000000019f7         mov        rax, qword [ds:rax+0x18]00000000000019fb         mov        qword [ss:rbp+var_E8], rax0000000000001a02         lea        rdi, qword [ss:rbp+var_58]0000000000001a06         mov        rsi, rbx0000000000001a09         call       rax0000000000001a0b         lea        rdi, qword [ss:rbp+var_40]0000000000001a0f         mov        rsi, r140000000000001a12         call       r130000000000001a15         test       r12b, 0x10000000000001a19         je         0x1bb10000000000001a1f         lea        r15, qword [ss:rbp+var_40]0000000000001a23         mov        rdi, r150000000000001a26         mov        rbx, qword [ss:rbp+var_D8]0000000000001a2d         mov        rsi, rbx0000000000001a30         call       qword [ss:rbp+var_108]0000000000001a36         mov        r13, rax0000000000001a39         mov        rax, qword [ss:rbp+var_100]0000000000001a40         mov        qword [ss:rsp+0x230+var_148], rax0000000000001a48         mov        qword [ss:rsp+0x230+var_150], rbx0000000000001a50         mov        qword [ss:rsp+0x230+var_158], rbx0000000000001a58         mov        qword [ss:rsp+0x230+var_160], 0x00000000000001a64         mov        qword [ss:rsp+0x230+var_168], 0x00000000000001a70         mov        qword [ss:rsp+0x230+var_170], 0x00000000000001a7c         mov        qword [ss:rsp+0x230+var_178], 0x00000000000001a88         mov        qword [ss:rsp+0x230+var_180], 0x00000000000001a94         mov        qword [ss:rsp+0x230+var_188], 0x00000000000001aa0         mov        qword [ss:rsp+0x230+var_190], 0x00000000000001aac         mov        qword [ss:rsp+0x230+var_198], 0x00000000000001ab8         mov        qword [ss:rsp+0x230+var_1A0], 0x00000000000001ac4         mov        qword [ss:rsp+0x230+var_1A8], 0x00000000000001ad0         mov        qword [ss:rsp+0x230+var_1B0], 0x00000000000001adc         mov        qword [ss:rsp+0x230+var_1B8], 0x00000000000001ae5         mov        qword [ss:rsp+0x230+var_1C0], 0x00000000000001aee         mov        qword [ss:rsp+0x230+var_1C8], 0x00000000000001af7         mov        qword [ss:rsp+0x230+var_1D0], 0x00000000000001b00         mov        qword [ss:rsp+0x230+var_1D8], 0x00000000000001b09         mov        qword [ss:rsp+0x230+var_1E0], 0x00000000000001b12         mov        qword [ss:rsp+0x230+var_1E8], 0x00000000000001b1b         mov        qword [ss:rsp+0x230+var_1F0], 0x00000000000001b24         mov        qword [ss:rsp+0x230+var_1F8], 0x00000000000001b2d         mov        qword [ss:rsp+0x230+var_200], 0x00000000000001b36         mov        qword [ss:rsp+0x230+var_208], 0x00000000000001b3f         mov        qword [ss:rsp+0x230+var_210], 0x00000000000001b48         mov        qword [ss:rsp+0x230+var_218], 0x00000000000001b51         mov        qword [ss:rsp+0x230+var_220], 0x00000000000001b5a         mov        qword [ss:rsp+0x230+var_228], 0x00000000000001b63         mov        qword [ss:rsp+0x230+var_230], 0x00000000000001b6b         mov        esi, 0x10000000000001b70         xor        edx, edx0000000000001b72         xor        ecx, ecx0000000000001b74         xor        r8d, r8d0000000000001b77         xor        r9d, r9d0000000000001b7a         mov        rdi, r130000000000001b7d         call       qword [ss:rbp+var_F0]0000000000001b83         mov        rdi, qword [ss:rbp+var_C8]0000000000001b8a         mov        rsi, r130000000000001b8d         mov        rdx, r140000000000001b90         mov        rcx, r140000000000001b93         mov        r8, qword [ss:rbp+var_E0]0000000000001b9a         call       qword [ss:rbp+var_D0]0000000000001ba0         mov        rdi, r150000000000001ba3         mov        rsi, rbx0000000000001ba6         call       qword [ss:rbp+var_E8]0000000000001bac         jmp        0x1e5a0000000000001bb1         mov        rbx, qword [ss:rbp+var_F8]                  ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+6810000000000001bb8         mov        rax, qword [ds:rbx+0x28]0000000000001bbc         mov        qword [ss:rbp+var_110], rax0000000000001bc3         lea        rdi, qword [ss:rbp+var_40]0000000000001bc7         mov        r12, qword [ss:rbp+var_128]0000000000001bce         mov        rsi, r120000000000001bd1         mov        rdx, r140000000000001bd4         mov        r15, qword [ss:rbp+var_130]0000000000001bdb         call       r150000000000001bde         mov        qword [ss:rbp+var_118], rax0000000000001be5         mov        rax, qword [ds:rbx+0x30]0000000000001be9         mov        qword [ss:rbp+var_120], rax0000000000001bf0         lea        rdi, qword [ss:rbp+var_58]0000000000001bf4         mov        rsi, r120000000000001bf7         mov        rdx, r140000000000001bfa         call       r150000000000001bfd         mov        r15, rax0000000000001c00         lea        rdi, qword [ss:rbp+var_70]0000000000001c04         mov        rbx, qword [ss:rbp+var_D8]0000000000001c0b         mov        rsi, rbx0000000000001c0e         call       qword [ss:rbp+var_108]0000000000001c14         mov        qword [ss:rbp+var_108], r130000000000001c1b         mov        r13, rax0000000000001c1e         mov        rax, qword [ss:rbp+var_100]0000000000001c25         mov        qword [ss:rsp+0x230+var_148], rax0000000000001c2d         mov        qword [ss:rsp+0x230+var_150], rbx0000000000001c35         mov        qword [ss:rsp+0x230+var_158], rbx0000000000001c3d         mov        qword [ss:rsp+0x230+var_160], 0x00000000000001c49         mov        qword [ss:rsp+0x230+var_168], 0x00000000000001c55         mov        qword [ss:rsp+0x230+var_170], 0x00000000000001c61         mov        qword [ss:rsp+0x230+var_178], 0x00000000000001c6d         mov        qword [ss:rsp+0x230+var_180], 0x00000000000001c79         mov        qword [ss:rsp+0x230+var_188], 0x00000000000001c85         mov        qword [ss:rsp+0x230+var_190], 0x00000000000001c91         mov        qword [ss:rsp+0x230+var_198], 0x00000000000001c9d         mov        qword [ss:rsp+0x230+var_1A0], 0x00000000000001ca9         mov        qword [ss:rsp+0x230+var_1A8], 0x00000000000001cb5         mov        qword [ss:rsp+0x230+var_1B0], 0x00000000000001cc1         mov        qword [ss:rsp+0x230+var_1B8], 0x00000000000001cca         mov        qword [ss:rsp+0x230+var_1C0], 0x00000000000001cd3         mov        qword [ss:rsp+0x230+var_1C8], 0x00000000000001cdc         mov        qword [ss:rsp+0x230+var_1D0], 0x00000000000001ce5         mov        qword [ss:rsp+0x230+var_1D8], 0x00000000000001cee         mov        qword [ss:rsp+0x230+var_1E0], 0x00000000000001cf7         mov        qword [ss:rsp+0x230+var_1E8], 0x00000000000001d00         mov        qword [ss:rsp+0x230+var_1F0], 0x00000000000001d09         mov        qword [ss:rsp+0x230+var_1F8], 0x00000000000001d12         mov        qword [ss:rsp+0x230+var_200], 0x00000000000001d1b         mov        qword [ss:rsp+0x230+var_208], 0x00000000000001d24         mov        qword [ss:rsp+0x230+var_210], 0x00000000000001d2d         mov        qword [ss:rsp+0x230+var_218], 0x00000000000001d36         mov        qword [ss:rsp+0x230+var_220], 0x00000000000001d3f         mov        qword [ss:rsp+0x230+var_228], 0x00000000000001d48         mov        qword [ss:rsp+0x230+var_230], 0x00000000000001d50         mov        esi, 0x10000000000001d55         xor        edx, edx0000000000001d57         xor        ecx, ecx0000000000001d59         xor        r8d, r8d0000000000001d5c         xor        r9d, r9d0000000000001d5f         mov        rdi, r130000000000001d62         call       qword [ss:rbp+var_F0]0000000000001d68         lea        rdi, qword [ss:rbp+var_88]0000000000001d6f         mov        rsi, r140000000000001d72         mov        rbx, qword [ss:rbp+var_138]0000000000001d79         call       rbx0000000000001d7b         mov        r12, rax0000000000001d7e         mov        rdi, r120000000000001d81         mov        rsi, r130000000000001d84         mov        rdx, r140000000000001d87         mov        rcx, r140000000000001d8a         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, r140000000000001da1         call       rbx0000000000001da3         mov        r13, rax0000000000001da6         mov        rdi, r130000000000001da9         mov        rsi, r150000000000001dac         mov        rdx, r120000000000001daf         mov        rcx, r140000000000001db2         mov        r8, r140000000000001db5         mov        r15, qword [ss:rbp+var_F8]0000000000001dbc         mov        r9, r150000000000001dbf         call       qword [ss:rbp+var_120]0000000000001dc5         lea        rdi, qword [ss:rbp+var_B8]0000000000001dcc         mov        rsi, r140000000000001dcf         call       rbx0000000000001dd1         mov        r12, rax0000000000001dd4         mov        rdi, r12                                    ; argument #1 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x0000000000001dd7         mov        rsi, r14                                    ; argument #2 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x0000000000001dda         mov        rdx, r15                                    ; argument #3 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x0000000000001ddd         mov        rcx, r13                                    ; argument #4 for method __TFE8TestFMWKPS_6Number15factorialNumberfT_x0000000000001de0         call       __TFE8TestFMWKPS_6Number15factorialNumberfT_x0000000000001de5         mov        rdi, qword [ss:rbp+var_C8]0000000000001dec         mov        rsi, qword [ss:rbp+var_118]0000000000001df3         mov        rdx, r120000000000001df6         mov        rcx, r140000000000001df9         mov        r8, r140000000000001dfc         mov        r9, r150000000000001dff         call       qword [ss:rbp+var_110]0000000000001e05         lea        rdi, qword [ss:rbp+var_B8]0000000000001e0c         mov        rsi, r140000000000001e0f         mov        rbx, qword [ss:rbp+var_108]0000000000001e16         call       rbx0000000000001e18         lea        rdi, qword [ss:rbp+var_A0]0000000000001e1f         mov        rsi, r140000000000001e22         mov        rax, qword [ss:rbp+var_C0]0000000000001e29         call       qword [ds:rax]0000000000001e2b         lea        rdi, qword [ss:rbp+var_88]0000000000001e32         mov        rsi, r140000000000001e35         call       rbx0000000000001e37         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, r140000000000001e4f         call       rbx0000000000001e51         lea        rdi, qword [ss:rbp+var_40]0000000000001e55         mov        rsi, r140000000000001e58         call       rbx0000000000001e5a         mov        rax, qword [ss:rbp+var_C8]                  ; XREF=__TFE8TestFMWKPS_6Number15factorialNumberfT_x+10840000000000001e61         add        rsp, 0x2080000000000001e68         pop        rbx0000000000001e69         pop        r120000000000001e6b         pop        r130000000000001e6d         pop        r140000000000001e6f         pop        r150000000000001e71         pop        rbp0000000000001e72         ret                                ; endp0000000000001e73         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.