Slide 1

Slide 1 text

HIGH PERFORMANCE SWIFT MATT GALLAGHER, PLAYGROUNDS 2017

Slide 2

Slide 2 text

Matt Gallagher @cocoawithlove https://www.cocoawithlove.com

Slide 3

Slide 3 text

https://www.zqueue.com StreamToMe & ServeToMe

Slide 4

Slide 4 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT OVERVIEW ▸ Is Swift a high performance language? What is a high performance language? ▸ How do we optimize Swift at the lowest level? ▸ Understanding the relative cost of abstractions in Swift.

Slide 5

Slide 5 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT WHAT THIS TALK WILL NOT BE ▸ When to optimize or why ▸ General optimization concerns (minimizing allocations, cache efficiency, complexity analysis, etc) ▸ Maximizing CPU through multithreading or SIMD ▸ Using APIs like OpenCL, MPI

Slide 6

Slide 6 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT SWIFT TOPICS I WON'T COVER THOROUGHLY OR AT ALL ▸ Avoiding memory allocations in the Swift standard library ▸ Implications of copy-on-write types ▸ Swift String performance

Slide 7

Slide 7 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT IS SWIFT "HIGH PERFORMANCE"? ▸ "Swift"

Slide 8

Slide 8 text

FAST. MODERN. SAFE. INTERACTIVE. Craig Federighi, introducing Swift at WWDC 2014 MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT

Slide 9

Slide 9 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT COMPARED TO WHAT? ▸ Between 3.9x and 220x faster than Python ▸ Between 1.4x and 1.75x faster than Objective-C ▸ When looking at the relative performance of different languages, we usually look at C.

Slide 10

Slide 10 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT COMPARED TO C ▸ Why is C considered the baseline for performance? ▸ Abstractions.

Slide 11

Slide 11 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT WHY IS C THE BASELINE FOR PERFORMANCE? ▸ It has C ABI function calls with pass-by-value semantics ▸ It has pointers and function pointers ▸ It has arithmetic and logic operations

Slide 12

Slide 12 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT MT19937_64 MERSENNE TWISTER RANDOM NUMBER GENERATOR IN C unsigned long long genrand64_int64(struct mt19937_64* context) { int i; unsigned long long x; static unsigned long long mag01[2]={0ULL, MATRIX_A}; unsigned long long *mt = context->mt; if (context->mti >= NN) { /* generate NN words at one time */ for (i = 0; i < NN - MM; i++) { x = (mt[i] & UM) | (mt[i + 1] & LM); mt[i] = mt[i + MM] ^ (x >> 1) ^ mag01[(int)(x & 1ULL)]; } for (; i> 1) ^ mag01[(int)(x & 1ULL)]; } x = (mt[NN - 1] & UM) | (mt[0] & LM); mt[NN - 1] = mt[MM - 1] ^ (x >> 1) ^ mag01[(int)(x & 1ULL)]; context->mti = 0; } x = mt[context->mti++]; x ^= (x >> 29) & 0x5555555555555555ULL; x ^= (x << 17) & 0x71D67FFFEDA60000ULL; x ^= (x << 37) & 0xFFF7EEE000000000ULL; x ^= (x >> 43); return x; }

Slide 13

Slide 13 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT MT19937_64 MERSENNE TWISTER RANDOM NUMBER GENERATOR IN SWIFT public mutating func random64() -> UInt64 { var x: UInt64 = 0 if mti >= NN { for i in 0..<(NN - MM) { x = (mt[i] & UM) | (mt[i + 1] & LM) mt[i] = mt[i + MM] ^ (x >> 1) ^ mag01[Int(x & 1)] } for i in (NN - MM)..<(NN - 1) { x = (mt[i] & UM) | (mt[i + 1] & LM) mt[i] = mt[i - MM] ^ (x >> 1) ^ mag01[Int(x & 1)] } x = (mt[NN - 1] & UM) | (mt[0] & LM) mt[NN - 1] = mt[MM - 1] ^ (x >> 1) ^ mag01[Int(x & 1)] mti = 0 } x = mt[mti]; mti = mti + 1; x ^= (x >> 29) & 0x5555555555555555 x ^= (x << 17) & 0x71D67FFFEDA60000 x ^= (x << 37) & 0xFFF7EEE000000000 x ^= x >> 43 return x }

