Hi.
I need a number of 2-dimensional data types that can be iterated on. For those, I thought of having a 2-dimensional "QuadraticSequence" protocol, joining 2 1-dimensional "Sequence" protocol:
public protocol QuadraticSequence: Sequence {
associatedtype LinearSequence: Sequence
var sequences: (LinearSequence, LinearSequence) { get }
static func combineDimensions(_: LinearSequence.Element, _: LinearSequence.Element) -> Element
}
then implementing makeIterator() as a protocol extension of QuadraticSequence,
then add compliance to QuadraticSequence to 2-dimensional types, such as this 2-d index range:
public struct NQuadraticIndexRange{
public let rows, columns: Int
}
extension NQuadraticIndexRange: QuadraticSequence {
public typealias Element = (Int, Int)
public typealias LinearSequence = Range<Int>
public typealias Iterator = QuadraticIterator<NQuadraticIndexRange>
public var sequences: (Range<Int>, Range<Int>) { return (0..<rows, 0..<columns) }
}
It all goes well and I can add quadratic iteration to all my quadratic types very easily.
Howerer, when I tested the execution speed of this versus making specific iterators, I see that it is 30x slower with the generic protocol implementation (optimized compiler settings). I suspect dynamic dispatch of some sort in the inner loop of the iterator. It seems strange, as I would have thought that the compiler has all necessary data to inline everything and be as fast as direct code. (added iterator below for reference)
At this time I'm reverting to direct implementation of Sequence for the quadratic types, but I'd be interested to know what is making it that slow, and if there are solutions to have an elegant, single generic implementation instead of multiple ones.
Thank you.
public struct QuadraticIterator<Sequence: QuadraticSequence>: IteratorProtocol {
let sequence: Sequence
var iterators: (Sequence.LinearSequence.Iterator, Sequence.LinearSequence.Iterator)
var current: (Sequence.LinearSequence.Element, Sequence.LinearSequence.Element)
var done: Bool
init(sequence s: Sequence) {
sequence = s
iterators = (sequence.sequences.0.makeIterator(), sequence.sequences.1.makeIterator())
if let i0 = iterators.0.next(), let i1 = iterators.1.next() {
done = false
current.0 = i0
current.1 = i1
} else {
done = true
current.0 = Sequence._defaultLinearSequenceElement()
current.1 = Sequence._defaultLinearSequenceElement()
}
}
public mutating func next() -> Sequence.Element? {
guard !done else { return nil }
let loc = Sequence.combineDimensions(current.0, current.1)
if let i1 = iterators.1.next() {
current.1 = i1
} else {
if let i0 = iterators.0.next() {
current.0 = i0
iterators.1 = sequence.sequences.1.makeIterator()
current.1 = iterators.1.next()!
} else {
done = true
}
}
return loc
}