<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div></div><div>Perhaps the optimizer unrolls the inner loop, and thus can skip safety checks. Naively, seems trickier to do for the iterator.&nbsp;</div><div><br>El ene. 4, 2017, a las 9:10 PM, Jens Persson via swift-users &lt;<a href="mailto:swift-users@swift.org">swift-users@swift.org</a>&gt; escribió:<br><br></div><blockquote type="cite"><div><div dir="ltr">I noticed disabling safety checks made the custom Iterator as fast as the two nested for-loops.<div>Karl, how does your test change when disabling safety checks?</div><div><br><div>Anyone having an idea why disabling safety checks should make the two sum funcs equally fast in my example program?</div><div><br></div><div>(It shouldn't be the preconditions of Table2D&lt;&gt;'s subscripts, unless the optimizer (with safety checks) can remove them in the case of the nested for-loops but not in the case of the custom iterator.)&nbsp;</div></div><div><br></div><div>/Jens</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 4, 2017 at 6:59 PM, Jens Persson <span dir="ltr">&lt;<a href="mailto:jens@bitcycle.com" target="_blank">jens@bitcycle.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Thanks, I wonder if it is currently impossible to make it as fast as the nested for loops, ie that some optimizer improvement could fix it.<div>Here's a stripped down version of my code:</div><div><br></div><div><div><br></div><div>import QuartzCore // This is just for timing using CACurrentMediaTime()</div><div><br></div><div><br></div><div>struct Point2DInt {</div><div>&nbsp; &nbsp; var x: Int</div><div>&nbsp; &nbsp; var y: Int</div><div>}</div><div><br></div><div>struct Size2DInt {</div><div>&nbsp; &nbsp; var width: Int</div><div>&nbsp; &nbsp; var height: Int</div><div>&nbsp; &nbsp; var rect: Rect2DInt { return Rect2DInt(x: 0, y: 0, width: width, height: height) }</div><div>}</div><div><br></div><div>struct Rect2DInt : Sequence {</div><div>&nbsp; &nbsp; var description: String { return "(\(origin), \(size))" }</div><div>&nbsp; &nbsp; var origin: Point2DInt</div><div>&nbsp; &nbsp; var size: Size2DInt</div><div>&nbsp; &nbsp; var minX: Int {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get { return origin.x }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set { size.width = maxX - newValue; origin.x = newValue }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; var maxX: Int {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get { return origin.x + size.width }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set { size.width = newValue - minX }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; var minY: Int {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get { return origin.y }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set { size.height = maxY - newValue; origin.y = newValue }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; var maxY: Int {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get { return origin.y + size.height }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set { size.height = newValue - minY }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; init(origin: Point2DInt, size: Size2DInt) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.origin = origin</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.size = size</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; init(x: Int, y: Int, width: Int, height: Int) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.init(origin: Point2DInt(x: x, y: y), size: Size2DInt(width: width, height: height))</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; init(minX: Int, minY: Int, maxX: Int, maxY: Int) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.init(origin: Point2DInt(x: minX, y: minY),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; size: Size2DInt(width: maxX - minX, height: maxY - minY))</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; func makeIterator() -&gt; Rect2DIntPointIterator {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return Rect2DIntPointIterator(rect: self)</div><div>&nbsp; &nbsp; }</div><div>}</div><div><br></div><div><br></div><div>// This is the crucial type here, this version is the fastest that</div><div>// I could find, but it's still slower than two nested for loops,</div><div>// see test below.</div><span class=""><div>struct Rect2DIntPointIterator : IteratorProtocol, Sequence {<br></div><div>&nbsp; &nbsp; let startX, startY, stopX, stopY: Int</div></span><div>&nbsp; &nbsp; var currentPoint: Point2DInt</div><div>&nbsp; &nbsp; init(rect: Rect2DInt) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; currentPoint = rect.origin</div><span class=""><div>&nbsp; &nbsp; &nbsp; &nbsp; startX = rect.origin.x</div><div>&nbsp; &nbsp; &nbsp; &nbsp; startY = rect.origin.y</div><div>&nbsp; &nbsp; &nbsp; &nbsp; stopX = rect.maxX</div><div>&nbsp; &nbsp; &nbsp; &nbsp; stopY = rect.maxY</div><div>&nbsp; &nbsp; }</div></span><span class=""><div>&nbsp; &nbsp; mutating func next() -&gt; Point2DInt? {</div></span><div>&nbsp; &nbsp; &nbsp; &nbsp; defer { currentPoint.x = currentPoint.x &amp;+ 1 }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if currentPoint.x == stopX {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentPoint.x = startX</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentPoint.y = currentPoint.y &amp;+ 1</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if currentPoint.y == stopY { return nil }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return currentPoint</div><div>&nbsp; &nbsp; }</div><div>}</div><div><br></div><div><br></div><div>struct Table2D&lt;Element&gt; {</div><div>&nbsp; &nbsp; let size: Size2DInt</div><div>&nbsp; &nbsp; var storage: [Element]</div><div>&nbsp; &nbsp; init(size: Size2DInt, filledWith element: Element) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; precondition(size.width &gt; 0 &amp;&amp; size.height &gt; 0)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.size = size</div><div>&nbsp; &nbsp; &nbsp; &nbsp; self.storage = [Element](repeating: element, count: size.width * size.height)</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; subscript(x: Int, y: Int) -&gt; Element {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; precondition(x &gt;= 0 &amp;&amp; y &gt;= 0 &amp;&amp; x &lt; size.width &amp;&amp; y &lt; size.height)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return storage[x + y * size.width]</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; precondition(x &gt;= 0 &amp;&amp; y &gt;= 0 &amp;&amp; x &lt; size.width &amp;&amp; y &lt; size.height)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; storage[x + y * size.width] = newValue</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; subscript(position: Point2DInt) -&gt; Element {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; get { return self[position.x, position.y] }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; set { self[position.x, position.y] = newValue }</div><div>&nbsp; &nbsp; }</div><div>}</div><div><br></div><div>func randomDouble() -&gt; Double {</div><div>&nbsp; &nbsp; // Returns a random Double in the range [0, 1)</div><div>&nbsp; &nbsp; let ui64 = (UInt64(arc4random()) &lt;&lt; 32) | UInt64(arc4random())</div><div>&nbsp; &nbsp; return Double(ui64 &gt;&gt; UInt64(63 - Double.significandBitCount)) * .ulpOfOne/2</div><div>}</div><div>func randomInt(from: Int, to: Int) -&gt; Int {</div><div>&nbsp; &nbsp; // Returns an Int in the range [from, to)</div><div>&nbsp; &nbsp; return Int(Double(from) + (randomDouble() * Double(to - from)).rounded(.down))</div><div>}</div><div><br></div><div>func randomSubrects(of rect: Rect2DInt, minSize: Size2DInt, count: Int) -&gt; [Rect2DInt] {</div><div>&nbsp; &nbsp; precondition(count &gt; 0 &amp;&amp; minSize.width &gt; 0 &amp;&amp; minSize.height &gt; 0)</div><div>&nbsp; &nbsp; var subrects = [Rect2DInt]()</div><div>&nbsp; &nbsp; subrects.reserveCapacity(<wbr>count)</div><div>&nbsp; &nbsp; for _ in 0 ..&lt; count {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; let size = Size2DInt(width: randomInt(from: minSize.width, to: rect.size.width),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;height: randomInt(from: minSize.height, to: rect.size.height))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; let origin = Point2DInt(x: randomInt(from: 0, to: rect.size.width - size.width),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; y: randomInt(from: 0, to: rect.size.height - size.height))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; subrects.append(Rect2DInt(<wbr>origin: origin, size: size))</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; return subrects</div><div>}</div><div><br></div><div><br></div><div>func randomTable(size: Size2DInt) -&gt; Table2D&lt;Double&gt; {</div><div>&nbsp; &nbsp; var table = Table2D(size: size, filledWith: 0.0)</div><div>&nbsp; &nbsp; for p in table.size.rect { table[p] = randomDouble() }</div><div>&nbsp; &nbsp; return table</div><div>}</div><div><br></div><div><br></div><div>func sum1(areas: [Rect2DInt], of table: Table2D&lt;Double&gt;) -&gt; Double {</div><div>&nbsp; &nbsp; var sum = 0.0</div><div>&nbsp; &nbsp; for r in areas {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; // Using custom iterator:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for p in r { sum += table[p] }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; return sum</div><div>}</div><div><br></div><div>func sum2(areas: [Rect2DInt], of table: Table2D&lt;Double&gt;) -&gt; Double {</div><div>&nbsp; &nbsp; var sum = 0.0</div><div>&nbsp; &nbsp; for r in areas {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; // Using two nested for loops:</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for y in r.minY ..&lt; r.maxY {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for x in r.minX ..&lt; r.maxX {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sum = sum + table[x, y]</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; return sum</div><div>}</div><div><br></div><div>func test(</div><div>&nbsp; &nbsp; sumFn: ([Rect2DInt], Table2D&lt;Double&gt;) -&gt; Double,</div><div>&nbsp; &nbsp; label: String,</div><div>&nbsp; &nbsp; table: Table2D&lt;Double&gt;,</div><div>&nbsp; &nbsp; areas: [Rect2DInt]</div><div>&nbsp; &nbsp; )</div><div>{</div><div>&nbsp; &nbsp; let t0 = CACurrentMediaTime()</div><div>&nbsp; &nbsp; let sum = sumFn(areas, table)</div><div>&nbsp; &nbsp; let t1 = CACurrentMediaTime()</div><div>&nbsp; &nbsp; print(label, t1 - t0, "seconds (sum \(sum))")</div><div>}</div><div><br></div><div>for _ in 0 ..&lt; 4 {</div><div>&nbsp; &nbsp; let rndTable = randomTable(size: Size2DInt(width: 1000, height: 1000))</div><div>&nbsp; &nbsp; let rndTableAreas = randomSubrects(of: rndTable.size.rect,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;minSize: Size2DInt(width: 100, height: 100),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;count: 1000)</div><div>&nbsp; &nbsp; test(sumFn: sum1, label: "sum1 - using custom iterator &nbsp;", table: rndTable, areas: rndTableAreas)</div><div>&nbsp; &nbsp; test(sumFn: sum2, label: "sum2 - using nested for-loops ", table: rndTable, areas: rndTableAreas)</div><div>&nbsp; &nbsp; print()</div><div>}</div><div><br></div><div>//</div><div>// Typical output on my MacBook Pro (Retina, 15-inch, Late 2013):</div><div>//</div><div>// sum1 - using custom iterator &nbsp; 0.480134483019356 seconds (sum 153408603.850653)</div><div>// sum2 - using nested for-loops &nbsp;0.348341046017595 seconds (sum 153408603.850653)</div><div>//&nbsp;</div><div>// sum1 - using custom iterator &nbsp; 0.426998238021042 seconds (sum 149851816.622638)</div><div>// sum2 - using nested for-loops &nbsp;0.34111139801098 seconds (sum 149851816.622638)</div><div>//&nbsp;</div><div>// sum1 - using custom iterator &nbsp; 0.466021075990284 seconds (sum 155267702.297466)</div><div>// sum2 - using nested for-loops &nbsp;0.351970263000112 seconds (sum 155267702.297466)</div><div>//&nbsp;</div><div>// sum1 - using custom iterator &nbsp; 0.426723245007452 seconds (sum 146331850.202214)</div><div>// sum2 - using nested for-loops &nbsp;0.340267747989856 seconds (sum 146331850.202214)</div><div>//&nbsp;</div></div><div><br></div></div><div class="HOEnZb"><div class="h5"><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 4, 2017 at 12:42 PM, Karl <span dir="ltr">&lt;<a href="mailto:razielim@gmail.com" target="_blank">razielim@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">Hmmm that’s interesting. A brief test I ran:<div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><font face="Courier">import CoreGraphics</font></div><div><font face="Courier">import Foundation</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">struct PointIterator {</font></div><div><font face="Courier">&nbsp; let rect: CGRect</font></div><div><font face="Courier">&nbsp; var nextPoint: CGPoint</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">&nbsp; let maxX: CGFloat</font></div><div><font face="Courier">&nbsp; let maxY: CGFloat</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">&nbsp; init(rect: CGRect) {</font></div><div><font face="Courier">&nbsp; &nbsp; self.rect &nbsp; &nbsp; &nbsp;= rect.standardized</font></div><div><font face="Courier">&nbsp; &nbsp; self.nextPoint = self.rect.origin</font></div><div><font face="Courier">&nbsp; &nbsp; // Cache for fast iteration</font></div><div><font face="Courier">&nbsp; &nbsp; self.maxX &nbsp; &nbsp; &nbsp;= self.rect.maxX</font></div><div><font face="Courier">&nbsp; &nbsp; self.maxY &nbsp; &nbsp; &nbsp;= self.rect.maxY</font></div><div><font face="Courier">&nbsp; }</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">&nbsp; mutating func next() -&gt; CGPoint? {</font></div><div><font face="Courier">&nbsp; &nbsp; guard nextPoint.x &lt;= maxX, nextPoint.y &lt;= maxY else {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; return .none</font></div><div><font face="Courier">&nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; &nbsp; defer {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; nextPoint.x += 1</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; if nextPoint.x &gt; maxX {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; &nbsp; nextPoint.x &nbsp;= rect.origin.x</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; &nbsp; nextPoint.y += 1</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; &nbsp; return nextPoint</font></div><div><font face="Courier">&nbsp; }</font></div><div><font face="Courier">}</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">// Use iterator</font></div><div><font face="Courier">func iteratePoints_it(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div><font face="Courier">&nbsp; var it = PointIterator(rect: rect)</font></div><div><font face="Courier">&nbsp; while let point = it.next() {</font></div><div><font face="Courier">&nbsp; &nbsp; with(point)</font></div><div><font face="Courier">&nbsp; }</font></div><div><font face="Courier">}</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">// Basic unwrapping of the iterator as a function (no&nbsp;‘defer’)</font></div><div><font face="Courier">func iteratePoints_fe(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div><font face="Courier">&nbsp; let rect &nbsp; &nbsp; &nbsp;= rect.standardized</font></div><div><font face="Courier">&nbsp; var nextPoint = rect.origin</font></div><div><font face="Courier">&nbsp; let maxX &nbsp; &nbsp; &nbsp;= rect.maxX</font></div><div><font face="Courier">&nbsp; let maxY &nbsp; &nbsp; &nbsp;= rect.maxY</font></div><div><font face="Courier">&nbsp;&nbsp;</font></div><div><font face="Courier">&nbsp; while true {</font></div><div><font face="Courier">&nbsp; &nbsp; guard nextPoint.x &lt;= maxX, nextPoint.y &lt;= maxY else {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; return</font></div><div><font face="Courier">&nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; &nbsp; with(nextPoint)&nbsp;</font></div><div><font face="Courier">&nbsp; &nbsp; nextPoint.x += 1</font></div><div><font face="Courier">&nbsp; &nbsp; if nextPoint.x &gt; maxX {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; nextPoint.x &nbsp;= rect.origin.x</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; nextPoint.y += 1</font></div><div><font face="Courier">&nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; }&nbsp;</font></div><div><font face="Courier">}</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">// for..in loop</font></div><div><font face="Courier">func iteratePoints_fe2(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div><font face="Courier">&nbsp; let rect = rect.standardized</font></div><div><font face="Courier">&nbsp; let maxX = rect.maxX</font></div><div><font face="Courier">&nbsp; let maxY = rect.maxY</font></div><div><font face="Courier">&nbsp; for y in stride(from: rect.origin.y, to: maxY, by: 1) {</font></div><div><font face="Courier">&nbsp; &nbsp; for x in stride(from: rect.origin.x, to: maxX, by: 1) {</font></div><div><font face="Courier">&nbsp; &nbsp; &nbsp; with(CGPoint(x: x, y: y))</font></div><div><font face="Courier">&nbsp; &nbsp; }</font></div><div><font face="Courier">&nbsp; }</font></div><div><font face="Courier">}</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">func profile(_ iterations: Int, _ thing: ()-&gt;()) -&gt; TimeInterval {</font></div><div><font face="Courier">&nbsp; var totalTime: TimeInterval = 0</font></div><div><font face="Courier">&nbsp; for _ in 0..&lt;iterations {</font></div><div><font face="Courier">&nbsp; &nbsp; let start = Date().timeIntervalSinceRefere<wbr>nceDate</font></div><div><font face="Courier">&nbsp; &nbsp; thing()</font></div><div><font face="Courier">&nbsp; &nbsp; totalTime += (Date().timeIntervalSinceRefer<wbr>enceDate - start)</font></div><div><font face="Courier">&nbsp; }</font></div><div><font face="Courier">&nbsp; return totalTime/TimeInterval(iterati<wbr>ons)</font></div><div><font face="Courier">}</font></div><div><font face="Courier"><br></font></div><div><font face="Courier"><br></font></div><div><font face="Courier">let bigRect = CGRect(x: 0, y: 0, width: 10_000, height: 10_000)</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">let iterator = profile(10) { iteratePoints_it(bigRect) &nbsp;{ if $0.x &gt; 1_000_000 { print("?") } } } // always false, won't be optimised out.</font></div><div><font face="Courier">let foreach = profile(10) &nbsp;{ iteratePoints_fe(bigRect) &nbsp;{ if $0.x &gt; 1_000_000 { print("?") } } }</font></div><div><font face="Courier">let foreach2 = profile(10) { iteratePoints_fe2(bigRect) { if $0.x &gt; 1_000_000 { print("?") } } }</font></div><div><font face="Courier">print("iterator: \(iterator) \nforeach: \(foreach) \nforeach2: \(foreach2)")</font></div></blockquote><div><br></div><div>Results:</div><div><br></div><div>iterator: 0.316907703876495&nbsp;<br>foreach: 0.283202117681503&nbsp;<br>foreach2: 0.568318998813629</div><div><br></div><div>That ranking is consistent, too. Using an iterator does appear marginally slower than a basic unwrapping of the iterator in to a function.</div><div><br><div><blockquote type="cite"><div><div class="m_-5733715007301674087h5"><div>On 4 Jan 2017, at 09:56, Jens Persson via swift-users &lt;<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a>&gt; wrote:</div><br class="m_-5733715007301674087m_308211131717086567Apple-interchange-newline"></div></div><div><div><div class="m_-5733715007301674087h5"><div dir="ltr">Hi,<div><br></div><div>I'm working on some low-level pixel processing code (stuff that is not possible to do using standard API or on eg GPU), and I had lots of eg these:</div><div><br></div><div>for y in someStartY ..&lt; someStopY {</div><div>&nbsp; &nbsp; for x in someStartX ..&lt; someStopX {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ... pixels[x, y] ...</div><div>&nbsp; &nbsp; }</div><div>}</div><div><br></div><div>So I implemented some (value) types like eg IntPoint2D, IntSize2D, IntRect2D and I made an IntRect2DIterator so that IntRect2D could be a Sequence over its (discrete) points. With this I could rewrite the above like so:</div><div><br></div><div>for pixelPosAsIntPoint2D in someIntRect2D {</div><div>&nbsp; &nbsp; ... pixels[pixelPosAsIntPoint2D] ...</div><div>}</div><div><br></div><div>For some reason the first version (two nested for loops for x and y) is always a bit faster than the abstracted version no matter how I write it (tried using eg &amp;+ instead of + etc).</div><div><br></div><div><br></div><div><br></div><div>Is it possible to write as a zero cost abstraction like this, if so, how? If not, why?</div><div><br></div><div><br></div><div><br></div><div><br></div><div>PS</div><div><br></div><div>Note that eg this:</div><div><br></div><div><div>for y in someStartY ..&lt; someStopY {</div><div>&nbsp; &nbsp; for x in someStartX ..&lt; someStopX {</div><div>&nbsp; &nbsp; &nbsp; &nbsp;let pixelPosAsIntPoint2D = IntPoint2D(x: x, y: y)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ... pixels[pixelPosAsIntPoint2D] ...</div><div>&nbsp; &nbsp; }</div><div>}</div></div><div><br></div><div>is exactly as fast as the top example (using just ... pixels[x, y] ...). So the difference in execution time seems to be due to something in the Iterator and not eg the pixel accessing subscript taking the 2d int point type instead of separate x and y ints.</div><div><br></div><div>Here is one Iterator variant that I have tested:</div><div><br></div><div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">struct Rect2DIntPointIterator : IteratorProtocol, Sequence {</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; let startX, startY, stopX, stopY: Int</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; var px, py: Int</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; init(rect: Rect2DInt) {</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; startX = rect.origin.x</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; startY = rect.origin.y</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; stopX = rect.maxX</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; stopY = rect.maxY</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; px = startX</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; py = startY</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; }</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; mutating func next() -&gt; Point2DInt? {</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; defer { px = px &amp;+ 1 }</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; if px == stopX {</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; px = startX</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; py = py &amp;+ 1</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if py == stopY { return nil }</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; }</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; &nbsp; &nbsp; return Point2DInt(x: px, y: py)</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">&nbsp; &nbsp; }</span></font></div><div style="margin:0px;line-height:normal"><font face="menlo"><span style="font-size:12px">}</span></font></div></div><div><br></div><div><br></div><div>And here are typical execution times from an example test:</div><div>2.1 seconds using my Iterator (the fastest I can get it, no matter how I try to rewrite it).</div><div>1.5 seconds using nested x, y for loops.</div><div><br></div><div>I'm pretty sure my testing is done thoroughly (measuring average of many runs, using random data, avoiding dead code elimination, whole module optimization, etc).<br></div><div><br></div><div>I have tried profiling the code and looking at the disassmbly but I'm failing to understand what's going on.</div><div><br></div><div>So the ultimate answer would be in the form of a (2d, Int) Rectangle type whose (2d, Int) Points can be iterated in a for loop, at zero cost compared to doing the same using two nested for loops. Or an explanation of why this is impossible.</div><div><br></div><div>DS</div><div><br></div><div>/Jens</div><div><br></div></div></div></div>
______________________________<wbr>_________________<br>swift-users mailing list<br><a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-users" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-users</a><br></div></blockquote></div><br></div></div></blockquote></div><br></div>
</div></div></blockquote></div><br></div>
</div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-users mailing list</span><br><span><a href="mailto:swift-users@swift.org">swift-users@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-users">https://lists.swift.org/mailman/listinfo/swift-users</a></span><br></div></blockquote></body></html>