OpenImageIO/oiio — pull request #255 by lgritz (2012-03-17 00:02:45+00:00)
Title: Review: Strutil wrappers to avoid locale-related mutex locks

Today was one of those days when I realized how little I know about programming.

We liberally use boost::algorithm::iequals for case-insensitive string comparison, as well as occasional calls to istarts_with, iends_with, to_lower, etc. Unbeknownst to me previously, these things check the "locale" for information about how to do upper/lower case conversion, and if you don't pass an explicit locale to them, they will call std::locale() for it, which itself will actually lock a mutex in order to thread-safely discover the current locale.

Any time texture tiles are requested from a different MIP level than the last tile read from that file, does a seek_subimage, which can trigger a new read of image header info for that subimage and creation of a new ImageSpec. That, it turns out, makes a substantial number of calls to iequals. When heavily threaded, then, the texture system can become heavily bottlenecked on those mutex locks, all because of innocent-looking iequals.

Frankly, I have not noticed this on Linux or OSX (I often run with many threads on Linux, but rarely on OSX, so I'm less confident about that one), but reports of bad TextureSystem performance on WIndows have dogged us for a long time, and it looks like this is a major culprit.

So, this patch creates Strutil wrappers for iequals, istarts_with, iens_with, to_upper, and to_lower. Their implementations use a static local to pass to the boost routines. This should completely avoid the mutex locking behavior as well as be thread-safe, though it will be immune to app-triggered changes in locale information that happen after OIIO's static locale is constructed (but I don't think that really matters for the way we use it, and if it does, we can fix it at that point).

It's such a critical performance issue for some people, that I'm eager to backport it to both 1.0 and 0.10, so I've broken this up into several individual commits in order to make that merge a little easier to do with the files I anticipate for merge conflicts isolated, hopefully.

After this is merged, I'll also look into ways to avoid doing so much work when switching MIPmap levels, but that might require some minor API or behavior changes, so I'm less confident about being able to safely backport it.