Slide 14

Slide 14 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT

Slide 15

Slide 15 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT MT19937-64 IN C VERSUS A DIRECT SWIFT TRANSLATION C SWIFT 1.04s 0.62s Swift is 60-70% slower than C. Not too bad, right?

Slide 16

Slide 16 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT TO UNDERSTAND WHY, LET'S LOOK AT THE PROFILE RESULTS...

Slide 17

Slide 17 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT MERSENNE TWISTER IMPLEMENTATION RESULTS C INITIAL SWIFT NO ARRAY INDEX NO ARRAYS FINAL SWIFT 0.52s 0.66s 1.49s 1.04s 0.62s Swift is 20% faster than C

Slide 18

Slide 18 text

IS SWIFT AS FAST AS C? ▸ Like C: ▸ Swift has C ABI functions with pass-by-value semantics ▸ Swift has pointers ▸ Swift has basic arithmetic and logic operations ▸ If you use Swift to write C, performance will be identical ▸ Generally though, Swift will often be 50% slower because we're happy to allow safety checks and reference counting. MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT

Slide 19

Slide 19 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT ZERO OVERHEAD ABSTRACTIONS HAVE NO COST ▸ C doesn't have many abstractions. That's why it's considered high performance. ▸ The only hard part is ensuring appropriate functions can be inlined. ▸ Swift is the same: give it a chance to inline an abstraction it its cost may drop to zero.

Slide 20

Slide 20 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT SWIFT ABSTRACTIONS ▸ Like C, Swift's primary abstraction the humble function. ▸ Exception in Swift, there's a lot of different kinds.

Slide 21

Slide 21 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT FUNCTION VARIATIONS EMPTY FUNCTION ONE PARAM REFERENCE PARAM OBJC PARAM INOUT REF PARAM 2.15ns 25.4ns 15.6ns 2.86ns 2.08ns func emptyFunction() func one(param: Result) func one(param: BasicClass) func one(param: ObjcSubclass) func one(param: inout BasicClass)

Slide 22

Slide 22 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT GENERIC VARIATIONS, 1BILLION INVOCATIONS ONE PARAM GENERIC PARAM GENERIC ENUM PARAM GENERIC INOUT PARAM LARGER INOUT PARAM 359ns 84.3ns 23.4ns 12.3ns 2.86ns func one(param: Result) // Same Result passed func generic(param: T) // Same Int generic param used func generic(param: Result) // Same Int generic param used func generic(param: inout Result) // Generic (Int, Int, Int) param used func generic(param: inout Result)

Slide 23

Slide 23 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT METHOD VARIATIONS, 1BILLION INVOCATIONS INOUT REF PARAM NON-FINAL FINAL DYNAMIC OBJ-C 6.69ns 5.82ns 2.13ns 3.23ns 2.15ns func one(param: inout BasicClass) class ObjcSubclass: NSObject { func nonFinalMethod() final func finalMethod() dynamic func dynamicMethod() @objc dynamic func objcMethod() }

Slide 24

Slide 24 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT GENERIC CONVERSIONS, 1BILLION INVOCATIONS NON-GENERIC GENERIC 309ns 4.87ns func paramCopyingFunction(array: inout Array, result: Int) { array[0] = result } func genericCopyingFunction(array: inout Array, result: T) { array[0] = result }

Slide 25

Slide 25 text

MATT GALLAGHER, PLAYGROUNDS 2017: HIGH PERFORMANCE SWIFT SWIFT FUNCTION ABSTRACTIONS AND PERFORMANCE ▸ Understand "pass-by-ownership" conventions. Single owner types would help greatly – see the Swift "Ownership" manifesto and the pending "owned/shared/moveonly" keywords. ▸ Use inout or methods to reduce reference count overheads. ▸ Specialize your generics by keeping declarations and usage in the same module. ▸ Swift can't specialize or inline across compilation/module boundaries (this is a possibility for Swift 4).