스위프트 성능 이해하기

158
let swift( 스위프트 성능 이해하기 Value 타입, Protocol 과 스위프트의 성능 최적화 @inkyfox 유용하

Transcript of 스위프트 성능 이해하기

Page 1: 스위프트 성능 이해하기

let swift(16)

스위프트�성능�이해하기Value�타입,�Protocol과�스위프트의�성능�최적화

@inkyfox유용하

Page 2: 스위프트 성능 이해하기

내용

Value�Semantics�

성능을�위해�고려할�것들�

스위프트의�추상화�기법들과�성능�

Page 3: 스위프트 성능 이해하기

let swift(16)

Value�Semantics

Page 4: 스위프트 성능 이해하기

Value�Semantics

Value�Type�Semantics�/�Copy-by-Value�Semantics�

Identity가�아닌�Value(값)에만�의미를�둔다�

• Int,�Double�등의�기본�타입들�

포인터만�복사되는�참조(Reference)�시맨틱스와�비교됨�

• Objective-C,�Java�등�

스위프트엔�Objc에�없던�새로운�Value�Type을�도입�

• struct,�enum,�tuple

Page 5: 스위프트 성능 이해하기

Value�Type의�특징

변수�할당�시�Stack에�값�전체가�저장됨�

다른�변수에�할당될�때�전체�값이�복사됨�(copy�by�value)�

• 변수들이�분리됨:�하나를�변경해도�다른�것에�영향�없음�

Heap을�안�쓰며�따라서�Reference�Counting도�필요�없음

Page 6: 스위프트 성능 이해하기

class�vs�struct

class Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Page 7: 스위프트 성능 이해하기

class�vs�struct

class Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Stackc1: ref

Heap…refCount: 1x: 0.0y: 0.0

Page 8: 스위프트 성능 이해하기

class�vs�struct

class Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Stackc1: refc2: ref

Heap…refCount: 2x: 0.0y: 0.0

Page 9: 스위프트 성능 이해하기

class�vs�struct

class Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

// c1.y == 2.0 // c2.y == 2.0

Stackc1: refc2: ref

Heap…refCount: 2x: 0.0y: 2.0

Reference�타입은�하나의�Identity�변수가�Copy되어도�값이�하나를�향해�같은�값을�가진다

Page 10: 스위프트 성능 이해하기

class�vs�struct

struct Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Page 11: 스위프트 성능 이해하기

class�vs�struct

struct Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Stackc1: x: 0.0

y: 0.0

Page 12: 스위프트 성능 이해하기

class�vs�struct

struct Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

Stackc1: x: 0.0

y: 0.0c2: x: 0.0

y: 0.0

Page 13: 스위프트 성능 이해하기

class�vs�struct

struct Point { var x: CGFloat var y: CGFloat }

var c1 = Point(x: 0.0, y: 0.0)

var c2 = c1

c2.y = 2.0

// c1.y == 0.0 // c2.y == 2.0

Stackc1: x: 0.0

y: 0.0c2: x: 0.0

y: 2.0

Value�타입의�각자의�변수는�Copy되어도�분리되어있다

Page 14: 스위프트 성능 이해하기

Value�Semantics:�’값’에�의해�구분됨

Value�semantics에서는�Identity가�아니라�Value가�중요하다.�

각�변수는�값(Value)에�의해�구분이�되어야한다.�

따라서�동치�관계여야�한다.�

->�간단합니다,�Equatable을�구현하세요�

(단순히�데이터를�전달할�목적인�struct�변수를�말하는�것이�아님)

Page 15: 스위프트 성능 이해하기

Equatable�간단�구현

protocol Equatable { func ==(lhs: Self, rhs: Self) -> Bool }

Page 16: 스위프트 성능 이해하기

Equatable�간단�구현

protocol Equatable { func ==(lhs: Self, rhs: Self) -> Bool }

extension CGPoint: Equatable { }

func ==(lhs: CGPoint, rhs: CGPoint) -> Bool { return lhs.x == rhs.x && lhs.y && rhs.y }

Page 17: 스위프트 성능 이해하기

Value�Type과�Thread

var numbers = [1, 2, 3, 4, 5, 6, 7, 8]

scheduler.processNumbersAsync(numbers)

for i in 0..<numbers.count { numbers[i] = numbers[i] + 1 }

scheduler.processNumbersAsync(numbers)

Copy�value

Copy�value

Thread간�의도하지�않은�공유로부터�안전함!

Page 18: 스위프트 성능 이해하기

그래도..�값�모두를�Copy하는데��성능�괜찮을까?

Page 19: 스위프트 성능 이해하기

Copy는�빠르다

기본�타입들,�enum,�tuple,�struct�

• 정해진�시간�(constant�time)�안에�끝남��

내부�데이터가�Heap과�혼용하는�struct의�경우�

• 정해진�시간�+�레퍼런스�copy등의�시간�

• String,�Array,�Set,�Dictionary�등�

• 쓰기�시�Copy-on-write로�속도�저하�보완�

Page 20: 스위프트 성능 이해하기

Immutable로�해도�되는것�아닌가?

Page 21: 스위프트 성능 이해하기

Immutable

참조형이어도�값이�불변하면�Thread간에�문제생길�일이�없음�

함수형�패러다임과�같이��널리�전파됨�

Immutable은�cocoa에서도�꽤�써�왔다�

• NSArray *array = [array arrayByAddingObject: component];

• NSURL *url = [url URLByAppendingPathComponent: component];

Page 22: 스위프트 성능 이해하기

정말�Immutable이�언제나�답일까?

Page 23: 스위프트 성능 이해하기

Mutable이�효율적인�경우

func makeURL(subDirectories: [String]) -> NSURL? { var array: NSArray = [NSHomeDirectory()] for dir in subDirectories { array = array.arrayByAddingObject(dir) } return NSURL.fileURLWithPathComponents(array as! [String]) }

계속�새로�개체를�생성하여�할당하고�

String을�copy함

Objc에서�많이�쓰던�Immutable�방식

비효율적이다

Page 24: 스위프트 성능 이해하기

Mutable이�효율적인�경우

func makeURL(subDirectories: [String]) -> NSURL? { var array: NSMutableArray = [NSHomeDirectory()] for dir in subDirectories { array.addObject(dir) } return NSURL.fileURLWithPathComponents(array as! [String]) }

Mutable로�바꾸자

Page 25: 스위프트 성능 이해하기

Mutable이�효율적인�경우

func makeURL(subDirectories: [String]) -> NSURL? { var array: [String] = [NSHomeDirectory()] for dir in subDirectories { array.append(dir) } return NSURL.fileURLWithPathComponents(array) }

Swift의�Array를�쓰면

Page 26: 스위프트 성능 이해하기

API가�이상해지는�경우도

// 속도가 변경되었다

// Mutable + Value Type car.dashboard.speed = 99

// Immutable + Reference Type car.dashboard = Dashboard(speed: 99, rpm: car.dashboard.rpm)

Heap과�Reference�Counting�

또�컴파일러�최적화가�어려움

Page 27: 스위프트 성능 이해하기

API가�이상해지는�경우도

// 속도가 변경되었다

// Mutable + Value Type car.dashboard.speed = 99

// Immutable + Reference Type car.dashboard = Dashboard(speed: 99, rpm: car.dashboard.rpm)

그것도�그렇지만,�

Dashboard를�바꾼�다는�의미인건가?

Page 28: 스위프트 성능 이해하기

그래도�class도�중요한�경우

Value보단�Identity가�중요한�경우�

• UIView�같이�모든�변수에서�단�하나의�state를�갖는�개체�

OOP�모델�

• 여전히�상속은�아주�훌륭한�도구�

Objective-C�연동�

Indirect�storage�(특수한�경우�struct내의�간접�저장소�역할)�

• 뒤에서�설명

Page 29: 스위프트 성능 이해하기

let swift(16)

성능을�위해�고려할�것들

Page 30: 스위프트 성능 이해하기

성능에�영향을�미치는�3가지

Memory�Allocation:�Stack�or�Heap�

Reference�Counting:�No�or�Yes�

Method�dispatch:�Static�or�Dynamic

Page 31: 스위프트 성능 이해하기

Heap�할당의�문제

할당시에�빈�곳을�찾고�관리하는�것은�복잡한�과정�

Heap

Page 32: 스위프트 성능 이해하기

Heap�할당의�문제

할당시에�빈�곳을�찾고�관리하는�것은�복잡한�과정�

무엇보다�그�과정이�thread�safe해야한다는�점이�가장�큰�문제�

• ��lock�등의�synchronization�동작은�큰�성능�저하�요소�

반면�Stack�할당은�

• 단순히�스택포인터�변수값만�바꿔주는�정도

Page 33: 스위프트 성능 이해하기

enum Color { case red, green, blue } enum Theme { case eat, stay, play}

var cache = [String: UIImage]()

func makeMapMarker(color: Color, theme: Theme, selected: Bool) -> UIImage { let key = "\(color):\(theme):\(selected)" if let image = cache[key] { return image }

… }

Heap�할당�줄이기

매번�Heap�할당

매우�빈번히�호출된다면�성능에�영향을�미칠�수�있다�

(예를�들면�매우�큰�Loop안에서�일어나는�경우)�

->�Key를�Value�type으로�바꿔보자!

Page 34: 스위프트 성능 이해하기

Heap�할당�줄이기

struct Attribute { var color: Color var theme: Theme var selected: Bool }

새로운�Key�타입�정의

Page 35: 스위프트 성능 이해하기

Heap�할당�줄이기

struct Attribute: Hashable { var color: Color var theme: Theme var selected: Bool }

func ==(lhs: Attribute, rhs: Attribute) -> Bool { return lhs.color == rhs.color && lhs.theme == rhs.theme && lhs.selected == lhs.selected }

extension Attribute { var hashValue: Int { return [color.hashValue, theme.hashValue, selected.hashValue].hashValue } }

Dictionary의�Key가�되려면

Page 36: 스위프트 성능 이해하기

enum Color { case red, green, blue } enum Theme { case eat, stay, play}

var cache = [String: UIImage]()

func makeMapMarker(color: Color, theme: Theme, selected: Bool) -> UIImage { let key = "\(color):\(theme):\(selected)" if let image = cache[key] { return image }

… }

Heap�할당�줄이기

struct Attribute: Hashable { var color: Color var theme: Theme var selected: Bool }

Page 37: 스위프트 성능 이해하기

enum Color { case red, green, blue } enum Theme { case eat, stay, play}

var cache = [Attribute: UIImage]()

func makeMapMarker(color: Color, theme: Theme, selected: Bool) -> UIImage { let key = Attribute(color: color, theme: theme, selected: selected) if let image = cache[key] { return image }

… }

Heap�할당�줄이기

struct Attribute: Hashable { var color: Color var theme: Theme var selected: Bool }

Value�Type�

Stack에서만�메모리�할당�

Heap�할당�오버헤드�없음

Page 38: 스위프트 성능 이해하기

Reference�Counting의�문제

정말�자주�실행된다�

• 변수�Copy할�때�마다�

그러나�이것도�역시�가장�큰�문제는�thread�safety�때문�

• 카운트를�Atomic하게�늘리고�줄여야함

Page 39: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) {

}

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0

