<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Hmmm that’s interesting. A brief test I ran:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><font face="Courier" class="">import CoreGraphics</font></div><div class=""><font face="Courier" class="">import Foundation</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">struct PointIterator {</font></div><div class=""><font face="Courier" class="">&nbsp; let rect: CGRect</font></div><div class=""><font face="Courier" class="">&nbsp; var nextPoint: CGPoint</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">&nbsp; let maxX: CGFloat</font></div><div class=""><font face="Courier" class="">&nbsp; let maxY: CGFloat</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">&nbsp; init(rect: CGRect) {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; self.rect &nbsp; &nbsp; &nbsp;= rect.standardized</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; self.nextPoint = self.rect.origin</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; // Cache for fast iteration</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; self.maxX &nbsp; &nbsp; &nbsp;= self.rect.maxX</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; self.maxY &nbsp; &nbsp; &nbsp;= self.rect.maxY</font></div><div class=""><font face="Courier" class="">&nbsp; }</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">&nbsp; mutating func next() -&gt; CGPoint? {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; guard nextPoint.x &lt;= maxX, nextPoint.y &lt;= maxY else {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; return .none</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; defer {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; nextPoint.x += 1</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; if nextPoint.x &gt; maxX {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; &nbsp; nextPoint.x &nbsp;= rect.origin.x</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; &nbsp; nextPoint.y += 1</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; return nextPoint</font></div><div class=""><font face="Courier" class="">&nbsp; }</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">// Use iterator</font></div><div class=""><font face="Courier" class="">func iteratePoints_it(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div class=""><font face="Courier" class="">&nbsp; var it = PointIterator(rect: rect)</font></div><div class=""><font face="Courier" class="">&nbsp; while let point = it.next() {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; with(point)</font></div><div class=""><font face="Courier" class="">&nbsp; }</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">// Basic unwrapping of the iterator as a function (no&nbsp;‘defer’)</font></div><div class=""><font face="Courier" class="">func iteratePoints_fe(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div class=""><font face="Courier" class="">&nbsp; let rect &nbsp; &nbsp; &nbsp;= rect.standardized</font></div><div class=""><font face="Courier" class="">&nbsp; var nextPoint = rect.origin</font></div><div class=""><font face="Courier" class="">&nbsp; let maxX &nbsp; &nbsp; &nbsp;= rect.maxX</font></div><div class=""><font face="Courier" class="">&nbsp; let maxY &nbsp; &nbsp; &nbsp;= rect.maxY</font></div><div class=""><font face="Courier" class="">&nbsp;&nbsp;</font></div><div class=""><font face="Courier" class="">&nbsp; while true {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; guard nextPoint.x &lt;= maxX, nextPoint.y &lt;= maxY else {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; return</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; with(nextPoint)&nbsp;</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; nextPoint.x += 1</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; if nextPoint.x &gt; maxX {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; nextPoint.x &nbsp;= rect.origin.x</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; nextPoint.y += 1</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; }&nbsp;</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">// for..in loop</font></div><div class=""><font face="Courier" class="">func iteratePoints_fe2(_ rect: CGRect, with: (CGPoint)-&gt;()) {</font></div><div class=""><font face="Courier" class="">&nbsp; let rect = rect.standardized</font></div><div class=""><font face="Courier" class="">&nbsp; let maxX = rect.maxX</font></div><div class=""><font face="Courier" class="">&nbsp; let maxY = rect.maxY</font></div><div class=""><font face="Courier" class="">&nbsp; for y in stride(from: rect.origin.y, to: maxY, by: 1) {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; for x in stride(from: rect.origin.x, to: maxX, by: 1) {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; &nbsp; with(CGPoint(x: x, y: y))</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; }</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">func profile(_ iterations: Int, _ thing: ()-&gt;()) -&gt; TimeInterval {</font></div><div class=""><font face="Courier" class="">&nbsp; var totalTime: TimeInterval = 0</font></div><div class=""><font face="Courier" class="">&nbsp; for _ in 0..&lt;iterations {</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; let start = Date().timeIntervalSinceReferenceDate</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; thing()</font></div><div class=""><font face="Courier" class="">&nbsp; &nbsp; totalTime += (Date().timeIntervalSinceReferenceDate - start)</font></div><div class=""><font face="Courier" class="">&nbsp; }</font></div><div class=""><font face="Courier" class="">&nbsp; return totalTime/TimeInterval(iterations)</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">let bigRect = CGRect(x: 0, y: 0, width: 10_000, height: 10_000)</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">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 class=""><font face="Courier" class="">let foreach = profile(10) &nbsp;{ iteratePoints_fe(bigRect) &nbsp;{ if $0.x &gt; 1_000_000 { print("?") } } }</font></div><div class=""><font face="Courier" class="">let foreach2 = profile(10) { iteratePoints_fe2(bigRect) { if $0.x &gt; 1_000_000 { print("?") } } }</font></div><div class=""><font face="Courier" class="">print("iterator: \(iterator) \nforeach: \(foreach) \nforeach2: \(foreach2)")</font></div></blockquote><div class=""><br class=""></div><div class="">Results:</div><div class=""><br class=""></div><div class="">iterator: 0.316907703876495&nbsp;<br class="">foreach: 0.283202117681503&nbsp;<br class="">foreach2: 0.568318998813629</div><div class=""><br class=""></div><div class="">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 class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 4 Jan 2017, at 09:56, Jens Persson via swift-users &lt;<a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Hi,<div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">for y in someStartY ..&lt; someStopY {</div><div class="">&nbsp; &nbsp; for x in someStartX ..&lt; someStopX {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; ... pixels[x, y] ...</div><div class="">&nbsp; &nbsp; }</div><div class="">}</div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">for pixelPosAsIntPoint2D in someIntRect2D {</div><div class="">&nbsp; &nbsp; ... pixels[pixelPosAsIntPoint2D] ...</div><div class="">}</div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Is it possible to write as a zero cost abstraction like this, if so, how? If not, why?</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">PS</div><div class=""><br class=""></div><div class="">Note that eg this:</div><div class=""><br class=""></div><div class=""><div class="">for y in someStartY ..&lt; someStopY {</div><div class="">&nbsp; &nbsp; for x in someStartX ..&lt; someStopX {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;let pixelPosAsIntPoint2D = IntPoint2D(x: x, y: y)</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; ... pixels[pixelPosAsIntPoint2D] ...</div><div class="">&nbsp; &nbsp; }</div><div class="">}</div></div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">Here is one Iterator variant that I have tested:</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">struct Rect2DIntPointIterator : IteratorProtocol, Sequence {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; let startX, startY, stopX, stopY: Int</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; var px, py: Int</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; init(rect: Rect2DInt) {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; startX = rect.origin.x</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; startY = rect.origin.y</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; stopX = rect.maxX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; stopY = rect.maxY</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; px = startX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; py = startY</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; mutating func next() -&gt; Point2DInt? {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; defer { px = px &amp;+ 1 }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; if px == stopX {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; px = startX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; py = py &amp;+ 1</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if py == stopY { return nil }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; &nbsp; &nbsp; return Point2DInt(x: px, y: py)</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">&nbsp; &nbsp; }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class="">}</span></font></div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">And here are typical execution times from an example test:</div><div class="">2.1 seconds using my Iterator (the fastest I can get it, no matter how I try to rewrite it).</div><div class="">1.5 seconds using nested x, y for loops.</div><div class=""><br class=""></div><div class="">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 class=""></div><div class=""><br class=""></div><div class="">I have tried profiling the code and looking at the disassmbly but I'm failing to understand what's going on.</div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">DS</div><div class=""><br class=""></div><div class="">/Jens</div><div class=""><br class=""></div></div>
_______________________________________________<br class="">swift-users mailing list<br class=""><a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-users<br class=""></div></blockquote></div><br class=""></div></body></html>