Swift: Compile Time versus Run Time

Swift: Compile Time versus Run Time

A6a3f76eac79606d705426bcc267dc01?s=128

airspeedswift

May 22, 2015
Tweet

Transcript

  1. Compile(Time(vs(Run(Time:(Fight!

  2. This%is%a%needlessly%trollbai0ng%0tle.%The%two%things%are%not% mutually%exclusive%–%they%are%two%different%tools%for%different%jobs.

  3. Common%Themes%on%Stack%Overflow 1. “My&God,&it’s&full&of&vars” 2. Also&!&and&as&!"#$%& 3. Obj;C&interop&can&be&quite&surprising 4. People&love&doing&stuff&dynamically&at&runGme

  4. As#@OldManKris#pointed#out#to#me,#F##is#nice#in#making#their#var# "let mutable",#since#it#means#lazy#people#do#the#right#thing#by# default. Swi$%1.2,%of%course,%makes%as%a%lot%safer%by%changing%the% explodina>ng%version%to%as!% When%I%say%the%interop%can%be%surprising,%I%don‘t%think%this%is%a% problem,%just%the%inevitable%result%of%the%tradeoff%between%seamless% interop%and%magical%type>coersion.%Unfortunately%it%has%the%effect% of%seAng%up%expectaCons%for%beginners%that%you%can%as%between%

    types.%I%definitely%like%how%1.2%requires%more%of%these%casts%to%be% explicit.
  5. Second'Most'Popular'SO'Swi4'Ques6on How$do$I$make$this$work? func anymorph<T>(a: Any, type: T.Type, f: T->()) {

    if let t = a as? T { f(t) } }
  6. “Second(most,popular”,based,on,my,non(scien6fic,observa6ons.1, The$goal$here$is$to$build$an$array$of$types$and$func5ons,$and$then$ throw$a$value$and$that$array$at$this$func5on$and$have$it$figure$it$all$ out.$Unfortunately,$this$technique$doesn‘t$work$quite$as$hoped.$The$ type$of$T$is$determined$at$compile$5me$(that's$how$generics$work),$ but$the$ques5oner$is$looking$for$a$runC5me$solu5on$that$takes$a$T$ argument. 1"The"most"popular"ques0on"being"“Why"doesn’t"this"[AnyObject]?"type"have"a"subscript"operator?”

  7. Screw&you,&Swi,! // I wish this was JavaScript var foo: AnyObject!

    —"@nicklockwood
  8. Eventually*the*ques.oner*gets*disheartened*when*they*discover* this.*They*some.mes*decide*Swi:*generics*"don‘t*work",*and*that* s.cking*with*AnyObject*is*the*way*forward.

  9. Is#this#code#“Swi.”? @objc class C { func doThing() { println("something") }

    } @objc class D { func doThing() { println("something else") } } func takeThing(o: AnyObject) { o.doThing() } takeThing(C()) takeThing(D())
  10. Classes&C&and&D&have&nothing&in&common&but&the&name&of&a& method,&yet&you&can&pass&either&into&a&func8on&and&have&it&work.&It& does&this&through&the&magic&of&objc_MsgSend,&the&same&as&with& Objec8ve?C&code.&

  11. Dynamic(Typing ...like&other&built-in&language&pa2erns&like&inheritance,&is&useful& when&you&need&it. If#I#do#more#at#compile#/me,#not#run/me,#what's#in#it#for#me?

  12. The$inheritance$analogy$refers$to$the$long1standing$tradi3on$of$ people$using$inheritance$(is1a)$when$they$want$aggrega3on$(is1 implemented1in1terms1of).$“I$want$a$library$class$where$you$can$ look$up$books$by$3tle,$so$I’ll$inherit$from$Dic3onary”.$For$as$long$as$ inheritance$has$been$a$thing,$people$have$been$doing$this,$and$ people$have$been$wri3ng$about$how$you$shouldn't$do$this.$No1one$ is$saying$don’t$use$inheritance$and$classes2,$just$to$only$use$it$when$ the$problem$you$need$to$solve$calls$for$it.$The$same$goes$for$ dynamic$types$–$break$them$out$when$the$problem$will$benefit$ from$them.

    2"This"is"a"lie,"plenty"of"people"(smart"people)"are"saying"this."See"Rob"Rix’s"great"talk"on"this"from"NSScotland.
  13. None
  14. There‘s'now'a'long'list'of'great'talks'about'how'using'immutable' value'types'instead'can'help'reduce'the'complexity'of'your'code' and'make'it'easier'to'reason'about.' The$downside$of$these$talks$is$they're$a$li3le$bit...$“Programming,$ You're$Doing$It$Wrong”$which,$while$it$might$be$true,$can$be$a$hard$ pill$to$swallow.$So$how$about$a$more$sugarDcoated$one$–$staFc$ value$types$and$compileDFme$opFmizaFon$have$the$potenFal$to$ make$your$code$faster! In#case#you#don’t#recognize#him,#that's#John#McCarthy,#inventor#of# Lisp.

  15. How$many$Haskell$programmers$ does$it$take$to$change$a$lightbulb? None,$you$need$a$new$house$with$a$ different$lightbulb.

  16. This%is%a%cheap%shot,%of%course.%Haskell%can%be%fast. The$joke$comes$from$this$ar0cle$on$the$F##for#Fun#and#Profit$blog,$ which$is$well$worth$a$read.

  17. Four%character+Codes Pack%four%character%bytes%into%a%324bit%integer: "utxt" = 'u'<<24 | 't'<<16 | 'x'<<8 |

    't' = 1,970,567,284
  18. Credit'to'@UINT_MIN'for'the'FourCharacterCode'example,' which'is'a'much'more'useful'case'than'example'I'had'originally,' which'was'10'applicaAons'of'the'XorshiC'generator,'which'also' constantDfolds'at'compile'Ame: struct Seed { let x, y,

    z, w: UInt32 } func xrand(seed: Seed) -> Seed { let t = seed.x ^ (seed.x << 11) return Seed(x: seed.y, y: seed.z, z: seed.w, w: seed.w ^ (seed.w >> 19) ^ (t ^ (t >> 8))) } var r = Seed(x: 2014, y: 12, z: 29, w: 2015) let x = reduce(0..<10, r) { r, _ in xrand(r) } println(String(x.w, radix: 16))
  19. The$Xorshi*$example$is$interes2ng$as$it$comes$from$a$ benchmarking$ques2on$on$Stack$Overflow.$It’s$an$easy$trap$to$fall$ into$when$you’re$wri2ng$a$benchmarking$test$that$your$code$isn’t$ actually$running$because$your$inputs$are$constant$and$the$func2on$ you$want$to$test$isn't$running$at$all!$If$the$results$are$too$good$to$be$ true,$try$randomizing$your$input$and$running$them$again. In#the#original#ques0on’s#code,#they#were#using#a#closure#to#capture# variables#that#held#the#state#between#itera0ons.#Closures#like#this# can#also#be#a#barrier#to#compiler#op0miza0on.#Once#that#was# removed,#SwiA#handily#beat#the#Delphi#version.

  20. Four%character+Codes extension FourCharCode { init(fromFourCharString str: StaticString) { precondition(str.byteSize ==

    4) self = str.withUTF8Buffer { buf in var result: FourCharCode = 0 for byte in buf { result <<= 8 result |= FourCharCode(byte) } return result } } }
  21. Four%character+Codes let utxt = FourCharCode(fromFourCharString: "utxt") println("0x" + String(utxt, radix:

    16)) // prints 0x75747874
  22. The$radix$version$of$String.init$is$very$convenient$if$you're$ debugging$bitshi4ing$code$(use$radix: 2$to$see$the$binary$ equivalent). That%screenshot%is%from%Hopper%which%is%a%very%useful%tool%for% examining%assembly%output.%Alterna;vely%you%can%run%swiftc%with% -emit-assembly,%but%hopper%adds%a%lot%of%features%that%make%it% easier%to%view.

  23. Using&reduce extension FourCharCode { init(fromFourCharString str: StaticString) { precondition(str.byteSize ==

    4) self = str.withUTF8Buffer { buf in reduce(buf, 0) { code, byte in (code << 8) | FourCharCode(byte) } } } }
  24. It#could#be#considered#a#bit#iffy#to#extend#FourCharCode#with#an# init#like#this,#as#it’s#just#a#typealias#for#UInt32#–#so#UInt32#now# has#this#method.#But#that's#not#a#huge#deal#since#you#invoke#it#with# an#explicit#argument#name.#But#it's#definitely#a#reason#not#to#use# IntegerLiteralConvertible.#The#other#reason#being,#that# seems#to#defeat#the#constant#folding#too#(even#if#you#typealias# StringLiteralType#to#be#StaticString)

  25. Using&String extension FourCharCode { init(fromFourCharString str: String) { precondition(str.byteSize ==

    4) self = reduce(str.utf8, 0) { code, byte in (code << 8) | UInt32(byte) } } }
  26. Nibble&Sort&Challenge Write&a&func,on&that&sorts&all&the&nibbles&in&a&644bit&word. nibble_sort_word(0xbadbeef) // returns 0xfeedbba000000000

  27. I"wrote"an"ar*cle"covering"this"challenge"and"the"Swi4" implemen*on"if"you"want"more"details.

  28. func read_nibble(w: UInt64, i: UInt64) -> UInt64 { let res

    = w >> (i * 4) return res & 0xf } func write_nibble(inout w: UInt64, i: UInt64, v: UInt64) { var mask: UInt64 = 0xf mask <<= (i * 4); w &= ~mask var prom = v; prom <<= (i * 4) w |= prom }
  29. func nibble_sort_word(var arg: UInt64) -> UInt64 { for var i:

    UInt64 = 0; i < 16; ++i { var min = i; for var j = i+1; j < 16; ++j { if read_nibble(arg, j) < read_nibble(arg, min) { min = j } } if (min != i) { var tmp = read_nibble(arg, i) write_nibble(&arg, i, read_nibble(arg, min)) write_nibble(&arg, min, tmp) } } return arg; }
  30. struct Nibbles: MutableCollectionType { var val: UInt64 var startIndex: UInt64

    { return 0 } var endIndex: UInt64 { return 16 } subscript(idx: UInt64) -> UInt64 { get { return (val >> (idx*4)) & 0xf } set(n) { let mask = 0xf << (idx * 4) val &= ~mask val |= n << (idx * 4) } } } // + generator impl.
  31. Now$use$the$standard$library’s$sort var col = Nibbles(val: 0xbadbeef) sort(&col) col.val // is

    0xfeedbba000000000 sizeof(UInt64) == sizeof(Nibbles) write/read/sort!version:!400µs sort(Nibbles)!version:!270µs
  32. The$Swi($standard$library’s$sort$appears$to$be$an$implementa6on$ of$an$introsort$(which$is$itself$a$hybrid$of$quicksort$and$mergesort),$ but$that$drops$to$an$inser6on$sort$if$the$collec6on$to$be$sorted$is$ sufficiently$small$for$that$to$be$faster.$The$LLVM$C++$std$lib$ implementa6on$does$something$similar.

  33. Zero%cost)Abstrac-ons struct Int { var value: Builtin.Int64 } func +(lhs:

    Int, rhs: Int) -> Int { precondition(no overflow) }
  34. Zero%cost)Abstrac-ons func f(x: P) { } versus func f<T: P>(x:

    T) { }
  35. For$more$on$the$differences$between$generics$and$func5ons$that$ take$protocols$as$arguments,$see$my$post;talk$ar5cle.

  36. Zero%cost)Abstrac-ons func f(x: P) { } versus func f(x: Int)

    { }
  37. You$can$write$your$own$fast$generic$func1ons! func insertionsort <C: MutableCollectionType where C.Index: RandomAccessIndexType, C.Generator.Element: Comparable>

    (inout source: C) { for i in indices(source) { for var j=i; j!=source.startIndex && source[j]<source[j-1]; j-- { swap(&source[j-1], &source[j]) } } } This%version:%420µs%!
  38. Don’t&uninten*onally&rewrite&std&lib&func*ons! func insertionsort<C: MutableCollectionType where C.Index: RandomAccessIndexType, C.Generator.Element: Comparable> (inout

    source: C) { if isEmpty(source) { return } for i in source.startIndex.successor()..<source.endIndex { let x = source[i] var j = i while j != source.startIndex { let k = j.predecessor() let cmp = source[k] if x < cmp { source[j] = cmp j = k } else { break } } source[j] = x } }
  39. Stay%Familiar%With%the%Swi/%Standard%Library developer.apple.com/swi0/blog Xcode&and&Swi,&Release&Notes swi$doc.org airspeedvelocity.net