416 Understanding Swift Performance
416 Understanding Swift Performance
© 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
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
struct Point {
var x, y: Double
func draw() { … }
}
point1: x:
struct Point {
y:
var x, y: Double
point2: x:
func draw() { … }
y:
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x:
func draw() { … }
y:
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 0.0
func draw() { … }
y: 0.0
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 5.0
func draw() { … }
y: 0.0
}
class Point {
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2:
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2:
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2:
var x, y: Double
x: 0.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 0.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}
point1:
class Point {
point2:
var x, y: Double
x: 5.0
func draw() { … }
y: 0.0
}
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
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}
…
}
// Modeling Techniques: Allocation
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
let key = "\(color):\(orientation):\(tail)"
if let image = cache[key] {
return image
}
…
}
// Modeling Techniques: Allocation
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
class Point {
var x, y: Double
func draw() { … }
}
class Point {
var refCount: Int
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2:
var refCount: Int
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2:
var refCount: Int
var x, y: Double
func draw() { … }
}
point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 0.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 2
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 1
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
point1:
class Point {
point2: refCount: 0
var refCount: Int
x: 5.0
var x, y: Double
y: 0.0
func draw() { … }
}
struct Point {
var x, y: Double
func draw() { … }
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x:
func draw() { … }
y:
}
point1: x: 0.0
struct Point {
y: 0.0
var x, y: Double
point2: x: 0.0
func draw() { … }
y: 0.0
}
struct Label {
var text: String
var font: UIFont
func draw() { … }
}
label1: text:
String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:
String
}
label1: text:
String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:
String
}
String
struct Label {
var text: String
var font: UIFont
font:
func draw() { … }
label2: text:
String
}
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
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:
self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
Stack
struct Attachment {
fileURL:
let fileURL: URL
UUID
uuid:
let uuid: UUID
let mimeType: String
String
mimeType:
self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
// Modeling Techniques: Reference Counting
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
self.fileURL = fileURL
self.uuid = uuid
self.mimeType = mimeType
}
}
Method Dispatch
Method Dispatch
Static
Method Dispatch
Static
struct Point {
var x, y: Double
func draw() {
// Point.draw implementation
}
}
struct Point {
var x, y: Double
func draw() {
// Point.draw implementation
}
}
struct Point {
var x, y: Double
func draw() {
// Point.draw implementation
}
}
struct Point {
var x, y: Double
func draw() {
// Point.draw implementation
}
}
point: x:
struct Point {
y:
var x, y: Double
func draw() {
// Point.draw implementation
}
}
point: x: 0.0
struct Point {
y: 0.0
var x, y: Double
func draw() {
// Point.draw implementation
}
}
point: x: 0.0
struct Point {
y: 0.0
var x, y: Double
func draw() {
// Point.draw implementation
}
}
point: x: 0.0
struct Point {
y: 0.0
var x, y: Double
func draw() {
// Point.draw implementation
}
}
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 …
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 …
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 …
} refCount: refCount:
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
Allocation
Stack Heap
Reference Counting
Less More
Method Dispatch
Static Dynamic
Protocol Types
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() { … } …
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
valueBuffer
The Existential Container
Boxing values of protocol types
x: 0.0
Point
y: 0.0
The Existential Container
Boxing values of protocol types
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
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
allocate: valueBuffer
copy:
destruct: vwt:
deallocate:
The Existential Container
Boxing values of protocol types
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:
// 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:
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:
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
// 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
// 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
Line
var second: Drawable y1: 1.0
} x2: 1.0
y2: 3.0
Point
y: 1.0
Protocol Type Stored Properties
Line
var second: Drawable y1: 1.0
} x2: 1.0
y2: 3.0
Line
pair.second = Line() y1: 1.0
x2: 1.0
y2: 3.0
Expensive Copies of Large Values
pair: Pair
x1: 1.0
Line
y1: 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
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
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
y2: 3.0
References Fit in the Value Buffer
first: type:
valueBuffer refCount:
x1: 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
y2: 3.0
second:
valueBuffer
vwt:
pwt:
References Fit in the Value Buffer
second.x1 = 3.0
first: type:
valueBuffer refCount:
x1: 1.0
y2: 3.0
second: type:
valueBuffer refCount:
x1: 3.0
vwt: y1: 1.0
y2: 3.0
Indirect Storage with Copy-on-Write
Use a reference type for storage
Line
type:
refCount: 2
let aLine = Line(1.0, 1.0, 1.0, 1.0) x1: 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
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
x2: 1.0
let copy = pair y2: 3.0
copy: Pair
Performance of Protocol Types
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()
}
drawACopy(Point(…))
Implementation of Generic Methods
drawACopy(Point(…))
Implementation of Generic Methods
drawACopy(Point(…))
Implementation of Generic Methods
deallocate:
PointDrawable
draw:
…
Implementation of Generic Methods
allocate:
copy:
destruct
deallocate
Implementation of Generic Methods
PointDrawable
draw:
…
Implementation of Generic Methods
PointDrawable
draw:
…
Storage of Local Variables
drawACopy(Point(…))
Storage of Local Variables
drawACopy(Point(…))
Storage of Local Variables
drawACopy(Point(…))
Stack
local:
valueBuffer
Storage of Local Variables
drawACopy(Point(…))
Stack
local:
x: 1.0
Point
y: 0.0
Storage of Local Variables
Stack Heap
local:
x1: 0.0
Line
valueBuffer y1: 0.0
x2: 0.0
y2: 3.0
Faster?
Specialization of Generics
drawACopy(Point(…))
Specialization of Generics
drawACopy(Point(…))
Specialization of Generics
drawACopy(Point(…))
Specialization of Generics
drawACopy(Point(…))
Specialization of Generics
drawACopyOfAPoint(Point(…))
Specialization of Generics
drawACopyOfAPoint(Point(…))
drawACopyOfALine(Line(…))
Specialization of Generics
Point().draw()
drawACopyOfALine(Line(…))
Specialization of Generics
Point().draw()
Line().draw()
When Does Specialization Happen?
main.swift
struct Point { … }
let point = Point()
drawACopy(point)
When Does Specialization Happen?
main.swift
struct Point { … }
let point = Point()
drawACopy(point)
When Does Specialization Happen?
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
}
// ...
Line
y1: 1.0
y2: 3.0
}
var first: Drawable
x1: 1.0
Line
var second: Drawable y1: 1.0
x2: 1.0
}
y2: 3.0
// ...
// ...
} y1: 0.0
x2: 0.0
y2: 3.0
y1: 1.0
x2: 0.0
y2: 3.0
Generic Stored Properties
} y1: 0.0
x2: 0.0
y2: 3.0
y1: 1.0
x2: 0.0
y2: 3.0
Performance of Generic Code
Unspecialized
} allocate:
copy:
drawACopy(Point(…)) destruct:
deallocate:
Performance of Generic Code
Unspecialized Specialized
deallocate: }
drawACopyOfAPoint(Point(…))
drawACopyOfALine(Line(…))
Specialized Generics—Struct Type
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
Protocol And Value Oriented Programming in UIKit Apps Presidio Friday 4:00 PM