0% found this document useful (0 votes)
54 views272 pages

416 Understanding Swift Performance

The document discusses Swift performance, beginning with an overview of key performance dimensions like allocation, reference counting, and method dispatch. It then covers allocation in more detail, comparing structs which are allocated on the stack, and classes which are allocated on the heap. Struct properties are stored directly in instances, while class properties require storing a reference to the shared property storage on the heap.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
54 views272 pages

416 Understanding Swift Performance

The document discusses Swift performance, beginning with an overview of key performance dimensions like allocation, reference counting, and method dispatch. It then covers allocation in more detail, comparing structs which are allocated on the stack, and classes which are allocated on the heap. Struct properties are stored directly in instances, while class properties require storing a reference to the shared property storage on the heap.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 272

Developer Tools #WWDC16

Understanding Swift Performance


Session 416

Kyle Macomber Software Engineer


Arnold Schwaighofer Swift Performance Engineer

© 2016 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.
struct

class enum
struct

class enum

inheritance generics

protocols
Choosing the Right Abstraction Mechanism
Choosing the Right Abstraction Mechanism

Modeling
Choosing the Right Abstraction Mechanism

Modeling Performance
Choosing the Right Abstraction Mechanism
Modeling

Protocol and Value Oriented Programming in UIKit Apps Presidio Friday 4:00PM

Protocol-Oriented Programming in Swift WWDC 2015

Building Better Apps with Value Types in Swift WWDC 2015


Understand the implementation
to understand performance
Agenda
Agenda

Allocation
Reference counting
Method dispatch
Agenda

Allocation
Reference counting
Method dispatch
Protocol types
Generic code
Dimensions of Performance
Dimensions of Performance
Dimensions of Performance

Allocation
Stack Heap
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Allocation
Allocation
Stack
Allocation
Stack

Decrement stack pointer to allocate


Allocation
Stack

Decrement stack pointer to allocate


Increment stack pointer to deallocate
Allocation
Heap
Allocation
Heap

Advanced data structure


Allocation
Heap

Advanced data structure


Search for unused block of memory to allocate
Allocation
Heap

Advanced data structure


Search for unused block of memory to allocate
Reinsert block of memory to deallocate
Allocation
Heap

Advanced data structure


Search for unused block of memory to allocate
Reinsert block of memory to deallocate
Thread safety overhead
// Allocation
// Struct

struct Point {
var x, y: Double
func draw() { … }
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x:
struct Point {
y:
var x, y: Double
point2: x:
func draw() { … }
y:
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x:
func draw() { … }
y:
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 0.0
func draw() { … }
y: 0.0
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}

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


var point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation // Allocation
// Struct // Class

struct Point { class Point {


var x, y: Double var x, y: Double
func draw() { … } func draw() { … }
} }

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


var point2 = point1 let point2 = point1
point2.x = 5 point2.x = 5
// use `point1` // use `point1`
// use `point2` // use `point2`
// Allocation
// Class

class Point {
var x, y: Double
func draw() { … }
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
func draw() { … }
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
func draw() { … }
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 0.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 0.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Allocation
// Class Stack Heap

point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
Dimensions of Performance
Class

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Struct

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

var cache = [String : UIImage]()

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray }


enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

var cache = [String : UIImage]()

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray } struct Attributes {


enum Orientation { case left, right } var color: Color
enum Tail { case none, tail, bubble } var orientation: Orientation
var tail: Tail
var cache = [String : UIImage]() }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}

}
// Modeling Techniques: Allocation

enum Color { case blue, green, gray } struct Attributes : Hashable {


enum Orientation { case left, right } var color: Color
enum Tail { case none, tail, bubble } var orientation: Orientation
var tail: Tail
var cache = [Attributes : UIImage]() }

func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = Attributes(color: color, orientation: orientation, tail: tail)
if let image = cache[key] {
return image
}

}
Reference Counting
Reference Counting
Reference Counting

There’s more to reference counting than incrementing, decrementing


Reference Counting

There’s more to reference counting than incrementing, decrementing


• Indirection
Reference Counting

There’s more to reference counting than incrementing, decrementing


• Indirection
• Thread safety overhead
// Reference Counting
// Class

class Point {
var x, y: Double
func draw() { … }
}

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


let point2 = point1
point2.x = 5
// use `point1`
// use `point2`
// Reference Counting // Reference Counting
// Class // Class (generated code)

class Point { class Point {


var x, y: Double var refCount: Int
func draw() { … } var x, y: Double
} func draw() { … }
}

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


let point2 = point1 let point2 = point1
point2.x = 5 retain(point2)
// use `point1` point2.x = 5
// use `point2` // use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code)

class Point {
var refCount: Int
var x, y: Double
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack

point1:
class Point {
point2:
var refCount: Int
var x, y: Double
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2:
var refCount: Int
var x, y: Double
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Class (generated code) Stack Heap

point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}

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


let point2 = point1
retain(point2)
point2.x = 5
// use `point1`
release(point1)
// use `point2`
release(point2)
// Reference Counting
// Struct

struct Point {
var x, y: Double
func draw() { … }
}

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


let point2 = point1
// use `point1`
// use `point2`
// Reference Counting
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x:
func draw() { … }
y:
}

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


