<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=""> let rect: CGRect</font></div><div class=""><font face="Courier" class=""> var nextPoint: CGPoint</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class=""> let maxX: CGFloat</font></div><div class=""><font face="Courier" class=""> let maxY: CGFloat</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class=""> init(rect: CGRect) {</font></div><div class=""><font face="Courier" class=""> self.rect = rect.standardized</font></div><div class=""><font face="Courier" class=""> self.nextPoint = self.rect.origin</font></div><div class=""><font face="Courier" class=""> // Cache for fast iteration</font></div><div class=""><font face="Courier" class=""> self.maxX = self.rect.maxX</font></div><div class=""><font face="Courier" class=""> self.maxY = self.rect.maxY</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=""> mutating func next() -> CGPoint? {</font></div><div class=""><font face="Courier" class=""> guard nextPoint.x <= maxX, nextPoint.y <= maxY else {</font></div><div class=""><font face="Courier" class=""> return .none</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> defer {</font></div><div class=""><font face="Courier" class=""> nextPoint.x += 1</font></div><div class=""><font face="Courier" class=""> if nextPoint.x > maxX {</font></div><div class=""><font face="Courier" class=""> nextPoint.x = rect.origin.x</font></div><div class=""><font face="Courier" class=""> nextPoint.y += 1</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> return nextPoint</font></div><div class=""><font face="Courier" class=""> }</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)->()) {</font></div><div class=""><font face="Courier" class=""> var it = PointIterator(rect: rect)</font></div><div class=""><font face="Courier" class=""> while let point = it.next() {</font></div><div class=""><font face="Courier" class=""> with(point)</font></div><div class=""><font face="Courier" class=""> }</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 ‘defer’)</font></div><div class=""><font face="Courier" class="">func iteratePoints_fe(_ rect: CGRect, with: (CGPoint)->()) {</font></div><div class=""><font face="Courier" class=""> let rect = rect.standardized</font></div><div class=""><font face="Courier" class=""> var nextPoint = rect.origin</font></div><div class=""><font face="Courier" class=""> let maxX = rect.maxX</font></div><div class=""><font face="Courier" class=""> let maxY = rect.maxY</font></div><div class=""><font face="Courier" class=""> </font></div><div class=""><font face="Courier" class=""> while true {</font></div><div class=""><font face="Courier" class=""> guard nextPoint.x <= maxX, nextPoint.y <= maxY else {</font></div><div class=""><font face="Courier" class=""> return</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> with(nextPoint) </font></div><div class=""><font face="Courier" class=""> nextPoint.x += 1</font></div><div class=""><font face="Courier" class=""> if nextPoint.x > maxX {</font></div><div class=""><font face="Courier" class=""> nextPoint.x = rect.origin.x</font></div><div class=""><font face="Courier" class=""> nextPoint.y += 1</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> } </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)->()) {</font></div><div class=""><font face="Courier" class=""> let rect = rect.standardized</font></div><div class=""><font face="Courier" class=""> let maxX = rect.maxX</font></div><div class=""><font face="Courier" class=""> let maxY = rect.maxY</font></div><div class=""><font face="Courier" class=""> for y in stride(from: rect.origin.y, to: maxY, by: 1) {</font></div><div class=""><font face="Courier" class=""> for x in stride(from: rect.origin.x, to: maxX, by: 1) {</font></div><div class=""><font face="Courier" class=""> with(CGPoint(x: x, y: y))</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> }</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: ()->()) -> TimeInterval {</font></div><div class=""><font face="Courier" class=""> var totalTime: TimeInterval = 0</font></div><div class=""><font face="Courier" class=""> for _ in 0..<iterations {</font></div><div class=""><font face="Courier" class=""> let start = Date().timeIntervalSinceReferenceDate</font></div><div class=""><font face="Courier" class=""> thing()</font></div><div class=""><font face="Courier" class=""> totalTime += (Date().timeIntervalSinceReferenceDate - start)</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class=""> 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) { if $0.x > 1_000_000 { print("?") } } } // always false, won't be optimised out.</font></div><div class=""><font face="Courier" class="">let foreach = profile(10) { iteratePoints_fe(bigRect) { if $0.x > 1_000_000 { print("?") } } }</font></div><div class=""><font face="Courier" class="">let foreach2 = profile(10) { iteratePoints_fe2(bigRect) { if $0.x > 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 <br class="">foreach: 0.283202117681503 <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 <<a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a>> 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 ..< someStopY {</div><div class=""> for x in someStartX ..< someStopX {</div><div class=""> ... pixels[x, y] ...</div><div class=""> }</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=""> ... 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 &+ 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 ..< someStopY {</div><div class=""> for x in someStartX ..< someStopX {</div><div class=""> let pixelPosAsIntPoint2D = IntPoint2D(x: x, y: y)</div><div class=""> ... pixels[pixelPosAsIntPoint2D] ...</div><div class=""> }</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=""> 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=""> 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=""> init(rect: Rect2DInt) {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> 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=""> 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=""> stopX = rect.maxX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> stopY = rect.maxY</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> px = startX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> py = startY</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 style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> mutating func next() -> Point2DInt? {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> defer { px = px &+ 1 }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> if px == stopX {</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> px = startX</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> py = py &+ 1</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> 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=""> }</span></font></div><div style="margin: 0px; line-height: normal;" class=""><font face="menlo" class=""><span style="font-size:12px" class=""> 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=""> }</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>