Skip to content

Latest commit

ย 

History

History
386 lines (214 loc) ยท 11.2 KB

Understanding Swift Performance.md

File metadata and controls

386 lines (214 loc) ยท 11.2 KB

@ WWDC 16

์ด๋ฒˆ ์˜์ƒ์˜ ๋ชฉํ‘œ๋Š” ์„ฑ๋Šฅ์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ตฌํ˜„์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ!

Understand the implementation to understand performance

Agenda

  • Allocation
  • Reference counting
  • Method dispatch
  • Protocol types
  • Generic code

Allocation

Stack

  • Decrement stack pointer to allocate
  • Increment stack pointer to deallocate

์Šคํƒ์˜ ์„ฑ์งˆ์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ด ๊ฐ€๋Šฅ. push, pop! ์Šคํƒ์€ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์ด๊ณ  stack allocation์€ ๊ต‰์žฅํžˆ ๋น ๋ฅด๋‹ค.

Heap

  • Advanced data structure
  • Search for unused block of memory to allocate
  • Reinsert block of memory to deallocate
  • Thread safety overhead

ํž™์€ ๋™์ ์ธ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ–๊ฒŒ ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์Šคํƒ์ด ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ๋Œ€์‹  ๋” ๋ณต์žกํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ฐ–๋Š”๋‹ค. ํž™์— ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ ์ž ํ•˜๋ฉด, ํž™ ์ž๋ฃŒ๊ตฌ์กฐ์—์„œ ์ ๋‹นํ•œ ์‚ฌ์ด์ฆˆ์˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ธ”๋ฝ์„ ์ฐพ์•„์•ผ ํ•œ๋‹ค. deallocateํ•  ๋•Œ๋„ ํ•ด๋‹น ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ์ ˆํ•œ ์œ„์น˜์— ๋‹ค์‹œ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค. ์ด์ฒ˜๋Ÿผ heap allocation์—๋Š” stack allocation์ด ๊ฐ„๋‹จํ–ˆ๋˜ ๊ฒƒ๊ณผ๋Š” ๋‹ฌ๋ฆฌ ํ• ๋‹น๊ณผ ๊ด€๋ จ๋œ ๋น„์šฉ์ด ๋“ ๋‹ค. ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ํž™์— ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํž™์€ lock์ด๋‚˜ ๋‹ค๋ฅธ ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ด์šฉํ•ด์•ผ ํ•˜๊ณ  ์ด๋Š” ์ƒ๋‹นํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๋‹ค.

image

์ด ํ•จ์ˆ˜์— ๋“ค์–ด๊ฐ€์„œ ์–ด๋–ค ์ฝ”๋“œ๋„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์ด๋ฏธ stack ์˜์—ญ์— point1๊ณผ point2๋ฅผ ์œ„ํ•œ ๊ณต๊ฐ„์ด ํ• ๋‹น๋œ๋‹ค. point1์ด ์žˆ๋Š” ๋ผ์ธ์„ ์‹คํ–‰ํ•˜๋ฉด ์ด๋ฏธ ํ• ๋‹น๋˜์–ด ์žˆ๋˜ point1์ด ์ดˆ๊ธฐํ™”๋˜๊ณ , point2๋Š” point1์„ ๋ณต์‚ฌํ•ด์„œ ์ดˆ๊ธฐํ™”๋œ๋‹ค. point2.x = 5๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹จ์ˆœํžˆ point2์˜ x๋งŒ์„ ๋ฐ”๊พผ๋‹ค. ์ด๋ฅผ value semantics๋‹ค! ํ•ด์ œํ•  ๋• ์Šคํƒํฌ์ธํ„ฐ๋ฅผ ์ฆ๊ฐ€์‹œ์ผœ์„œ ํ•ด์ œํ•œ๋‹ค.