let point2 = point1
// use `point1`
// use `point2`
// Reference Counting
// Struct Stack

point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 0.0
func draw() { … }
y: 0.0
}

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


let point2 = point1
// use `point1`
// use `point2`
// Reference Counting
// Struct containing references

struct Label {
var text: String
var font: UIFont
func draw() { … }
}

let label1 = Label(text: "Hi", font: font)


let label2 = label1
// use `label1`
// use `label2`
// Reference Counting
// Struct containing references Stack

label1: text:

String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:

String
}

let label1 = Label(text: "Hi", font: font)


font:
let label2 = label1
// use `label1`
// use `label2`
// Reference Counting
// Struct containing references Stack

label1: text:

String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:

String
}

let label1 = Label(text: "Hi", font: font)


font:
let label2 = label1
// use `label1`
// use `label2`
// Reference Counting
// Struct containing references Stack
(generated code)
label1: text:

String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:

String
}

let label1 = Label(text: "Hi", font: font)


font:
let label2 = label1
retain(label2.text._storage)
retain(label2.font)
// use `label1`
release(label1.text._storage)
release(label1.font)
// use `label2`
release(label2.text._storage)
release(label2.font)
Dimensions of Performance
Class

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Struct

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Struct containing a reference

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Struct containing many references

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
// Modeling Techniques: Reference Counting

