Swift 拾遗 – 内联函数

微信扫一扫,分享到朋友圈

Swift 拾遗 – 内联函数

Preface

内联函数(Inline Functions)是指编译时刻将函数调用展开成函数体的一种编程语言结构。编译器将适时采取这一技术可以节省调用函数所带来的额外开销。本文将简单讲讲 Swift 中的内联函数。

前置条件

func foo() {
print("foo")
}
foo() // BREAKPOINT :red_circle:

内联函数(Inline Functions)指编译时刻将函数调用展开成函数体。在 Xcode 中的 Swift 项目中,如果开启了编译器优化(Release 模式默认会开启优化),编译器会自动将某些函数变成内联函数。但若函数存在以下特点将不会被自动内联:

  1. 函数体较长时(因内联会导致可执行代码体积增大)
  2. 包含递归调用时(因内联会导致死循环)
  3. 包含动态派发时(因编译时刻无法确认要执行的代码)
func foo() {
print("foo") // BREAKPOINT :red_circle:
}
foo() // BREAKPOINT :red_circle:

将以上代码转换为汇编:

// DEBUG 默认(No Optimization [-Onone])
demo`main:
0x100000a50 <+0>:  pushq  %rbp
0x100000a51 <+1>:  movq   %rsp, %rbp
0x100000a54 <+4>:  subq   $0x10, %rsp
0x100000a58 <+8>:  movl   %edi, -0x4(%rbp)
0x100000a5b <+11>: movq   %rsi, -0x10(%rbp)
; 函数调用(下面开启编译器优化后,则不存在该行汇编指令)
->  0x100000a5f <+15>: callq  0x100000a70               ; demo.foo() -> () at main.swift:11
0x100000a64 <+20>: xorl   %eax, %eax
0x100000a66 <+22>: addq   $0x10, %rsp
0x100000a6a <+26>: popq   %rbp
0x100000a6b <+27>: retq
// DEBUG(Xcode - Target - Build Settings - Swift Compiler - Optimization Level - DEBUG: Optimize for Speed [-O])
demo`main:
0x100000a10 <+0>:   pushq  %rbp
0x100000a11 <+1>:   movq   %rsp, %rbp
0x100000a14 <+4>:   pushq  %rbx
0x100000a15 <+5>:   pushq  %rax
0x100000a16 <+6>:   xorl   %edi, %edi
0x100000a18 <+8>:   callq  0x100000b10               ; type metadata accessor for Swift._ContiguousArrayStorage<Any> at <compiler-generated>
0x100000a1d <+13>:  pushq  $0x40
0x100000a1f <+15>:  popq   %rsi
0x100000a20 <+16>:  pushq  $0x7
0x100000a22 <+18>:  popq   %rdx
0x100000a23 <+19>:  movq   %rax, %rdi
0x100000a26 <+22>:  callq  0x100000e90               ; symbol stub for: swift_allocObject
0x100000a2b <+27>:  movq   %rax, %rbx
0x100000a2e <+30>:  movaps 0x51b(%rip), %xmm0
0x100000a35 <+37>:  movups %xmm0, 0x10(%rax)
->  0x100000a39 <+41>:  movq   0x5c8(%rip), %rax         ; (void *)0x00007fff86ceebc8: type metadata for Swift.String
0x100000a40 <+48>:  movq   %rax, 0x38(%rbx)
0x100000a44 <+52>:  movq   $0x6f6f66, 0x20(%rbx)     ; imm = 0x6F6F66
0x100000a4c <+60>:  movabsq $-0x1d00000000000000, %rax ; imm = 0xE300000000000000
0x100000a56 <+70>:  movq   %rax, 0x28(%rbx)
0x100000a5a <+74>:  pushq  $0x20
0x100000a5c <+76>:  popq   %rsi
0x100000a5d <+77>:  pushq  $0xa
0x100000a5f <+79>:  popq   %rcx
0x100000a60 <+80>:  movabsq $-0x1f00000000000000, %rdx ; imm = 0xE100000000000000
0x100000a6a <+90>:  movq   %rbx, %rdi
0x100000a6d <+93>:  movq   %rdx, %r8
0x100000a70 <+96>:  callq  0x100000e60               ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> ()
0x100000a75 <+101>: movq   %rbx, %rdi
0x100000a78 <+104>: callq  0x100000ea8               ; symbol stub for: swift_release
0x100000a7d <+109>: xorl   %eax, %eax
0x100000a7f <+111>: addq   $0x8, %rsp
0x100000a83 <+115>: popq   %rbx
0x100000a84 <+116>: popq   %rbp
0x100000a85 <+117>: retq
// DEBUG(Optimize for Size [-Osize],多次调用时则不开启内联以降低大小)
demo`main:
0x100000a00 <+0>:   pushq  %rbp
0x100000a01 <+1>:   movq   %rsp, %rbp
0x100000a04 <+4>:   pushq  %rbx
0x100000a05 <+5>:   pushq  %rax
0x100000a06 <+6>:   movq   0x6bb(%rip), %rdi         ; lazy cache variable for type metadata for Swift._ContiguousArrayStorage<Any>
0x100000a0d <+13>:  testq  %rdi, %rdi
0x100000a10 <+16>:  jne    0x100000a33               ; <+51> [inlined] generic specialization <Any> of Swift._allocateUninitializedArray<A>(Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer) + 47 at main.swift:12
0x100000a12 <+18>:  movq   0x5f7(%rip), %rsi         ; (void *)0x00007fff86cf6908: type metadata for Any
0x100000a19 <+25>:  addq   $0x8, %rsi
0x100000a1d <+29>:  xorl   %edi, %edi
0x100000a1f <+31>:  callq  0x100000e5a               ; symbol stub for: type metadata accessor for Swift._ContiguousArrayStorage
0x100000a24 <+36>:  movq   %rax, %rdi
0x100000a27 <+39>:  testq  %rdx, %rdx
0x100000a2a <+42>:  jne    0x100000a33               ; <+51> [inlined] generic specialization <Any> of Swift._allocateUninitializedArray<A>(Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer) + 47 at main.swift:12
0x100000a2c <+44>:  movq   %rdi, 0x695(%rip)         ; lazy cache variable for type metadata for Swift._ContiguousArrayStorage<Any>
0x100000a33 <+51>:  movl   $0x40, %esi
0x100000a38 <+56>:  movl   $0x7, %edx
0x100000a3d <+61>:  callq  0x100000e90               ; symbol stub for: swift_allocObject
0x100000a42 <+66>:  movq   %rax, %rbx
0x100000a45 <+69>:  movaps 0x504(%rip), %xmm0
0x100000a4c <+76>:  movups %xmm0, 0x10(%rax)
->  0x100000a50 <+80>:  movq   0x5b1(%rip), %rax         ; (void *)0x00007fff86ceebc8: type metadata for Swift.String
0x100000a57 <+87>:  movq   %rax, 0x38(%rbx)
0x100000a5b <+91>:  movq   $0x6f6f66, 0x20(%rbx)     ; imm = 0x6F6F66
0x100000a63 <+99>:  movabsq $-0x1d00000000000000, %rax ; imm = 0xE300000000000000
0x100000a6d <+109>: movq   %rax, 0x28(%rbx)
0x100000a71 <+113>: movabsq $-0x1f00000000000000, %rdx ; imm = 0xE100000000000000
0x100000a7b <+123>: movl   $0x20, %esi
0x100000a80 <+128>: movl   $0xa, %ecx
0x100000a85 <+133>: movq   %rbx, %rdi
0x100000a88 <+136>: movq   %rdx, %r8
0x100000a8b <+139>: callq  0x100000e60               ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> ()
0x100000a90 <+144>: movq   %rbx, %rdi
0x100000a93 <+147>: callq  0x100000ea8               ; symbol stub for: swift_release
0x100000a98 <+152>: xorl   %eax, %eax
0x100000a9a <+154>: addq   $0x8, %rsp
0x100000a9e <+158>: popq   %rbx
0x100000a9f <+159>: popq   %rbp
0x100000aa0 <+160>: retq
  • 官方源码中使用 @inline
    来建议(注意「建议」而非强制)编译器去禁止( never
    )或在条件允许的情况下(除递归、动态派发等)总是( __always
    )内联,优先级高于编译设置;
  • 注意 __always
    只在开启编译器优化选项时(无论是优化速度还是体积)几乎总是内联(未开启时将不会被内联)。
// [-O] / [-Osize]
@inline(never) func bar() {
print("bar")
}
@inline(__always) func baz() {
print("baz")
}
bar()
baz()

微信扫一扫,分享到朋友圈

Swift 拾遗 – 内联函数

设计师分享采用环绕屏无按键设计的“iPhone 13”概念视频

上一篇

微软公布Windows 10 v2009硬件要求:CPU要求没变

下一篇

你也可能喜欢

Swift 拾遗 – 内联函数

长按储存图像,分享给朋友