Skip to content

Instantly share code, notes, and snippets.

@jepers
Last active January 26, 2016 03:50
Show Gist options
  • Save jepers/72417e6eb84632b59239 to your computer and use it in GitHub Desktop.
Save jepers/72417e6eb84632b59239 to your computer and use it in GitHub Desktop.
Example of something that takes a long time to type check.
// This little program takes ~ 6 seconds to compile on my MacBook Pro Late 2013.
// It compiles and works as expected. But the type checker spends a lot of time
// on the function at the end.
// In my actual project, I have much more code like this, and it results in very
// long compile times. So I'd like to know if there is something I can do to
// speed that up.
protocol DefaultInitializable { init() }
extension Float : DefaultInitializable {}
protocol CountType { static var value: Int { get } }
struct Count0 : CountType { static var value: Int { return 0 } }
struct CountSuccesorOf<T: CountType> : CountType { static var value: Int { return T.value + 1 } }
extension CountType {
typealias Plus1 = CountSuccesorOf<Self>
typealias Plus2 = Plus1.Plus1
typealias Plus3 = Plus1.Plus2
typealias Plus4 = Plus1.Plus3
}
typealias Count1 = Count0.Plus1
typealias Count2 = Count0.Plus2
typealias Count3 = Count0.Plus3
typealias Count4 = Count0.Plus4
protocol StaticArrayType : DefaultInitializable, CollectionType {
typealias Element: DefaultInitializable
typealias Count: CountType
//typealias Successor = StaticArraySuccessorOf<Self> // (As a side note: This line has to be in an extension, or debug-built binary will crash (while release-built binary works as expected). Filed radar 23602584 about this.)
}
extension StaticArrayType {
typealias Successor = StaticArraySuccessorOf<Self> // (As a side note about a side note: See comment above.)
typealias Successor2 = Successor.Successor
typealias Successor3 = Successor2.Successor
}
// A concrete static array type with one element of type E.
struct StaticArray<E: DefaultInitializable> : StaticArrayType {
typealias Element = E
typealias Count = Count1
var storage = Element()
typealias WithCount1 = StaticArray
typealias WithCount2 = StaticArray.Successor
typealias WithCount3 = StaticArray.Successor2
typealias WithCount4 = StaticArray.Successor3
}
// A concrete static array type with one more element than its predecessor P.
struct StaticArraySuccessorOf<P: StaticArrayType> : StaticArrayType {
typealias Predecessor = P
typealias Element = Predecessor.Element
typealias Count = Predecessor.Count.Plus1
var storage = (Element(), Predecessor())
}
// Default implementation of conformance to CollectionType:
extension StaticArrayType where Index == Int, Generator.Element == Element {
var startIndex: Int { return 0 }
var endIndex: Int { return Count.value }
func generate() -> IndexingGenerator<Self> { return IndexingGenerator(self) }
subscript(index: Int) -> Generator.Element {
get { precondition(index >= 0 && index < count)
var selfCopy = self; return withUnsafePointer(&selfCopy, { UnsafePointer<Generator.Element>($0)[index] }) }
set { precondition(index >= 0 && index < count)
withUnsafeMutablePointer(&self) { UnsafeMutablePointer<Generator.Element>($0)[index] = newValue }
}
}
}
// Conformance to CustomStringConvertible:
extension StaticArray : CustomStringConvertible {}
extension StaticArraySuccessorOf : CustomStringConvertible {}
extension StaticArrayType {
var description: String {
var a: [String] = []
for i in self.indices { a.append("\(self[i])") }
return "(" + a.joinWithSeparator(", ") + ")"
}
}
// This is the function that (together with the above type definitions) demonstrates
// the type of thing on which the compiler spends nearly all of its time type checking.
// (As can be seen when compiling with -Xfrontend -debug-time-function-bodies)
func functionThatTakesTimeToTypeCheck() {
typealias Float4 = StaticArray<Float>.WithCount4
typealias Float4x4 = StaticArray<Float4>.WithCount4
var a = Float4x4()
for row in a.indices {
for col in a[row].indices {
a[row][col] = Float(row) + Float(col) / 10.0
}
}
print(a)
print(strideof(Float4x4))
}
functionThatTakesTimeToTypeCheck()
//----------------------------------------------------------------------------
// Example of compiling it:
//----------------------------------------------------------------------------
// $ time swiftc -O -Xfrontend -debug-time-function-bodies test.swift
// 0.1ms test.swift:10:51 get {}
// 0.0ms test.swift:10:8 init()
// 0.6ms test.swift:11:74 get {}
// 0.0ms test.swift:11:8 init()
// 0.0ms test.swift:34:8 init()
// 0.0ms test.swift:44:8 init()
// 0.0ms test.swift:52:25 get {}
// 0.0ms test.swift:53:23 get {}
// 0.2ms test.swift:54:10 func generate() -> IndexingGenerator<Self>
// 2.3ms test.swift:56:9 get {}
// 1.5ms test.swift:58:9 set {}
// 9.2ms test.swift:67:29 get {}
// 5229.8ms test.swift:76:6 func functionThatIsSlowCompileSlashTypeCheck()
// 0.0ms <invalid loc> init()
//
// real 0m6.282s
// user 0m6.028s
// sys 0m0.253s
//----------------------------------------------------------------------------
@jepers
Copy link
Author