struct Attachment {
let fileURL: URL
let uuid: String
let mimeType: String

init?(fileURL: URL, uuid: String, mimeType: String) {


guard mimeType.isMimeType
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting

Stack
struct Attachment {
fileURL:
let fileURL: URL
uuid:

String
let uuid: String
let mimeType: String

String
mimeType:
init?(fileURL: URL, uuid: String, mimeType: String) {
guard mimeType.isMimeType
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting

Stack
struct Attachment {
fileURL:
let fileURL: URL
uuid:

String
let uuid: String
let mimeType: String

String
mimeType:
init?(fileURL: URL, uuid: String, mimeType: String) {
guard mimeType.isMimeType
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting
NEW
Stack
struct Attachment {
fileURL:
let fileURL: URL

UUID
uuid:
let uuid: UUID
let mimeType: String

String
mimeType:

init?(fileURL: URL, uuid: UUID, mimeType: String) {


guard mimeType.isMimeType
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}

What's New in Foundation for Swift Mission Tuesday 4:00PM


// Modeling Techniques: Reference Counting

Stack
struct Attachment {
fileURL:
let fileURL: URL

UUID
uuid:
let uuid: UUID
let mimeType: String

String
mimeType:

init?(fileURL: URL, uuid: UUID, mimeType: String) {


guard mimeType.isMimeType
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting

struct Attachment { extension String {


let fileURL: URL var isMimeType: Bool {
let uuid: UUID switch self {
let mimeType: String case "image/jpeg":
return true
init?(fileURL: URL, uuid: UUID, mimeType: String) { case "image/png":
guard mimeType.isMimeType return true
else { return nil } case "image/gif":
return true
self.fileURL = fileURL default:
self.uuid = uuid return false
self.mimeType = mimeType }
} }
} }
// Modeling Techniques: Reference Counting

struct Attachment { enum MimeType {


let fileURL: URL init?(rawValue: String) {
let uuid: UUID switch rawValue {
let mimeType: MimeType case "image/jpeg":
self = .jpeg
init?(fileURL: URL, uuid: UUID, mimeType: String) { case "image/png":
guard let mimeType = MimeType(rawValue: mimeType) self = .png
else { return nil } case "image/gif":
self = .gif
self.fileURL = fileURL default:
self.uuid = uuid return nil
self.mimeType = mimeType }
} }
} case jpeg, png, gif
}
// Modeling Techniques: Reference Counting

struct Attachment { enum MimeType : String {


let fileURL: URL case jpeg = "image/jpeg"
let uuid: UUID case png = "image/png"
let mimeType: MimeType case gif = "image/gif"
}
init?(fileURL: URL, uuid: UUID, mimeType: String) {
guard let mimeType = MimeType(rawValue: mimeType)
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting

Stack
struct Attachment {
fileURL:
let fileURL: URL

UUID
uuid:
let uuid: UUID
let mimeType: MimeType
mimeType: .png

init?(fileURL: URL, uuid: UUID, mimeType: String) {


guard let mimeType = MimeType(rawValue: mimeType)
else { return nil }

self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
Method Dispatch
Method Dispatch
Static
Method Dispatch
Static

Jump directly to implementation at run time


Method Dispatch
Static

Jump directly to implementation at run time


Candidate for inlining and other optimizations
Method Dispatch
Dynamic
Method Dispatch
Dynamic

Look up implementation in table at run time


Method Dispatch
Dynamic

Look up implementation in table at run time


Then jump to implementation
Method Dispatch
Dynamic

Look up implementation in table at run time


Then jump to implementation
Prevents inlining and other optimizations
// Method Dispatch
// Struct (inlining)

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


drawAPoint(point)
// Method Dispatch
// Struct (inlining)

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

func drawAPoint(_ param: Point) {


point.draw()
param.draw()
}

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


drawAPoint(point)
// Method Dispatch
// Struct (inlining)

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


param.draw()
point.draw()
// Method Dispatch
// Struct (inlining)

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


// Point.draw implementation
param.draw()
// Method Dispatch
// Struct (inlining) Stack

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


// Point.draw implementation
// Method Dispatch
// Struct (inlining) Stack

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


// Point.draw implementation
// Method Dispatch
// Struct (inlining) Stack

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


// Point.draw implementation
// Method Dispatch
// Struct (inlining) Stack

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

func drawAPoint(_ param: Point) {


param.draw()
}

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


// Point.draw implementation
Inheritance-Based Polymorphism
class Drawable { func draw() {} }

Drawable.Type
class Point : Drawable {
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
var x1, y1, x2, y2: Double Point.Type Line.Type …

override func draw() { … }


}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Inheritance-Based Polymorphism
class Drawable { func draw() {} }

Drawable.Type
class Point : Drawable {
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
var x1, y1, x2, y2: Double Point.Type Line.Type …

override func draw() { … }


}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Inheritance-Based Polymorphism
class Drawable { func draw() {} }

Drawable.Type
class Point : Drawable {
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
var x1, y1, x2, y2: Double Point.Type Line.Type …

override func draw() { … }


}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Polymorphism Through Reference Semantics
class Drawable { func draw() {} }
[Drawable]
class Point : Drawable {
1
refCount d[0] d[1]
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
var x1, y1, x2, y2: Double
override func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Polymorphism Through Reference Semantics
class Drawable { func draw() {} }
[Drawable]
class Point : Drawable {
refCount d[0] d[1]
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
var x1, y1, x2, y2: Double
override func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Polymorphism Through Reference Semantics
class Drawable { func draw() {} }
[Drawable]
class Point : Drawable {
refCount d[0] d[1]
var x, y: Double
override func draw() { … }
}
class Line : Drawable {
Line Point
var x1, y1, x2, y2: Double
override func draw() { … } type: type:

} refCount: refCount:

x1: 0.0 y: 0.0


var drawables: [Drawable] y1: 0.0 y: 0.0
for d in drawables {
x2: 0.0
d.draw()
y2: 3.0
}
Polymorphism Through V-Table Dispatch
class Drawable { func draw() {} }
Drawable.Type Point.Type Line.Type
class Point : Drawable {
draw: draw: draw:
var x, y: Double
… … …
override func draw() { … }
}
class Line : Drawable { Line
var x1, y1, x2, y2: Double
type:
override func draw() { … }
refCount:
}
x1: 0.0

var drawables: [Drawable] y1: 0.0

for d in drawables { x2: 0.0


d.draw() y2: 3.0
}
Polymorphism Through V-Table Dispatch
class Drawable { func draw() {} }
Drawable.Type Point.Type Line.Type
class Point : Drawable {
draw: draw: draw:
var x, y: Double
… … …
override func draw() { … }
}
class Line : Drawable { Line
var x1, y1, x2, y2: Double
type:
override func draw(_ self: Line) { … }
refCount:
}
x1: 0.0

var drawables: [Drawable] y1: 0.0

for d in drawables { x2: 0.0


d.type.vtable.draw(d) y2: 3.0
}
Dimensions of Performance
Class

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Final Class

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Dimensions of Performance
Final Class

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic

Optimizing Swift Performance WWDC 2015


Dimensions of Performance
Struct

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Protocol Types

Arnold Schwaighofer Swift Performance Engineer


Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable { class SharedLine : Drawable {
var x1, y1, x2, y2: Double var x1, y1, x2, y2: Double
func draw() { … } func draw() { … }
} }

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
Protocol Types
Polymorphism without inheritance or reference semantics
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
}
struct Line : Drawable {
var x1, y1, x2, y2: Double
func draw() { … }
}

var drawables: [Drawable]


for d in drawables {
d.draw()
}
No Inheritance Relationship
Dynamic dispatch without a V-Table
protocol Drawable { func draw() }

struct Point : Drawable {


var x, y: Double
func draw() { … }
Line Point
}
struct Line : Drawable { x1: 0.0 x: 0.0
var x1, y1, x2, y2: Double y1: 0.0 y: 0.0 SharedLine

func draw() { … } x2: 0.0 type:


} y2: 0.0 refCount:

x1: 0.0
var drawables: [Drawable]
y1: 0.0
for d in drawables {
x2: 0.0
d.draw()
y2: 3.0
}
The Protocol Witness Table (PWT)
Dynamic dispatch without a V-Table

4protocol Drawable {
func draw()
}
struct Point : Drawable {
func draw() { … } PointDrawable
}
draw:
struct Line : Drawable {

func draw() { … }
}
The Protocol Witness Table (PWT)
Dynamic dispatch without a V-Table

4protocol Drawable {
func draw()
}
struct Point : Drawable {
func draw() { … } PointDrawable LineDrawable
}
draw: draw:
struct Line : Drawable {
… …
func draw() { … }
}
The Protocol Witness Table (PWT)
Dynamic dispatch without a V-Table

4protocol Drawable {
func draw()
}
struct Point : Drawable {
func draw() { … } PointDrawable LineDrawable
}
draw: draw:
struct Line : Drawable {
… …
func draw() { … }
}
How to Look Up the Protocol Witness Table?
protocol Drawable { func draw() }
[Drawable]
struct Point : Drawable {
_storage: refCount ? ? ?
var x, y: Double
func draw() { … }
}
PointDrawable
struct Line : Drawable {
draw:
var x1, y1, x2, y2: Double
func draw() { … } …

var drawables: [Drawable]


for d in drawables {
d.draw()
}
How to Store Values Uniformly?
protocol Drawable { func draw() }
[Drawable]
struct Point : Drawable {
_storage: refCount ? ? ?
var x, y: Double
func draw() { … }
}
struct Line : Drawable { Line Point

var x1, y1, x2, y2: Double x1: 0.0 x: 0.0


func draw() { … }
y1: 0.0 y: 0.0
}
x2: 0.0

y2: 3.0
var drawables: [Drawable]
for d in drawables {
d.draw()
}
The Existential Container
Boxing values of protocol types
The Existential Container
Boxing values of protocol types

Inline Value Buffer: currently 3 words

valueBuffer
The Existential Container
Boxing values of protocol types

Inline Value Buffer: currently 3 words

x: 0.0

Point
y: 0.0
The Existential Container
Boxing values of protocol types

Inline Value Buffer: currently 3 words


Large values stored on heap

x1: 0.0

Line
valueBuffer y1: 0.0

x2: 0.0

y2: 3.0
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

allocate:

copy:

destruct:

deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

allocate:

copy:

destruct:

deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

allocate: valueBuffer
copy:

destruct:

deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

Line
allocate: valueBuffer
copy:

destruct:

deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT x1: 0.0

Line
allocate: valueBuffer y1: 0.0
copy: x2: 0.0
destruct: y2: 3.0
deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

Line
allocate: valueBuffer
copy:

destruct:

deallocate:
The Value Witness Table (VWT)
Allocation, Copy, Destruction of any Value

LineVWT

allocate: valueBuffer
copy:

destruct:

deallocate:
The Existential Container
Boxing values of protocol types

Inline Value Buffer: currently 3 words


Large values stored on heap
Reference to Value Witness Table
LineVWT

allocate: valueBuffer
copy:

destruct: vwt:
deallocate:
The Existential Container
Boxing values of protocol types

Inline Value Buffer: currently 3 words


Large values stored on heap
Reference to Value Witness Table
Reference to Protocol Witness Table PointDrawable

valueBuffer draw:

vwt:

pwt:
// Protocol Types
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Point()
drawACopy(val)
// Protocol Types
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Point()
drawACopy(val)

// Generated code
struct ExistContDrawable {
var valueBuffer: (Int, Int, Int)
var vwt: ValueWitnessTable
var pwt: DrawableProtocolWitnessTable
}
// Protocol Types
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Point()
drawACopy(val)

// Generated code
func drawACopy(val: ExistContDrawable) {
// Protocol Types
let local = val
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Point()
drawACopy(val)

// Generated code
func drawACopy(val: ExistContDrawable) {
// Protocol Types
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Point()
drawACopy(val)

// Generated code
func drawACopy(val: ExistContDrawable) {
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
local.draw()
valueBuffer
}
let val : Drawable = Point()
vwt:
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
local.draw()
valueBuffer
}
let val : Drawable = Point()
vwt:
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local: LineVWT
local.draw()
valueBuffer allocate:
}
copy:
let val : Drawable = Point()
vwt: destruct:
drawACopy(val)
pwt: deallocate:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local: PointVWT
local.draw()
valueBuffer allocate:
}
copy:
let val : Drawable = Point()
vwt: destruct:
drawACopy(val)
pwt: deallocate:

// Generated code
PointDrawable
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable() draw:

let vwt = val.vwt …

let pwt = val.pwt


local.type = type
local.pwt = pwt
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
local.draw()
valueBuffer
}
let val : Drawable = Point()
vwt:
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local: x: 0.0

Point
local.draw()
y:
valueBuffer
0.0
}
let val : Drawable = Point()
vwt:
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
x2: 0.0
let val : Drawable = Line()
vwt: y2: 3.0
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
x2: 0.0
let val : Drawable = Line()
vwt: y2: 3.0
drawACopy(val)
pwt:

// Generated code
LineDrawable
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable() draw:

let vwt = val.vwt …

let pwt = val.pwt


local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
x2: 0.0
let val : Drawable = Line()
vwt: y2: 3.0
drawACopy(val)
pwt:

// Generated code
LineDrawable
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable() draw:

let vwt = val.vwt …

let pwt = val.pwt


local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
x2: 0.0
let val : Drawable = Line()
vwt: y2: 3.0
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local: x: 0.0

Point
local.draw()
y: 0.0
valueBuffer
}
let val : Drawable = Point()
vwt:
drawACopy(val)
pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
let val : Drawable = Line() x2: 0.0

drawACopy(val) vwt: y2: 3.0


pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
x1: 0.0

Line
local.draw()
valueBuffer y1: 0.0
}
let val : Drawable = Line() x2: 0.0

drawACopy(val) vwt: y2: 3.0


pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
vwt.destructAndDeallocateBuffer(temp)
}
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:

Line
local.draw()
valueBuffer
}
let val : Drawable = Line()
drawACopy(val) vwt:

pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
vwt.destructAndDeallocateBuffer(temp)
}
// Protocol Types
// The Existential Container in action Stack Heap
func drawACopy(local : Drawable) { local:
local.draw()
valueBuffer
}
let val : Drawable = Line()
drawACopy(val) vwt:

pwt:

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
vwt.destructAndDeallocateBuffer(temp)
}
// Protocol Types
// The Existential Container in action
func drawACopy(local : Drawable) {
local.draw()
}
let val : Drawable = Line()
drawACopy(val)

// Generated code
func drawACopy(val: ExistContDrawable) {
var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
vwt.destructAndDeallocateBuffer(temp)
}
Protocol Type Stored Properties

struct Pair {
init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
var first: Drawable
var second: Drawable
}
Protocol Type Stored Properties

struct Pair { Existential Container inline


init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
var first: Drawable
var second: Drawable
}
Protocol Type Stored Properties

struct Pair { Existential Container inline


init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
pair: Pair
var first: Drawable
var second: Drawable
}

var pair = Pair(Line(), Point())


Protocol Type Stored Properties

struct Pair { Existential Container inline


init(_ f: Drawable, _ s: Drawable) {
Large values on the heap
first = f ; second = s
}
pair: Pair
var first: Drawable
x1: 1.0

Line
var second: Drawable y1: 1.0

} x2: 1.0

y2: 3.0

var pair = Pair(Line(), Point()) x: 2.0

Point
y: 1.0
Protocol Type Stored Properties

struct Pair { Supports dynamic polymorphism


init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
pair: Pair
var first: Drawable
x1: 1.0

Line
var second: Drawable y1: 1.0

} x2: 1.0

y2: 3.0

var pair = Pair(Line(), Point()) x1: 1.0

Line
pair.second = Line() y1: 1.0

x2: 1.0

y2: 3.0
Expensive Copies of Large Values
pair: Pair

let aLine = Line(1.0, 1.0, 1.0, 3.0)


let pair = Pair(aLine, aLine)
let copy = pair
Expensive Copies of Large Values
pair: Pair

x1: 1.0

Line
y1: 1.0

let aLine = Line(1.0, 1.0, 1.0, 3.0) x2: 1.0

y2: 3.0
let pair = Pair(aLine, aLine)
let copy = pair
Expensive Copies of Large Values
pair: Pair

x1: 1.0

Line
y1: 1.0

let aLine = Line(1.0, 1.0, 1.0, 3.0) x2: 1.0

y2: 3.0
let pair = Pair(aLine, aLine)
let copy = pair x1: 1.0

Line
y1: 1.0

x2: 1.0

y2: 3.0
Expensive Copies of Large Values
pair: Pair

x1: 1.0

Line
y1: 1.0

let aLine = Line(1.0, 1.0, 1.0, 3.0) x2: 1.0

y2: 3.0
let pair = Pair(aLine, aLine)
let copy = pair x1: 1.0

Line
y1: 1.0

x2: 1.0

y2: 3.0

copy: Pair

x1: 1.0

Line
y1: 1.0

x2: 1.0

y2: 3.0

x1: 1.0

Line
y1: 1.0

x2: 1.0

y2: 3.0
References Fit in the Value Buffer

type:

valueBuffer refCount:

x1: 1.0

vwt: y1: 1.0

pwt: x2: 1.0

y2: 3.0
References Fit in the Value Buffer

first: type:

valueBuffer refCount:

x1: 1.0

vwt: y1: 1.0

pwt: x2: 1.0

y2: 3.0

second:

valueBuffer

vwt:

pwt:
References Fit in the Value Buffer
second.x1 = 3.0

first: type:

valueBuffer refCount:

x1: 3.0
vwt: y1: 1.0

pwt: x2: 1.0

y2: 3.0

second:

valueBuffer

vwt:

pwt:
References Fit in the Value Buffer
second.x1 = 3.0

first: type:

valueBuffer refCount:

x1: 1.0

vwt: y1: 1.0

pwt: x2: 1.0

y2: 3.0

second: type:

valueBuffer refCount:

x1: 3.0
vwt: y1: 1.0

pwt: x2: 1.0

y2: 3.0
Indirect Storage with Copy-on-Write
Use a reference type for storage

class LineStorage { var x1, y1, x2, y2: Double }


struct Line : Drawable {
var storage : LineStorage
init() { storage = LineStorage(Point(), Point()) }
func draw() { … }
mutating func move() {
if !isUniquelyReferencedNonObjc(&storage) {
storage = LineStorage(storage)
}
storage.start = ...
}
}
Indirect Storage with Copy-on-Write
Use a reference type for storage

class LineStorage { var x1, y1, x2, y2: Double }


struct Line : Drawable {
var storage : LineStorage
init() { storage = LineStorage(Point(), Point()) }
func draw() { … }
mutating func move() {
if !isUniquelyReferencedNonObjc(&storage) {
storage = LineStorage(storage)
}
storage.start = ...
}
}
Indirect Storage with Copy-on-Write
Implement copy-on-write

class LineStorage { var x1, y1, x2, y2: Double }


struct Line : Drawable {
var storage : LineStorage
init() { storage = LineStorage(Point(), Point()) }
func draw() { … }
mutating func move() {
if !isUniquelyReferencedNonObjc(&storage) {
storage = LineStorage(storage)
}
storage.start = ...
}
}
Copy Using Indirect Storage
pair: Pair

let aLine = Line(1.0, 1.0, 1.0, 1.0)


let pair = Pair(aLine, aLine)
let copy = pair
Copy Using Indirect Storage
pair: Pair

Line
type:

refCount: 2
let aLine = Line(1.0, 1.0, 1.0, 1.0) x1: 1.0

let pair = Pair(aLine, aLine) y1: 1.0

x2: 1.0
let copy = pair y2: 3.0
Copy Using Indirect Storage
pair: Pair

Line
type:

refCount: 3
let aLine = Line(1.0, 1.0, 1.0, 1.0) x1: 1.0

let pair = Pair(aLine, aLine) y1: 1.0

x2: 1.0
let copy = pair y2: 3.0
Copy Using Indirect Storage
pair: Pair

Line
type:

refCount: 5
let aLine = Line(1.0, 1.0, 1.0, 1.0) x1: 1.0

let pair = Pair(aLine, aLine) y1: 1.0

x2: 1.0
let copy = pair y2: 3.0

copy: Pair
Performance of Protocol Types

func drawACopy(val: ExistContDrawable) {


var local = ExistContDrawable()
let vwt = val.vwt
let pwt = val.pwt
local.type = type
local.pwt = pwt
vwt.allocateBufferAndCopyValue(&local, val)
pwt.draw(vwt.projectBuffer(&local))
vwt.destructAndDeallocateBuffer(temp)
}
Protocol Type—Small Value

Fits in Value Buffer: no heap allocation


Protocol Type—Small Value

Fits in Value Buffer: no heap allocation


No reference counting
Protocol Type—Small Value

Fits in Value Buffer: no heap allocation


No reference counting
Dynamic dispatch through Protocol Witness Table
Protocol Type—Large Value

Heap allocation
Protocol Type—Large Value

Heap allocation
Reference counting if value contains references
Protocol Type—Large Value
Expensive heap allocation on copying

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Protocol Type—Indirect Storage
Trade off reference counting for heap allocation

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Protocol Type—Indirect Storage
Trade off reference counting for heap allocation CLASS

Allocation
Stack Heap

Reference Counting
Less More

Method Dispatch
Static Dynamic
Summary—Protocol Types

Dynamic polymorphism
Indirection through Witness Tables and Existential Container
Copying of large values causes heap allocation
// Drawing a copy
protocol Drawable {
func draw()
}
func drawACopy(local : Drawable) {
local.draw()
}
// Drawing a copy
protocol Drawable {
func draw()
}
func drawACopy(local : Drawable) {
local.draw()
}

let line = Line()


drawACopy(line)
// Drawing a copy
protocol Drawable {
func draw()
}
func drawACopy(local : Drawable) {
local.draw()
}

let line = Line()


drawACopy(line)
// ...
let point = Point()
drawACopy(point)
Generic Code
// Drawing a copy using a generic method
protocol Drawable {
func draw()
}
func drawACopy<T: Drawable>(local : T) {
local.draw()
}

let line = Line()


drawACopy(line)
// ...
let point = Point()
drawACopy(point)
// Drawing a copy using a generic method
protocol Drawable {
func draw()
}
func drawACopy<T: Drawable>(local : T) {
local.draw()
}

let line = Line()


drawACopy(line)
// ...
let point = Point()
drawACopy(point)
Generic Code

func foo<T: Drawable>(local : T) { Static polymorphism


bar(local)
One type per call context
}
func bar<T: Drawable>(local: T) { … }

let point = Point()


foo(point)
Generic Code

func foo<T: Drawable>(local : T) { Static polymorphism


bar(local)
One type per call context
}
func bar<T: Drawable>(local: T) { … }

let point = Point()


foo(point)
foo<T = Point>(point)
Generic Code

func foo<T: Drawable>(local : T) { Static polymorphism


bar(local)
One type per call context
}
func bar<T: Drawable>(local: T) { … } Type substituted down the call chain

let point = Point()


foo(point)
foo<T = Point>(point)
bar<T = Point>(local)
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) {


local.draw()
}

drawACopy(Point(…))
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
}

drawACopy(Point(…))
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}

drawACopy(Point(…))
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}
One type per call context
drawACopy(Point(…))
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw() PointVWT Uses Protocol/Value Witness Table
} allocate:
One type per call context: passes tables
copy:
drawACopy(Point(…))
destruct:

deallocate:

PointDrawable

draw:


Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}
One type per call context: passes tables
drawACopy(Point(…))
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}
let local = Point()
One type per call context: passes tables
drawACopy(Point(…))
PointVWT