foo(c0) c1 = nil

}

Reference�Counting의�동작

Page 40: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) {

}

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0

foo(c0) c1 = nil

}

Reference�Counting의�동작

MyClass

Ref Count: 1

Heapc0

Page 41: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) {

}

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil

}

Reference�Counting의�동작

MyClass

Ref Count: 2

c0

c1

Heap

Page 42: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) …

}

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil

}

Reference�Counting의�동작

MyClass

Ref Count: 3

c0

c1

Heap

c

Page 43: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) … release(c) }

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil

}

Reference�Counting의�동작

MyClass

Ref Count: 2

c0

c1

Heap

c

Page 44: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) … release(c) }

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil release(c1)

}

Reference�Counting의�동작

MyClass

Ref Count: 1

c0

c1

Heap

Page 45: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) … release(c) }

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil release(c1) release(c0) }

Reference�Counting의�동작

MyClass

Ref Count: 0

c0

Heap

Page 46: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) … release(c) }

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

foo(c0) c1 = nil release(c1) release(c0) }

Reference�Counting의�동작

이것이�ARC�

Automatic�Reference�Counting�

손으로�다�넣던�시절이�있었습니다…�

Page 47: 스위프트 성능 이해하기

class MyClass { }

func foo(c: MyClass) { retain(c) … release(c) }

