[swift-users] Zero cost abstraction 2D Iterator (equivalent to two nested for loops) impossible?

Jens Persson jens at bitcycle.com
Wed Jan 4 02:56:43 CST 2017


Hi,

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:

for y in someStartY ..< someStopY {
    for x in someStartX ..< someStopX {
        ... pixels[x, y] ...
    }
}

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:

for pixelPosAsIntPoint2D in someIntRect2D {
    ... pixels[pixelPosAsIntPoint2D] ...
}

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).



Is it possible to write as a zero cost abstraction like this, if so, how?
If not, why?




PS

Note that eg this:

for y in someStartY ..< someStopY {
    for x in someStartX ..< someStopX {
       let pixelPosAsIntPoint2D = IntPoint2D(x: x, y: y)
        ... pixels[pixelPosAsIntPoint2D] ...
    }
}

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.

Here is one Iterator variant that I have tested:

struct Rect2DIntPointIterator : IteratorProtocol, Sequence {

    let startX, startY, stopX, stopY: Int

    var px, py: Int

    init(rect: Rect2DInt) {

        startX = rect.origin.x

        startY = rect.origin.y

        stopX = rect.maxX

        stopY = rect.maxY

        px = startX

        py = startY

    }

    mutating func next() -> Point2DInt? {

        defer { px = px &+ 1 }

        if px == stopX {

            px = startX

            py = py &+ 1

            if py == stopY { return nil }

        }

        return Point2DInt(x: px, y: py)

    }

}


And here are typical execution times from an example test:
2.1 seconds using my Iterator (the fastest I can get it, no matter how I
try to rewrite it).
1.5 seconds using nested x, y for loops.

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).

I have tried profiling the code and looking at the disassmbly but I'm
failing to understand what's going on.

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.

DS

/Jens
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170104/418a7f73/attachment.html>


More information about the swift-users mailing list