allocate:

copy:

destruct

deallocate
Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}
One type per call context: passes tables
drawACopy(Point(…))

PointDrawable

draw:


Implementation of Generic Methods

func drawACopy<T : Drawable>(local : T) { One shared implementation


local.draw()
Uses Protocol/Value Witness Table
}
One type per call context: passes tables
drawACopy(Point(…))

PointDrawable

draw:

Storage of Local Variables

func drawACopy<T : Drawable>(local : T) {


local.draw()
}

drawACopy(Point(…))
Storage of Local Variables

func drawACopy<T : Drawable>(local : T) { Value Buffer: currently 3 words


local.draw()
}
let local = Point()

drawACopy(Point(…))
Storage of Local Variables

func drawACopy<T : Drawable>(local : T) { Value Buffer: currently 3 words


local.draw()
}

drawACopy(Point(…))

Stack
local:

valueBuffer
Storage of Local Variables

func drawACopy<T : Drawable>(local : T) { Value Buffer: currently 3 words


local.draw()
Small values stored inline
}

drawACopy(Point(…))

Stack
local:
x: 1.0

Point
y: 0.0
Storage of Local Variables

func drawACopy<T : Drawable>(local : T) { Value Buffer: currently 3 words


local.draw()
Small values stored inline
}
Large values stored on heap
drawACopy(Line(…))