do { let c0: MyClass = MyClass()

var c1: MyClass? = c0 retain(c1)

for _ in 1...100_000 { foo(c0) } c1 = nil release(c1) release(c0) }

Reference�Counting의�동작

Loop는�프로그래밍의�기본�

Ref�Count�매우�빈번한�것

Page 48: 스위프트 성능 이해하기

Method�Dispatch�(Static)

컴파일�시점에�메소드의�실제�코드�위치를�안다면�

실행중�찾는�과정�없이�바로�해당�코드�주소로�점프할�수�있음�

컴파일러의�최적화,�메소드�인라이닝�(Inlining)�가능

Page 49: 스위프트 성능 이해하기

메소드�인라이닝

컴파일�시점에�메소드��호출�부분에�메소드�내용을�붙여넣음�

• 효과가�있다고�판단되는�경우에만�

Call�stack�오버헤드�줄임�

• CPU�icache나�레지스터를�효율적으로�쓸�가능성�

컴파일러의�추가�최적화�가능�

• 최근�메소드들이�작으므로�더더욱�기회가�많음�

• 루프�안에서�불리는�경우�큰�효과

Page 50: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

drawAPoint(point)

메소드�인라이닝

Page 51: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

drawAPoint(point)

메소드�인라이닝

인라이닝�(1)

Page 52: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

point.draw()

메소드�인라이닝

인라이닝�(1)

Page 53: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

point.draw()

메소드�인라이닝

인라이닝�(2)

Page 54: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

// Point.draw implementation

메소드�인라이닝

인라이닝�(2)

Page 55: 스위프트 성능 이해하기

