Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Better Recursion With Swift
Search
Matt Isaacs
August 26, 2015
Programming
1
590
Better Recursion With Swift
Presented at the Brooklyn Swift Developers Meetup, August 2015.
Matt Isaacs
August 26, 2015
Tweet
Share
More Decks by Matt Isaacs
See All by Matt Isaacs
iOS Testing with Appium at Gilt
ontherocks
0
140
Mobile Testing at Gilt
ontherocks
0
47
Other Decks in Programming
See All in Programming
Composerが「依存解決」のためにどんな工夫をしているか #phpcon
o0h
PRO
1
210
Blazing Fast UI Development with Compose Hot Reload (droidcon New York 2025)
zsmb
1
190
プロダクト志向なエンジニアがもう一歩先の価値を目指すために意識したこと
nealle
0
110
VS Code Update for GitHub Copilot
74th
1
310
AWS CDKの推しポイント 〜CloudFormationと比較してみた〜
akihisaikeda
3
310
deno-redisの紹介とJSRパッケージの運用について (toranoana.deno #21)
uki00a
0
140
GraphRAGの仕組みまるわかり
tosuri13
7
480
すべてのコンテキストを、 ユーザー価値に変える
applism118
2
720
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
130
アンドパッドの Go 勉強会「 gopher 会」とその内容の紹介
andpad
0
260
ASP.NETアプリケーションのモダナイズ インフラ編
tomokusaba
1
410
Deep Dive into ~/.claude/projects
hiragram
7
1.3k
Featured
See All Featured
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
124
52k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
490
Building an army of robots
kneath
306
45k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
930
Building a Modern Day E-commerce SEO Strategy
aleyda
41
7.3k
GraphQLとの向き合い方2022年版
quramy
47
14k
Documentation Writing (for coders)
carmenintech
71
4.9k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
33
5.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
330
24k
Product Roadmaps are Hard
iamctodd
PRO
53
11k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
Fireside Chat
paigeccino
37
3.5k
Transcript
BETTER RECURSION WITH SWIFT
AKA…
RAIDERS OF THE LOST ARC Another reason why Swift value
types matter.
None
None
None
FAST. MODERN. SAFE
FUNCTIONAL-ISH
let str = "Favours Immutability"
GOOD: CONCURRENCY
MEH: CACHE EFFICIENCY
CHALLENGE FOR ITERATION
func factorial(n: Int) -> Int { var result = 1
var x = n while x > 0 { result = result * x x-- } return result }
RECURSION Immutable iteration!
func factorialRec(n: Int) -> Int { if n > 0
{ return n * factorialRec(n - 1) } return 1 }
ELEGANT.
YET INEFFICIENT. New call frame on each iteration.
STACK OVERFLOW Easier than ever with recursion!
All Is not Lost
TAIL CALLS Recursive call is performed last.
func tco(n: Int, acc: Int) -> Int { if n
> 0 { return tco(n - 1, acc * n) } return acc }
func tco(n: Int, acc: Int) -> Int { if n
> 0 { return tco(n - 1, acc * n) } return acc } func factorialTCO(n: Int) -> Int { return tco(n, 1) }
func tco(n: Int, acc: Int) -> Int { if n
> 0 { return tco(n - 1, acc * n) } return acc }
CAN BE OPTIMIZED To something like this…
func factorial(n: Int) -> Int { var result = 1
var x = n while x > 0 { result = result * x x-- } return result }
DOES SWIFT DO THIS?
DOES SWIFT DO THIS? The internet says: *Sometimes*
None
Or… xcrun swiftc -S
AMD64 ABI Args placed in rdi, rsi, rdx, .. Return
value -> rax
func tco(n: Int, acc: Int) -> Int rdi → n
rsi → acc rax → return value
IS TCO HAPPENING? call → Bad - Creates new stack
frame jmp, jg, jle, j… → Good - Stays in current stack frame
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup.
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0 Return value
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0 Return value Restore caller stack frame
__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq
$0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq No TCO
OPTIMIZE
WALK: -Onone
KILL: -O
-Ounchecked
__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle
LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq
__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle
LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq
__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle
LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq
SOMETIMES Swift Does This.
AUTOMATIC REFERENCE COUNTING Imagine compile time garbage collection.
OLDEN TIMES Memory managed by hand.
@interface Something() { NSNumber *_numberIvar; } @end @implementation Something -
(void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end
@interface Something() { NSNumber *_numberIvar; } @end @implementation Something -
(void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end
WITH ARC
- (void)setNumber:(NSNumber *)number { _numberIvar = number; }
COMPILER MAGIC Inserts retain/release calls.
ARC in Swift Manages references.
TCO + ARC
class Integer: IntegerLiteralConvertible { let val: Int required init(integerLiteral value:
Int) { self.val = value } init(_ val: Int) { self.val = val } }
func +(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val
+ rhs.val) } func -(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val - rhs.val) } func *(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val * rhs.val) } func >(lhs: Integer, rhs: Integer) -> Bool { return lhs.val > rhs.val }
func factorialTC(n: Integer) -> Integer { return tc(n, 1) }
func tc(n: Integer, acc: Integer) -> Integer { if n > 0 { return tc(n - 1, acc * n) } return acc }
__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip),
%rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8:
__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip),
%rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8:
__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip),
%rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8: No TCO
__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip),
%rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8: No TCO Last Action Performed
Integer created on +,-,* ops
func +(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val
+ rhs.val) } func -(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val - rhs.val) } func *(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val * rhs.val) }
NEEDS RELEASING
func tc(n: Integer, acc: Integer) -> Integer { if n
> 0 { return tc(n - 1, acc * n) } return acc } SO THIS…
func tc(n: Integer, acc: Integer) -> Integer { let _n1
= n - 1 let _accn = acc * n if n > 0 { let tmp = tc(_n1, _accn) swift_release(_accn) swift_release(_n1) return tmp } return acc } IS REALLY SOMETHING LIKE
All Is sometimes Lost
FUNCTIONAL SWIFT Use value types.
VALUE SEMANTICS ≠ VALUE TYPES
SO.. BE CAREFULL
THANKS!
github.com/haveahennessy @haveahennessy
http://www.hopperapp.com/