jepers commented Nov 20, 2015

Removing the inheritance from CollectionType from the StaticArrayType protocol makes this example program compile fast (blink of an eye instead of 6 s), here's the modified program:

protocol DefaultInitializable { init() }
extension Float : DefaultInitializable {}

protocol CountType { static var value: Int { get } }
struct Count0 : CountType { static var value: Int { return 0 } }
struct CountSuccesorOf<T: CountType> : CountType { static var value: Int { return T.value + 1 } }
extension CountType {
typealias Plus1 = CountSuccesorOf
typealias Plus2 = Plus1.Plus1
typealias Plus3 = Plus1.Plus2
typealias Plus4 = Plus1.Plus3
}
typealias Count1 = Count0.Plus1
typealias Count2 = Count0.Plus2
typealias Count3 = Count0.Plus3
typealias Count4 = Count0.Plus4

protocol StaticArrayType : DefaultInitializable, CustomStringConvertible {
typealias Element: DefaultInitializable
typealias Count: CountType
//typealias Successor = StaticArraySuccessorOf // (As a side note: This line has to be in an extension, or debug-built binary will crash (while release-built binary works as expected). Filed radar 23602584 about this.)
}
extension StaticArrayType {
typealias Successor = StaticArraySuccessorOf // (As a side note about a side note: See comment above.)
typealias Successor2 = Successor.Successor
typealias Successor3 = Successor2.Successor
}
// A concrete static array type with one element of type E.
struct StaticArray<E: DefaultInitializable> : StaticArrayType {
typealias Element = E
typealias Count = Count1
var storage = Element()
typealias WithCount1 = StaticArray
typealias WithCount2 = StaticArray.Successor
typealias WithCount3 = StaticArray.Successor2
typealias WithCount4 = StaticArray.Successor3
}
// A concrete static array type with one more element than its predecessor P.
struct StaticArraySuccessorOf<P: StaticArrayType> : StaticArrayType {
typealias Predecessor = P
typealias Element = Predecessor.Element
typealias Count = Predecessor.Count.Plus1
var storage = (Element(), Predecessor())
}
extension StaticArrayType {
var count: Int { return Count.value }
var indices: Range { return 0 ..< count }
subscript(index: Int) -> Element {
get { precondition(index >= 0 && index < count)
var selfCopy = self; return withUnsafePointer(&selfCopy, { UnsafePointer($0)[index] }) }
set { precondition(index >= 0 && index < count)
withUnsafeMutablePointer(&self) { UnsafeMutablePointer($0)[index] = newValue }
}
}
var description: String {
var a: [String] = []
for i in self.indices { a.append("(self[i])") }
return "(" + a.joinWithSeparator(", ") + ")"
}
}

func functionThatTakesTimeToTypeCheck() {
typealias Float4 = StaticArray.WithCount4
typealias Float4x4 = StaticArray.WithCount4
var a = Float4x4()
for row in a.indices {
for col in a[row].indices {
a[row][col] = Float(row) + Float(col) / 10.0
}
}
print(a)
print(strideof(Float4x4))
}
functionThatTakesTimeToTypeCheck()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment