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
600
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
GraphQL×Railsアプリのデータベース負荷分散 - 月間3,000万人利用サービスを無停止で
koxya
1
1.3k
組込みだけじゃない!TinyGo で始める無料クラウド開発入門
otakakot
0
240
スマホから Youtube Shortsを見られないようにする
lemolatoon
27
31k
なぜあの開発者はDevRelに伴走し続けるのか / Why Does That Developer Keep Running Alongside DevRel?
nrslib
3
400
そのpreloadは必要?見過ごされたpreloadが技術的負債として爆発した日
mugitti9
2
3.3k
(Extension DC 2025) Actor境界を越える技術
teamhimeh
1
250
Android16 Migration Stories ~Building a Pattern for Android OS upgrades~
reoandroider
0
110
uniqueパッケージの内部実装を支えるweak pointerの話
magavel
0
1k
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
330
Range on Rails ―「多重範囲型」という新たな選択肢が、複雑ロジックを劇的にシンプルにしたワケ
rizap_tech
0
120
バッチ処理を「状態の記録」から「事実の記録」へ
panda728
PRO
0
150
Goで実践するドメイン駆動開発 AIと歩み始めた新規プロダクト開発の現在地
imkaoru
4
830
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
95
14k
Building Better People: How to give real-time feedback that sticks.
wjessup
369
20k
Designing for humans not robots
tammielis
254
26k
How to Think Like a Performance Engineer
csswizardry
27
2k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
A designer walks into a library…
pauljervisheath
209
24k
Docker and Python
trallard
46
3.6k
GraphQLの誤解/rethinking-graphql
sonatard
73
11k
Agile that works and the tools we love
rasmusluckow
331
21k
Scaling GitHub
holman
463
140k
Writing Fast Ruby
sferik
629
62k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
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/