Stack Heap
local:
x1: 0.0

Line
valueBuffer y1: 0.0

x2: 0.0

y2: 3.0
Faster?
Specialization of Generics

func drawACopy<T : Drawable>(local : T) {


local.draw()
}

drawACopy(Point(…))
Specialization of Generics

func drawACopy<T : Drawable>(local : T) { Static polymorphism


local.draw()
}

drawACopy(Point(…))
Specialization of Generics

func drawACopy<T : Drawable>(local : T) { Static polymorphism: uses type at call-site


local.draw()
}

drawACopy(Point(…))
Specialization of Generics

func drawACopy<T : Drawable>(local : T) { Static polymorphism: uses type at call-site


local.draw()
}

drawACopy(Point(…))
Specialization of Generics

func drawACopyOfAPoint(local : Point) { Static polymorphism: uses type at call-site


local.draw()
Creates type-specific version of method
}

drawACopyOfAPoint(Point(…))
Specialization of Generics

func drawACopyOfAPoint(local : Point) { Static polymorphism: uses type at call-site


local.draw()
Creates type-specific version of method
}
func drawACopyOfALine(local : Line) { Version per type in use
local.draw()
}

drawACopyOfAPoint(Point(…))
drawACopyOfALine(Line(…))
Specialization of Generics

