OpenImageIO/oiio — pull request #488 by lgritz (2013-01-09 08:06:28+00:00)
Title: ImageBuf::Iterator speed improvements

You can only improve what you measure.

First, imagespeed_test improvements:
1. --threads lets you test with different numbers of threads (default: 0, which means as many threads as cores available).
2. New iteration tests that include "slave" iterators, as we often use in ImageBufAlgo functions.
3. When more than one filename is provided, the read tests go through every file in sequence. For the iteration tests, just the first file is used.
4. --autotile lets you set what size to test for autotiling.
5. --iteronly skips the lengthy image read tests, only times the iterators.

In this round of improvements, I was particularly interested in the following idiom that we frequently use to traverse two images simultaneously.

for (Iterator master(img1), slave(img2); master.valid(); ++master) {
slave.pos (master.x(), master.y(), master.z()); // synchronize
// do something with *master and *slave
}

We repeatedly re-position the slave to the master's position, because there's no guarantee that two iterators will advance simultaneously with ++ (among other reasons, that the two images themselves may have different pixel data windows, and thus different iteration ranges).

Re-positioning the slave on every iteration of the loop was a big time sink. From the imagespeed_test output:

Loop pointers on loaded image ("1D") : 0.006s = 464.2 Mpel/s <-- "speed of light", just pointer arithmetic
Loop pointers on loaded image ("3D") : 0.006s = 468.4 Mpel/s
Loop + getchannel on loaded image ("3D"): 0.134s = 20.9 Mpel/s
Loop + getchannel on cached image ("3D"): 0.568s = 4.9 Mpel/s
Iterate over a loaded image : 0.016s = 176.6 Mpel/s <-- single iterator
Iterate over a cache image : 0.017s = 164.6 Mpel/s
Iterate over a loaded image (with slave): 0.047s = 59.1 Mpel/s <-- master/slave iterators
Iterate over a cache image (with slave) : 0.094s = 29.6 Mpel/s
Iterate over a loaded image (with slave2): 0.023s = 119.4 Mpel/s <-- 2 "master"-like iterators
Iterate over a cache image (with slave2) : 0.027s = 102.3 Mpel/s

Improvments in this patch:
1. Have pos() recognize the common special case of incrementing x from its last position and still staying in the iteration range, and take a big shortcut for that case.
2. Cache the end x position of the tile in the iterator, for quick comparison
3. Incidentally, migrate operator++ from Iterator and ConstIterator into the base class.

New speed (note: timing inaccuracies are such that you should only consider these speeds accurate to 2 significant figures at most; don't sweat a few percent change up or down):

Loop pointers on loaded image ("1D") : 0.006s = 463.8 Mpel/s
Loop pointers on loaded image ("3D") : 0.006s = 463.2 Mpel/s
Loop + getchannel on loaded image ("3D"): 0.134s = 20.8 Mpel/s
Loop + getchannel on cached image ("3D"): 0.577s = 4.9 Mpel/s
Iterate over a loaded image : 0.016s = 170.4 Mpel/s
Iterate over a cache image : 0.017s = 164.3 Mpel/s
Iterate over a loaded image (pos slave): 0.030s = 92.1 Mpel/s <--- !! was 59.1
Iterate over a cache image (pos slave) : 0.030s = 93.6 Mpel/s <--- !! was 29.6
Iterate over a loaded image (incr slave): 0.024s = 114.8 Mpel/s
Iterate over a cache image (incr slave) : 0.025s = 112.0 Mpel/s <--- ! was 102.3