OpenImageIO/oiio — pull request #482 by lgritz (2013-01-04 23:56:55+00:00)
Title: ImageBuf internals overhaul

No real change in algorithms, mostly just movement of code and tightening up of lots of little things.

1. Change ImageBuf to use PIMPL idiom, hiding all implementation details behind non-polymorphic interface. This allows us to change ImageBuf internals at will in the future, without endangering link-compatibility.

2. Also refactor ImageBuf::Iterator and ImageBuf::ConstIterator, which had long and redundant definitions, to be fairly minimal derivations of a shared ImageBuf::IteratorBase base class.

The PIMPL had the potential of slowing things down (and did, on its own when implemented naively), but in combination with the other refactors and minor optimizations, I actually managed to improve performance overall

I also beefed up imagespeed_test a bit as well, now benchmarking a variety of ways of walking through every pixel of an image, producing the reports below. And it had so many tests, I also refactored it to remove a lot of pointless redundancy.

Before:

Timing ways of iterating over an image:
Loop pointers on loaded image ("1D") : 0.007s = 424.4 Mpel/s
Loop pointers on loaded image ("3D") : 0.007s = 428.4 Mpel/s
Loop + getchannel on loaded image ("3D"): 0.156s = 17.9 Mpel/s
Iterate over a loaded image : 0.012s = 227.7 Mpel/s
Iterate over a cache image : 0.016s = 171.7 Mpel/s
Loop + getchannel on cached image ("3D"): 0.514s = 5.4 Mpel/s

After:

Timing ways of iterating over an image:
Loop pointers on loaded image ("1D") : 0.007s = 421.7 Mpel/s
Loop pointers on loaded image ("3D") : 0.007s = 417.5 Mpel/s
Loop + getchannel on loaded image ("3D"): 0.074s = 37.7 Mpel/s
Iterate over a loaded image : 0.010s = 275.0 Mpel/s
Iterate over a cache image : 0.012s = 242.7 Mpel/s
Loop + getchannel on cached image ("3D"): 0.516s = 5.4 Mpel/s

Don't sweat the "loop pointers" difference -- those aren't real, it's just timing variance. You can see that overall things are no worse than before, but actually using Iterators has sped up quite a bit (and so has 'getchannel').

The "loop pointers" tests are "speed of light" -- literally getting a pointer to the internal memory of a pre-loaded ImageBuf, and looping over all pixels while advancing the pointer (no more calls to the ImageBuf). The fact that we can use a totally safe, format-converting IB::Iterator over a read-on-demand cached image, within a factor of 2 of speed-of-light performance, is really good, I think. The methods that involve getpixel/getchannel are slow, but that's what we always expected and we don't advise people to use it.