func drawACopyOfAPoint(local : Point) { Static polymorphism: uses type at call-site


local.draw()
Creates type-specific version of method
}
func drawACopyOfALine(local : Line) { Version per type in use
local.draw() Can be more compact after optimization
}

let local = Point()


local.draw()
drawACopyOfALine(Line(…))
Specialization of Generics

func drawACopyOfAPoint(local : Point) { Static polymorphism: uses type at call-site


local.draw()
Creates type-specific version of method
}
func drawACopyOfALine(local : Line) { Version per type in use
local.draw() Can be more compact after optimization
}

Point().draw()

drawACopyOfALine(Line(…))
Specialization of Generics

Static polymorphism: uses type at call-site


Creates type-specific version of method
Version per type in use
Can be more compact after optimization

Point().draw()

Line().draw()
When Does Specialization Happen?

main.swift
struct Point { … }
let point = Point()
drawACopy(point)
When Does Specialization Happen?

Infer type at call-site

main.swift
struct Point { … }
let point = Point()
drawACopy(point)
When Does Specialization Happen?

Infer type at call-site


Definition must be available

main.swift
struct Point { … }
let point = Point()
drawACopy(point)
Whole Module Optimization
Increases optimization opportunity

Point.swift UsePoint.swift
struct Point { let point = Point()
func draw() {} drawACopy(point)
}
Whole Module Optimization
Increases optimization opportunity