image

ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ–ˆ์„ ๋•Œ๋„ struct๋ฅผ ์ด์šฉํ–ˆ์„ ๋•Œ์™€ ๊ฐ™์ด ํ•จ์ˆ˜์— ๋“ค์–ด๊ฐ€๋ฉด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์Šคํƒ์— ํ• ๋‹นํ•œ๋‹ค. ์ด ๋•Œ ์‹ค์ œ์ ์ธ Point์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ point1๊ณผ point2์˜ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ํ• ๋‹นํ•œ๋‹ค. (0, 0)์„ ์ƒ์„ฑํ•  ๋•Œ ์Šค์œ„ํ”„ํŠธ๋Š” ํž™์„ lockํ•˜๊ณ  ์ž๋ฃŒ ๊ตฌ์กฐ์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ ์ ์ ˆํ•œ ํฌ๊ธฐ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ฝ์„ ์ฐพ๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  point1์˜ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ํž™ ์˜์—ญ์— ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ์˜ ์ฃผ์†Œ๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. struct๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์‹ค์ œ ํฌ๊ธฐ๋Œ€๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹นํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ํฌ์ธํŠธ๋ฅผ ํ• ๋‹นํ•  ๋•Œ 2 ์›Œ๋“œ๋งŒ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•  ๋•Œ๋Š” 4 ์›Œ๋“œ๊ฐ€ ํ• ๋‹น๋œ๋‹ค. ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ 2 ์›Œ๋“œ๊ฐ€ ๋” ํ• ๋‹น๋œ ๊ฒƒ.

point1์ด ์ดˆ๊ธฐํ™”๋˜๊ณ  ๋‚œ ๋‹ค์Œ let point2 = point1 ์„ ์‹คํ–‰ํ•˜๋ฉด point2๋Š” point1์˜ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๋ณต์‚ฌํ•œ๋‹ค. ๊ทธ๋ž˜์„œ point2์™€ point1์ด ์™„์ „ํžˆ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๊ฒƒ์ด reference semantics๋‹ค! ๊ทธ๋ฆฌ๊ณ  reference semantics๋Š” ์˜๋„ํ•˜์ง€ ์•Š๊ฒŒ state๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•ด์ œํ•  ๋• ์Šค์œ„ํ”„ํŠธ๊ฐ€ ํž™์— lock์„ ๊ฑธ๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ deallocateํ•œ ๋‹ค์Œ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ฝ์„ ๋‹ค์‹œ ์ ์ ˆํ•œ ์œ„์น˜๋กœ ๋Œ๋ ค๋†“๋Š”๋‹ค. ๊ทธ ๋‹ค์Œ ์Šคํƒ์„ popํ•œ๋‹ค.

// 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
  }
  ...
}

์œ„์™€ ๊ฐ™์ด caching์„ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž! String์„ ์ด์šฉํ•ด์„œ key๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๊ทธ๋ ‡๊ฒŒ ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๊ณ , String์€ Character๋ฅผ ์ €์žฅํ•  ๋•Œ ๊ฐ„์ ‘์ ์œผ๋กœ heap allocation์„ ์‚ฌ์šฉํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ๋ฐฉ์‹์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค!!

// Modeling Techniques: Allocation

enum Color { case blue, green, gray }
enum Orientation { case left, right }
enum Tail { case none, tail, bubble }

struct Attributes: Hashable {
  var color: Color
  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
  }
  ...
}

String์„ ์‚ฌ์šฉํ•  ๋•Œ๋ณด๋‹ค ๋” ์•ˆ์ „ํ•˜๊ณ , ๊ทธ ์–ด๋–ค heap allocation๋„ ์—†๋‹ค!

Reference Counting

  • There's more to reference counting than incrementing, decrementing
    • Indirection
    • Thread safety overhead

์•„๊นŒ ๋ณด์•˜๋˜ Point ์ฝ”๋“œ์˜ reference counting์„ ๋ณด๋ฉด ์ด๋Ÿฐ ๋Š๋‚Œ์ด๋‹ค.

image