struct Point { var x, y: CGFloat func draw() { // Point.draw implementation } }

func drawAPoint(param: Point) { param.draw() }

let point = Point(x: 0, y: 0)

// Point.draw implementation

메소드�인라이닝

인라이닝�(2)

2단계의�호출이�줄었다�

두�코드가�붙어�추가적인�최적화의�기회도�생겼다

Page 56: 스위프트 성능 이해하기

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

Page 57: 스위프트 성능 이해하기

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

d: Drawable

d.draw()

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

Drawable.draw?�Point.draw?�Line.draw?�

어떻게�알지?

Page 58: 스위프트 성능 이해하기

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

Line : Drawable

d: Drawable

d.draw()

class의�실제�type을�얻고

Line.Type

Page 59: 스위프트 성능 이해하기

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

Line : Drawable

d: Drawable

d.draw()

그�class�type에�속한�V-Table을�찾아서

Line.Type

V-Table

draw:

Page 60: 스위프트 성능 이해하기

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

Line : Drawable

override func draw() { ... }

d: Drawable

d.draw()

Line.Type

V-Table

draw:

실제�draw의�코드�주소를�알아내어

Page 61: 스위프트 성능 이해하기

Method�Dispatch�(Dynamic)Reference�semantics에서의�다형성�(Polymorphism)��

class Drawable { func draw() {} }

class Point : Drawable { var x, y: CGFloat override func draw() { ... } }

class Line : Drawable { var x1, y1, x2, y2: CGFloat override func draw() { ... } }

func draw(d: Drawable, withColor color: UIColor) { color.setFill() d.draw() }

Line : Drawable

override func draw() { ... }

d: Drawable

d.draw()

Line.Type

V-Table

draw:

호출한다

Page 62: 스위프트 성능 이해하기

Dynamic�Method�Dispatch의�문제

요점은,�실제�Type을�컴파일�시점에�알�수가�없다는�것�

때문에,�코드�주소를�runtime에�찾아야�한다�

Static에�비해�단지�이것이�문제.�Thread�saftety문제도�없다�

하지만�이로�인해�컴파일러가�최적화를�못하는�것이�큰�문제

Page 63: 스위프트 성능 이해하기

Objective-C

Objective-C의�method�dispatch는�Message�sending�방식�

[anObject doMethod:aParameter];

아래처럼�동적으로�메소드를�Lookup하여�호출된다.�

objc_msgSend(anObject, @selector(doMethod:), aParameter);

강력하고�유연한�특징을�가지고�있지만�성능�저하�요소�

특히�Loop안에서�빈번하게�Method�호출이�일어나는�경우

Page 64: 스위프트 성능 이해하기

Static�Dispatch로�강제하기

final,�private�등을�쓰는�버릇�

• 해당�메소드,�프로퍼티등은�상속�안�되므로�static하게�처리�

dynamic�키워드�최소화�

Objc�연동�최소화�

• Objective-C�Runtime을�통하게�됨�

WMO�(whole�module�optimization)

Page 65: 스위프트 성능 이해하기

Whole�Module�Optimization

빌드시에�모든�파일을�한번에�분석하여,�

static�dispatch로�변환��가능한지�등을�판단하여�최적화�

Page 66: 스위프트 성능 이해하기

Whole�Module�Optimization

빌드시에�모든�파일을�한번에�분석하여,�

static�dispatch로�변환��가능한지�등을�판단하여�최적화�

Page 67: 스위프트 성능 이해하기

Whole�Module�Optimization

빌드시에�모든�파일을�한번에�분석하여,�

static�dispatch로�변환��가능한지�등을�판단하여�최적화��

겁나�느려짐�주의�(Xcode7)�

디버그�빌드에�적용하는�것은�정신�건강에�좋지�않습니다�

아직�안정화가�안�됨�주의�(Xcode7)��너무�믿진�마세요…

Page 68: 스위프트 성능 이해하기

정리:�성능에�영향을�미치는�3가지

Memory�Allocation:�Stack�or�Heap�

Reference�Counting:�No�or�Yes�

Method�Dispatch:�Static�or�Dynamic

Page 69: 스위프트 성능 이해하기

let swift(16)

스위프트의�추상화�기법들과�성능

Page 70: 스위프트 성능 이해하기

추상화�기법들

Class�

Struct�

Protocol�Type�

Generics�Type�

각각�앞서�소개한�성능�요소들에�대해�어떤�특징을�가지는가

Page 71: 스위프트 성능 이해하기

class

class Point { var x: CGFloat var y: CGFloat }

let c1 = Point(x: 0.0, y: 0.0)

let c2 = c1

Page 72: 스위프트 성능 이해하기

class

class Point { var x: CGFloat var y: CGFloat }

let c1 = Point(x: 0.0, y: 0.0)

let c2 = c1 retain(c2)

release(c1) release(c2)

Stackc1:c2:

Heap…refCount: 2x: 0.0y: 0.0

Heap,�Reference�Counting�사용

Page 73: 스위프트 성능 이해하기

class

Memory�Allocation:�Heap�

Reference�Counting:�Yes�

Method�Dispatch:�Dynamic�(V-Table)�

• 성능�상관�없이�레퍼런스�시맨틱스가�필요하다면�써야함�

• Identity,�상속,�…�

• 단�레퍼런스의�의도하치�공유로�인한�문제�조심

Page 74: 스위프트 성능 이해하기

final�class

Memory�Allocation:�Heap�

Reference�Counting:�Yes�

Method�Dispatch:�Static

Page 75: 스위프트 성능 이해하기

참조�타입이�없는�struct

struct Point { var x: CGFloat var y: CGFloat }

let c1 = Point(x: 0.0, y: 0.0)

let c2 = c1

Page 76: 스위프트 성능 이해하기

참조�타입이�없는�struct

struct Point { var x: CGFloat var y: CGFloat }

let c1 = Point(x: 0.0, y: 0.0)

let c2 = c1

Stackc1: x: 0.0

y: 0.0

Page 77: 스위프트 성능 이해하기

참조�타입이�없는�struct

struct Point { var x: CGFloat var y: CGFloat }

let c1 = Point(x: 0.0, y: 0.0)

let c2 = c1

Stackc1: x: 0.0

y: 0.0c2: x: 0.0

y: 0.0

Page 78: 스위프트 성능 이해하기

참조�타입이�없는�struct

Memory�Allocation:�Stack�

Reference�Counting:�No�

Method�Dispatch:�Static

Page 79: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

Page 80: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

class�type

Page 81: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

class�type

value�type�…?

Page 82: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

class�type

value�type�안에�class�있음

String은�Value�semantics이지만,�

내부�storage로�class�타입을�가지고�있음�

• Copy시�해당�프로퍼티에�reference�counting이�동작한다�

• (Array,�Dictionary�등도�마찬가지)

Page 83: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

Page 84: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1

Stackc1: text:

… _storagefont:

Heap…refCount: 1…

…refCount: 1…

Page 85: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1 retain(c2.text._storage) retain(c2.font) …

Stackc1: text:

… _storagefont:

c2: text: … _storage

font:

Heap…refCount: 2…

…refCount: 2…

Page 86: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1 retain(c2.text._storage) retain(c2.font) … release(c1.text._storage) release(c1) release(c2.text._storage) release(c2)

Stack

c1:text: … _storagefont:

c2:text: … _storage

font:

Heap…refCount: 0…

…refCount: 0…

Page 87: 스위프트 성능 이해하기

참조�타입을�가진�struct

struct Label { var text: String var font: UIFont }

let c1 = Label(text: “msg”, font: font)

let c2 = c1 retain(c2.text._storage) retain(c2.font) … release(c1.text._storage) release(c1) release(c2.text._storage) release(c2)

Reference�Counting이�한번�Copy할때마다�2번씩�일어난다!!�

struct안에�참조�타입의�property�수만큼�많아진다.

Page 88: 스위프트 성능 이해하기

참조�타입을�가진�struct

Memory�Allocation:�Stack�

Reference�Counting:�Yes�

Method�Dispatch:�Static

Page 89: 스위프트 성능 이해하기

참조�타입이�많은�struct

Memory�Allocation:�Stack�

Reference�Counting:�MANY!�

Method�Dispatch:�Static

struct HTTPRequest { var protocol: String var domain: String var path: String var filename: String var extension: String var query: [String: String] var httpMethod: String var httpVersion: String }

Page 90: 스위프트 성능 이해하기

struct내�참조�타입을�줄여보자

struct HTTPRequest { var protocol: String // (1) var domain: String // (2) var path: String // (3) var filename: String // (4) var extension: String // (5) var query: [String: String] // (6) var httpMethod: String // (7) var httpVersion: String // (8) var httpHost: String // (9) }

9개의�참조�타입�

->�Copy할�때마다�9번의�Reference�Counting

Page 91: 스위프트 성능 이해하기

struct내�참조�타입을�줄여보자

struct HTTPRequest { var protocol: String // (1) var domain: String // (2) var path: String // (3) var filename: String // (4) var extension: String // (5) var query: [String: String] // (6) var httpMethod: String // (7) var httpVersion: String // (8) var httpHost: String // (9) }

9개의�참조�타입�

->�Copy할�때마다�9번의�Reference�Counting

enum HTTPMethod { case Get, Post, Put, Delete }

enum HTTPVersion { case _1_0, _1_1 }

struct HTTPRequest { var url: NSURL // (1) var httpMethod: HTTPMethod var httpVersion: HTTPVersion var httpHost: String // (2) }

2개로�줄임!

값의�제한이�가능하면�enum�등의�Value�type으로�변경하기�

다수의�class들을�하나의�class로�몰아�넣기

Page 92: 스위프트 성능 이해하기

Protocol�Type

코드�없이�API만�정의함�

상속�없는�다형성�(Polymorphism)�구현이�가능��

Objective�C의�protocol,�Java의�Interface�매우�유사함�

Value�type인�struct에도�적용이�가능하다�

• Value��semantics에서의�다형성

Page 93: 스위프트 성능 이해하기

Protocol을�이용한�Value�Type�다형성

protocol Drawable { func draw() } struct Point : Drawable { var x, y: CGFloat func draw() { ... } } struct Line : Drawable { var x1, y1, x2, y2: CGFloat func draw() { ... } } var drawables: [Drawable] … for d in drawables { d.draw() }

추상�메소드�정의

추상 메소드 정의메소드�구현

변수를�Protocol�type으로

실제�메소드�호출

Page 94: 스위프트 성능 이해하기

의문점:�변수�할당

class라면�주소값이니�모두�같은�사이즈지만,�

struct인�Point와�Line은�사이즈가�다르다.�

어떻게�Drawable에�메모리를�미리�할당해�놓고�값을�저장할까?

struct Point : Drawable { var x, y: CGFloat … } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

Page 95: 스위프트 성능 이해하기

의문점:�Method�Dispatch

class의�다형성�구조에선�V-Table을�통해서�찾았다.�

상속이�아닌�Protocol의�다형성�구조에선�V-Table이�없다�

어떻게�Point.draw와�Line.draw를�구분해서�호출할까?

var drawables: [Drawable] … for d in drawables { d.draw() }

Page 96: 스위프트 성능 이해하기

Protocol�type의�변수�할당

protocol Drawable { func draw() } struct Point : Drawable { var x, y: CGFloat func draw() { ... } } struct Line : Drawable { var x1, y1, x2, y2: CGFloat func draw() { ... } } var drawables: [Drawable] … for d in drawables { d.draw() }

[Drawable] _storage…

Heap

refCount ? ? ?

Page 97: 스위프트 성능 이해하기

[Drawable] _storage…

Protocol�type의�변수�할당

protocol Drawable { func draw() } struct Point : Drawable { var x, y: CGFloat func draw() { ... } } struct Line : Drawable { var x1, y1, x2, y2: CGFloat func draw() { ... } } var drawables: [Drawable] … for d in drawables { d.draw() }

Pointx: 0.0y: 0.0

Linex1: 0.0y1: 0.0x2: 1.0y2: 1.0

refCount ? ? ?

Page 98: 스위프트 성능 이해하기

Protocol�type의�변수�할당

protocol Drawable { func draw() } struct Point : Drawable { var x, y: CGFloat func draw() { ... } } struct Line : Drawable { var x1, y1, x2, y2: CGFloat func draw() { ... } } var drawables: [Drawable] … for d in drawables { d.draw() }

모두�같은�사이즈

다른�사이즈

어떻게�넣을까?

refCount ? ? ?

[Drawable] _storage…

Pointx: 0.0y: 0.0

Linex1: 0.0y1: 0.0x2: 1.0y2: 1.0

Page 99: 스위프트 성능 이해하기

Existential�Container

Value�Buffer�

(3�words)

Protocol�type의�실제�값을�넣고�관리하는�구조

(1�word는�32bit�CPU에서는�32bit,�64bit�CPU에서는�64bit)

(Fixed�size)

Page 100: 스위프트 성능 이해하기

Existential�Container

Drawable Existence x: 0.0y: 0.0

struct가�3�words�이하인�경우

Pointx: 0.0y: 0.0

Existential�container�안에�값�모두�저장됨

Existential�Container

Page 101: 스위프트 성능 이해하기

Drawableref

Linex1: 0.0y1: 0.0x2: 1.0y2: 1.0

Existential�Container

struct가�3�words보다�큰�경우

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

HeapExistential�Container

Heap�할당하여�값�저장�

Existential�container에�해당�레퍼런스�저장

Page 102: 스위프트 성능 이해하기

어떻게�3�word를�구분해�할당하고�복사하는가?�

Page 103: 스위프트 성능 이해하기

Value�Witness�Table�(VWT)

VWTallocate:copy:destruct:deallocate:

Existential�container의�생성/해제를�담당하는�인터페이스

Page 104: 스위프트 성능 이해하기

Value�Witness�Table�(VWT)

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Protocol을�구현하는�type마다�있다

Page 105: 스위프트 성능 이해하기

Drawable

Drawable

Value�Witness�Table�(VWT)

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

실제�변수�영역

Existential�Container

Page 106: 스위프트 성능 이해하기

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Drawable

Drawableref:

Value�Witness�Table�(VWT)

Heap

Existential�Container

Page 107: 스위프트 성능 이해하기

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Drawablex: 0.0y: 0.0

Drawableref:

Value�Witness�Table�(VWT)

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

Heap

Existential�Container

Page 108: 스위프트 성능 이해하기

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Drawable

Drawableref:

Value�Witness�Table�(VWT)

Heap

Existential�Container

Page 109: 스위프트 성능 이해하기

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Drawable

Drawable

Value�Witness�Table�(VWT)

Existential�Container

Page 110: 스위프트 성능 이해하기

Drawablex: 0.0y: 0.0

vwt:

Drawableref:

vwt:

Value�Witness�Table�(VWT)

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

Heap

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Existential�Container

Page 111: 스위프트 성능 이해하기

Drawablex: 0.0y: 0.0

vwt:

Drawableref:

vwt:

Method�Dispatch는?

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

Heap

Protocol�Witness�Table

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Point Drawabledraw:…

Line Drawabledraw:…

Existential�Container

Page 112: 스위프트 성능 이해하기

Point Drawabledraw:…

Line Drawabledraw:…

Drawablex: 0.0y: 0.0

vwt:

Drawableref:

vwt:

Method�Dispatch는?

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

Heap

Line VWTallocate:copy:destruct:deallocate:

Point VWTallocate:copy:destruct:deallocate:

Existential�ContainerProtocol�Witness�Table

Page 113: 스위프트 성능 이해하기

Drawablex: 0.0y: 0.0

vwt: pwt:

Drawableref:

vwt: pwt:

Method�Dispatch는?

Line VWTallocate:copy:destruct:deallocate:

x1: 0.0y1: 0.0x2: 1.0y2: 1.0

Heap

Point VWTallocate:copy:destruct:deallocate:

Point Drawabledraw:…

Line Drawabledraw:…Dynamic Method Dispatch

Existential�ContainerProtocol�Witness�Table

Page 114: 스위프트 성능 이해하기

Copy�동작�정리

Value�타입이므로�값�전체가�Copy된다.�

3�words�이하의�경우�

• 단순히�새로운�Existential�container에�전체가�복사됨�

3�words를�넘는�경우�

• 새로운�Existential�container�생성�

• 값�전체가�새로운�Heap할당�후�복사됨

Page 115: 스위프트 성능 이해하기

큰�사이즈�protocol�타입의�copy

protocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Page 116: 스위프트 성능 이해하기

Drawableline: ref:

vwt: pwt:

큰�사이즈�protocol�타입의�copy

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Heapprotocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Page 117: 스위프트 성능 이해하기

Drawableline: ref:

vwt: pwt:

큰�사이즈�protocol�타입의�copy

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Heapprotocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0 Drawablecopy: ref:

vwt: pwt:

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

copy

Page 118: 스위프트 성능 이해하기

Drawableline: ref:

vwt: pwt:

큰�사이즈�protocol�타입의�copy

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Heapprotocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0 Drawablecopy: ref:

vwt: pwt:

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

copy

Heap의�데이터도�복사가된다!�

Page 119: 스위프트 성능 이해하기

Drawableline: ref:

vwt: pwt:

큰�사이즈�protocol�타입의�copy

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Heapprotocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0 Drawablecopy: ref:

vwt: pwt:

x1: 0.0y1: 0.0x2: 1.0y2: 0.0

copy

Heap의�데이터도�복사가된다!�

나름�Value�type이니까!�

Heap은�쓰지만�Reference�counting이�없다

Page 120: 스위프트 성능 이해하기

Drawableline: ref:

vwt: pwt:

큰�사이즈�protocol�타입의�copy

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Heapprotocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0 Drawablecopy: ref:

vwt: pwt:

copy

Copy마다�새로운�Heap�할당하는데�이것이��큰�성능�저하�요소!

x1: 0.0y1: 0.0x2: 1.0y2: 0.0

Page 121: 스위프트 성능 이해하기

개선해�봅시다

protocol Drawable { func draw() } struct Line : Drawable { var x1, y1, x2, y2: CGFloat … }

var line: Drawable = Line() var copy: Drawable = line

//copy.x2 = 1.0

Page 122: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

//copy.x2 = 1.0

class�타입의�간접�저장소로�이동

Page 123: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

//copy.x2 = 1.0

Drawableline: _storage:

vwt: pwt:

Heap

…refCount: 1x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Page 124: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

//copy.x2 = 1.0

Drawableline: _storage:

vwt: pwt:

Heap

Drawablecopy: _storage:

vwt: pwt:

…refCount: 2x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Page 125: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

//copy.x2 = 1.0

Heap

Heap할당이�더�싼�Reference�counting으로�바뀌었다

…refCount: 2x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 126: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Heap

…refCount: 2x1: 0.0y1: 0.0x2: 0.0y2: 0.0

하지만�값을�바꾼다면?

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 127: 스위프트 성능 이해하기

Indirect�Storage

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Heap

…refCount: 2x1: 0.0y1: 0.0x2: 1.0y2: 0.0

둘�다�바뀌어�버림!

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 128: 스위프트 성능 이해하기

Copy-on-Write

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … var x2: CGFloat { get { return _storage.x2 } set { if !isUniquelyReferencedNonObjC(&_storage) { _storage = LineStorage(_storage) } _storage.x2 = x2 } } … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Heap

…refCount: 2x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 129: 스위프트 성능 이해하기

Copy-on-Write

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … var x2: CGFloat { get { return _storage.x2 } set { if !isUniquelyReferencedNonObjC(&_storage) { _storage = LineStorage(_storage) } _storage.x2 = x2 } } … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Heap

…refCount: 1x1: 0.0y1: 0.0x2: 0.0y2: 0.0

…refCount: 1x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 130: 스위프트 성능 이해하기

Copy-on-Write

protocol Drawable { func draw() }

class LineStorage { var x1, y1, x2, y2: CGFloat … }

struct Line : Drawable { private var _storage: LineStorage … var x2: CGFloat { get { return _storage.x2 } set { if !isUniquelyReferencedNonObjC(&_storage) { _storage = LineStorage(_storage) } _storage.x2 = x2 } } … }

var line: Drawable = Line() var copy: Drawable = line

copy.x2 = 1.0

Heap

…refCount: 1x1: 0.0y1: 0.0x2: 0.0y2: 0.0

…refCount: 1x1: 0.0y1: 0.0x2: 1.0y2: 0.0

Drawableline: _storage:

vwt: pwt:

Drawablecopy: _storage:

vwt: pwt:

Page 131: 스위프트 성능 이해하기

Existential�Container

변수가�Protocol�type으로�정의된�경우�쓰임�

프로토콜을�통한�다형성을�구현하기�위한�목적으로�쓰임�

내부�동작이�복잡하긴해도�성능이�class�쓰는것과�비슷하다�

• 둘�다�초기화�시�Heap�할당하여�사용�

• 둘�다�Dynamic�dispatch�(class도�V-Table,�protocol은�PWT)

Page 132: 스위프트 성능 이해하기

큰�사이즈�protocol�타입의�copy

Indirect�Storage�

• Copy시�Heap�할당�대신�Reference�counting으로�대체�

• class타입의�다형성�쓸때와�비슷한�수준�

Copy-on-Write�

• Indirect�storage를�값이�변경될�시점에�Heap�할당하여�복사�

• 성능�저하를�최소화�함�(변경�동작에서만)�

String,�Array,�Dictionary�등도�이런�개념으로�Value�semantics�구현

Page 133: 스위프트 성능 이해하기

작은�사이즈의�Protocol�Type

Memory�Allocation:�Stack�

Reference�Counting:�No�

Method�Dispatch:�Dynamic�(Protocol�Witness�Table)

Page 134: 스위프트 성능 이해하기

큰�사이즈의�Protocol�Type

Memory�Allocation:�MANY!�(Copy할�때마다�할당)�

Reference�Counting:�No�(class�프로퍼티가�있을�때만)�

Method�Dispatch:�Dynamic�(Protocol�Witness�Table)

Page 135: 스위프트 성능 이해하기

큰�사이즈의�Protocol�Type

Memory�Allocation:�Heap�

Reference�Counting:�Yes�

Method�Dispatch:�Dynamic�(Protocol�Witness�Table)

with�Indirect�Storage

Page 136: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

Page 137: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

Point VWTallocate:copy:destruct:deallocate:

Drawablelocal: x: 0.0

y: 0.0

vwt:pwt:

VWT�이용하여�값�복사

Page 138: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

Line VWTallocate:copy:destruct:deallocate:

Drawablelocal: ref

vwt:pwt:

VWT�이용하여��

메모리�할당,�값�복사

x1: 0.0y1: 0.0x2: 0.0y2: 0.0

Page 139: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

Drawablelocal: x: 0.0

y: 0.0

vwt:pwt:

Point Drawabledraw:…

Dynamic�Method�Dispatch

Page 140: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

Drawablelocal: x: 0.0

y: 0.0

vwt:pwt:

Point Drawabledraw:…

Dynamic�Method�Dispatch�

성능�개선할�수�있을까?

Page 141: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopy<T: Drawable>(local: T) { local.draw() } drawACopy(Point(…))

drawACopy(Line(…))

정적 다형성 (Static Polymorphism)

Method�내에서는�Drawable의�

실제�타입이�바뀌지�않는다

Page 142: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopyForPoint(local: Point) { d.draw() }

func drawACopyForLine(local: Line) { d.draw() }

drawACopyForPoint(Point(…))

drawACopyForLine(Line(…))

복잡한�Existential�Container�안�써도�됨�

함수�호출�시�Heap�할당을�아주�없앨�수�있음

실제�타입별로�만들어�준다면�

(Generics�특수화)

Page 143: 스위프트 성능 이해하기

Generics�Type

protocol Drawable { func draw() } func drawACopyForPoint(local: Point) { d.draw() }

func drawACopyForLine(local: Line) { d.draw() }

drawACopyForPoint(Point(…))

drawACopyForLine(Line(…))

Static�Method�Dispatch�가�되어�

컴파일러�최적화가�가능하게�되었다�(인라이닝�등)

실제�타입별로�만들어�준다면�

(Generics�특수화)

Page 144: 스위프트 성능 이해하기

이걸�손으로�하면,�Generics�쓰지�말란�말?

Page 145: 스위프트 성능 이해하기

Generic�특수화�(Specialization)

컴파일러가�해�줍니다.�

더�효과를�보려면�WMO�(Whole�Module�Optimization)�이용

아직�너무�믿진�마세요.�(Xcode�7)

Page 146: 스위프트 성능 이해하기

Generics�Type�정리

정적�다형성�(Static�Polymorphism)�

• 컴파일�시점에�부르는�곳마다�타입이�정해져�있음�

• 런타임에�바뀌지�않음�

• 특수화�(Specialization)가�가능�

Page 147: 스위프트 성능 이해하기

특수화�되지�않은�Generics��(작은�사이즈의�Protocol�Type)

Memory�Allocation:�Stack�

Reference�Counting:�No�

Method�Dispatch:�Dynamic�(Protocol�Witness�Table)

Page 148: 스위프트 성능 이해하기

특수화된�Generics�Type�(struct)

Memory�Allocation:�Stack�

Reference�Counting:�No�

Method�Dispatch:�Static

Page 149: 스위프트 성능 이해하기

특수화�되지�않은�Generics��(큰�사이즈의�Protocol�Type)

Memory�Allocation:�MANY!�(Copy할�때마다�할당)�

Reference�Counting:�No�(class�프로퍼티가�있을�때만)�

Method�Dispatch:�Dynamic�(Protocol�Witness�Table)

Page 150: 스위프트 성능 이해하기

특수화된�Generics�Type�(class)

Memory�Allocation:�Heap�

Reference�Counting:�Yes�

Method�Dispatch:�Dynamic�(V-Table)

Page 151: 스위프트 성능 이해하기

let swift(16)

정리

Page 152: 스위프트 성능 이해하기

스위프트의�성능

Objective-C에�비해�큰�향상이�있었으나�

Value�타입과�Protocol�타입�등의�성격을�고려해야�함�

성능�최적화를�고려해야하는�경우의�예�

• 렌더링�관련�로직�등�반복적으로�매우�빈번히�불리는�경우��

• 서버�환경에서의�대용량�데이터�처리

Page 153: 스위프트 성능 이해하기

추상화�기법의�선택

Struct:�엔티티�등�Value�시맨틱이�맞는�부분�

Class:�Identity가�필요한�부분,�상속등의�OOP,�Objective-C�

Generics:�정적�다형성으로�가능한�경우�

Protocol:�동적�다형성이�필요한�경우�

Page 154: 스위프트 성능 이해하기

고려할�수�있는�성능�최적화�기법들

Struct에�클래스�타입의�Property가�많으면�

• enum,�struct등�Value�type으로�대체��

• Reference�counting�줄임�

Protocol�Type을�쓸�때�대상이�큰�struct면�

• Indirect�storage로�struct�구조�변경�

• Mutable해야하면�Copy-on-Write�구현

Page 155: 스위프트 성능 이해하기

고려할�수�있는�성능�최적화�기법들

Dynamic�method�dispatch를�static하게�

• final,�private의�생활화�

• dynamic�사용�최소화�

• Objc�연동�최소화�하기�

• 릴리즈�빌드에�WMO�옵션�적용�고려

Page 156: 스위프트 성능 이해하기

마지막으로

정답은�없습니다.�

잘�된�디자인을�해치면서까지,�

모든�경우에�반드시�적용�해야하는�것은�아닙니다.�

돌아가는�환경,�데이터의�특성과�다루는�양�등에�따라�다릅니다.�

하지만�배경을�알면�옳은�방향으로�향할�수가�있습니다.

Page 157: 스위프트 성능 이해하기

참고

WWDC 2016

• Session 416: Understanding Swift Performance

WWDC 2015

• Session 409: Optimizing Swift Performance

• Session 414: Building Better Apps with Value Types in Swift

Page 158: 스위프트 성능 이해하기

let swift(16)