Module A
Point.swift UsePoint.swift
struct Point { let point = Point()
func draw() {} drawACopy(point)
}
// Pairs in our program
struct Pair {
init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
var first: Drawable
var second: Drawable
}
// Pairs in our program
struct Pair {
init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
var first: Drawable
var second: Drawable
}

let pairOfLines = Pair(Line(), Line())


// Pairs in our program
struct Pair {
init(_ f: Drawable, _ s: Drawable) {
first = f ; second = s
}
var first: Drawable
var second: Drawable
}

let pairOfLines = Pair(Line(), Line())

// ...

let pairOfPoint = Pair(Point(), Point())


// Pairs in our program
pairOfLines: Pair
struct Pair {
init(_ f: Drawable, _ s: Drawable) { x1: 1.0

Line
y1: 1.0

first = f ; second = s x2: 1.0

y2: 3.0
}
var first: Drawable
x1: 1.0

Line
var second: Drawable y1: 1.0

x2: 1.0
}
y2: 3.0

let pairOfLines = Pair(Line(), Line())

// ...

let pairOfPoint = Pair(Point(), Point())


// Pairs in our program using generic types
struct Pair<T : Drawable> {
init(_ f: T, _ s: T) {
first = f ; second = s
}
var first: T
var second: T
}

let pairOfLines = Pair(Line(), Line())