point1์ด ์ดˆ๊ธฐํ™”๋  ๋•Œ refCount: 1, point2์— ๋ณต์‚ฌํ•˜๊ณ  retain๋˜์„œ refCount: 2 ์ดํ›„ release๋˜๋ฉด์„œ refCount: 1, refCount: 0์ด ๋˜๊ณ  refCount๊ฐ€ 0์ด ๋˜๋ฉด deallocate๋œ๋‹ค. struct๋Š” reference๋ฅผ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— reference counting์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

image

์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋งŽ์ด ์ผ์–ด๋‚œ๋‹ค! struct๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธด ํ•˜์ง€๋งŒ ๋‚ด๋ถ€์ ์œผ๋กœ heap allocation์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— reference counting๋„ ๋งŽ์ด ์ผ์–ด๋‚จ.

image

์ด๋Ÿฐ ๊ตฌ์กฐ๋Š” ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์„๊นŒ? String์€ ์•ž์„œ ๋งํ–ˆ๋‹ค์‹œํ”ผ struct์ด์ง€๋งŒ ๋‚ด๋ถ€ Character๋“ค์ด heap์— ํ• ๋‹น๋˜๊ธฐ ๋•Œ๋ฌธ์— reference counting์ด ๋งŽ์ด ์ผ์–ด๋‚œ๋‹ค.

// Modeling Techniques: Reference Counting

enum MimeType: String {
  case jpeg = "image/jpeg"
  case png = "image/png"
  case gif = "image/gif"
}

struct Attachment {
  let fileURL: URL
  let uuid: UUID
  let mimeType: MimeType
  
  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
  }
}

uuid์—๋Š” UUID ํƒ€์ž…์„ ์ด์šฉํ•˜๊ณ , String ํƒ€์ž…์ด์—ˆ๋˜ mimeType์€ enum์„ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์„œ ์ด์šฉํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ์ข€ ๋” ์•ˆ์ „ํ•˜๊ณ  ๋” ์ ์€ reference counting์„ ํ•œ๋‹ค! (์„ฑ๋Šฅ์ด ๋” ์ข‹์•„์ง„๋‹ค๋Š” ๋ง์ด๊ฒ ์ฃ ~!)

Method Dispatch

Static

  • Jump directly to implementation at run time
  • Candidate for inlining and other optimizations
    • no call stack overhead!

Dynamic

  • Look up implementation in table at run time
  • Then jump to implementation
  • Prevents inlining and other optimizations

static dispatch์™€ dynamic dispatch ์‚ฌ์ด์—๋Š” ์—„์ฒญ๋‚œ ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ๊ฐ„์ ‘์„ฑ ๋ ˆ๋ฒจ์ด ํ•˜๋‚˜ ์ถ”๊ฐ€๋œ๋‹ค. dynamic dispatch์—๋Š” ์Šค๋ ˆ๋“œ ๋™๊ธฐํ™” ์˜ค๋ฒ„ํ—ค๋“œ์ฒ˜๋Ÿผ reference counting์ด๋‚˜ heap allocation์—์„œ ์žˆ์—ˆ๋˜ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ปดํŒŒ์ผ ํƒ€์ž„์—์„œ optimization์ด ์•ˆ ๋จ ใ…  ์ด๊ฒŒ ๊ต‰์žฅํžˆ ์ค‘์š”ํ•œ ํฌ์ธํŠธ! ํ•˜๋‚˜์˜ static dispatch์™€ ํ•˜๋‚˜์˜ dynamic dispatch ์‚ฌ์ด์—๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์—†์ง€๋งŒ chainingํ•  ๊ฒฝ์šฐ์—๋Š” ์ด optimization ๋•๋ถ„์— ๋งŽ์€ ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

subclassingํ•˜์ง€ ์•Š์„ class์— ๋Œ€ํ•ด์„œ๋Š” final ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ฃผ์ž! ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด dynamic dispatch๋ฅผ static dispatch๋กœ ๋ฐ”๊ฟ€ ๊ฒƒ์ด๋‹ค.