// ...

let pairOfPoint = Pair(Point(), Point())


Generic Stored Properties

struct Pair<T: Drawable> {


init(_ f: T, _ s: T) {
first = f ; second = s
}
var first: T
var second: T
}

var pair = Pair(Line(), Line())


Generic Stored Properties

struct Pair<T: Drawable> { Type does not change at runtime


init(_ f: T, _ s: T) {
first = f ; second = s
}
var first: T
var second: T
}

var pair = Pair(Line(), Line())


Generic Stored Properties

struct Pair<T: Drawable> { Type does not change at runtime


init(_ f: T, _ s: T) {
Storage inline
first = f ; second = s
}
pair: Pair
var first: T
var second: T
}

var pair = Pair(Line(), Line())


Generic Stored Properties

struct Pair<T: Drawable> { Type does not change at runtime


init(_ f: T, _ s: T) {
Storage inline
first = f ; second = s
}
pair: Pair
var first: T
Line
var second: T x1: 0.0

} y1: 0.0

x2: 0.0

y2: 3.0

var pair = Pair(Line(), Line()) Line


x1: 1.0

y1: 1.0

x2: 0.0

y2: 3.0
Generic Stored Properties

struct Pair<T: Drawable> { Type does not change at runtime


init(_ f: T, _ s: T) {
Storage inline
first = f ; second = s
}
pair: Pair
var first: T
Line
var second: T x1: 0.0

} y1: 0.0

x2: 0.0

y2: 3.0

var pair = Pair(Line(), Line()) Line


pair.first = Point() x1: 1.0

y1: 1.0

x2: 0.0

y2: 3.0
Performance of Generic Code
Unspecialized

func drawACopy<T : Drawable>(local : T) {


local.draw() PointVWT

} allocate:

copy:
drawACopy(Point(…)) destruct:

deallocate:
Performance of Generic Code
Unspecialized Specialized

func drawACopy<T : Drawable>(local : T) { func drawACopyOfAPoint(local : Point) {


local.draw() PointVWT local.draw()
} allocate: }
copy: func drawACopyOfALine(local : Line) {
drawACopy(Point(…)) destruct: local.draw()

deallocate: }

drawACopyOfAPoint(Point(…))
drawACopyOfALine(Line(…))
Specialized Generics—Struct Type

Performance characteristics like struct types


• No heap allocation on copying
Specialized Generics—Struct Type

Performance characteristics like struct types


• No heap allocation on copying
• No reference counting
Specialized Generics—Struct Type

Performance characteristics like struct types


• No heap allocation on copying
• No reference counting
• Static method dispatch
Specialized Generics—Class Type

Performance characteristics like class types


• Heap allocation on creating an instance
• Reference counting
• Dynamic method dispatch through V-Table
Unspecialized Generics—Small Value

No heap allocation: value fits in Value Buffer


Unspecialized Generics—Small Value

No heap allocation: value fits in Value Buffer


No reference counting
Unspecialized Generics—Small Value

No heap allocation: value fits in Value Buffer


No reference counting
Dynamic dispatch through Protocol Witness Table
Unspecialized Generics—Large Value

Heap allocation (use indirect storage as a workaround)


Reference counting if value contains references
Dynamic dispatch through Protocol Witness Table
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
• struct types: value semantics
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
• struct types: value semantics
• class types: identity or OOP style polymorphism
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
• struct types: value semantics
• class types: identity or OOP style polymorphism
• Generics: static polymorphism
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
• struct types: value semantics
• class types: identity or OOP style polymorphism
• Generics: static polymorphism
• Protocol types: dynamic polymorphism
Summary

Choose fitting abstraction with the least dynamic runtime type requirements
• struct types: value semantics
• class types: identity or OOP style polymorphism
• Generics: static polymorphism
• Protocol types: dynamic polymorphism
Use indirect storage to deal with large values
Related Sessions

What's New in Swift Presidio Tuesday 9:00AM

What's New in Foundation for Swift Mission Tuesday 4:00 PM

Protocol And Value Oriented Programming in UIKit Apps Presidio Friday 4:00 PM

Protocol-Oriented Programming in Swift WWDC 2015

Building Better Apps with Value Types in Swift WWDC 2015

Optimizing Swift Performance WWDC 2015


Labs

Swift Open Hours Dev Tools Lab A Friday 12:00PM

You might also like