image

๊ทธ๋Ÿผ dynamic dispatch๋Š” ์™œ ์“ธ๊นŒ?! polymorphism ๋•Œ๋ฌธ์ด๋‹ค.

Protocol Types

polymorphism์„ inheritance๋‚˜ reference semantics ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. Protocol์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์–ด๋–ป๊ฒŒ ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋Š๋ƒ! Existential Container๋ฅผ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ.

Existential Container

  • Inline Value Buffer: currently 3 words
  • Large values stored on heap
  • Reference to Value Witness Table (allocation, copy, destruction)
  • Reeference to Protocol Witness Table

image

์ž‘๋™ํ•˜๋Š” ์ˆœ์„œ๋Š” ์•„๋ž˜์— ์žˆ๋Š” Generated code๋ฅผ ๋ณด๋ฉด ๋œ๋‹ค.

image

ํ”„๋กœํ† ์ฝœ ํƒ€์ž…์€ existential container๋กœ ํ‘œํ˜„๋˜๊ณ , ํฌ๊ธฐ๊ฐ€ ์ž‘์€ ๊ฐ’๋“ค์€ value buffer์—, ํฌ๊ธฐ๊ฐ€ ํฐ ๊ฐ’์€ ๋ ˆํผ๋Ÿฐ์Šค๋กœ ์ €์žฅํ•œ๋‹ค.

image

์ด๋Ÿฐ ๊ฐ’๋“ค์„ ๋ณต์‚ฌํ•˜๊ฒŒ ๋˜๋ฉด copy-on-write ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค. first์™€ second๊ฐ€ ๊ฐ™์€ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋‹ค๊ฐ€ second.x1 = 3.0์œผ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด

image

๊ทธ๋•Œ ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ด์„œ ๋ณต์‚ฌํ•œ๋‹ค.

Indirect Storage with 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 !isUniquelyReferencedNoneObjc(&storage) { // uniqueํ•˜๊ฒŒ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” ๊ฒŒ ์•„๋‹ ๊ฒฝ์šฐ์—๋Š” ์ƒˆ๋กญ๊ฒŒ ํ• ๋‹นํ•œ๋‹ค.
      storage = LineStorage(storage)
    }
    storage.start = ...
  }
}

Small Value

  • Fits in Value Buffer: no heap allocation
  • No reference counting
  • Dynamic dispatch through Protocol Witness Table (PWT)

Large Value

  • Heap allocation
  • Reference counting if value contains references

Summary

  • Dynamic polymorphism
  • Indirection through Witness Tables and Existential Container
  • Copying of large values causes heap allocation

Generic Code

  • Static polymorphism
  • One type per call context
  • Type substituted down the call chain

Implementation of Generic Methods

  • One shared implementation
  • Uses Protocol/Value Witness Table
  • One type per call context: passes tables

Storage of Local Variables

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

drawACopy(Point(...))
drawACopy(Line(...))
  • Value Buffeer: currently 3 words
  • Small values stored inline
  • Large values stored on heap

๊ทธ๋Ÿผ ์‹ค์ œ๋กœ Generic์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๋นจ๋ผ?

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

image

whole module optimization์„ ์ด์šฉํ•ด์„œ ๋” ์ตœ์ ํ™”๋ฅผ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. Xcode6๋ถ€ํ„ฐ ๊ธฐ๋ณธ!

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())
  • Type does not change at runtime
  • Storage inline!!!!

image

Specialized Generics - Struct Type

  • Performance characteristics like struct types
    • No heap allocation on copying
    • No reference counting
    • Static method dispatching

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
  • 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
    • struct types: value semantics
    • class type: identity or OOP style polymorphism
    • Generics: static polymorphism
    • Protocol types: dynamic polymorphism
  • Use indirect storage to deal with large values