Friday, October 14, 2011

Selectively Unchecking Iterators: the key to High Performance C++ in Visual Studio

In Visual Studio 2005, Microsoft introduced checked iterators for STL containers. This means that every single operation that uses these iterators is range-checked to prevent memory accesses outside the container's allocated storage. If an range violation occurs, the program terminates with an exception at the location of the violation, instead of trashing potentially unrelated memory as C++ has been happily doing for decades.

This safety net comes with a price: every single iterator change undergoes scrutiny by the runtime to determine whether it is safe. For most programs this is a paltry price to pay for the peace of mind of safe container access. However, for performance sensitive code that include tight loops that iterate over STL containers, the penalties can be massive.

The folks at Microsoft realized this, and implemented a way to disable this functionality. If you define _SECURE_SCL to 0, the checking goes away. You are back at the level of performance (and unsafeness) of  C++ that you know and love. Unfortunately, there is a little snag. You can't exchange STL containers between modules that are compiled with _SECURE_SCL set to 0 and modules that are not. It may look like it's working, but it will blow up in your face when you least expect it. Also note that std::string is an STL container, and it features prominently in most modern C++ interfaces. The reality of the matter is this: if you are defining _SECURE_SCL to 0 you cannot use any C++ code that you did not compile yourself. No libraries, no DLLs, no nothing. For any non-trivial program, that's a pretty tall order.

Also, by setting _SECURE_SCL to 0 you are disabling iterator checking for the entire application. In any given program there are maybe 100s of places where STL iterators are used, but only a handful (one function, two iterators in my current code) where these checks hinder performance to any real degree. Disabling iterator checking globally, if it is even possible given the limitations discussed previously, is rarely desirable.

It turns out that, as of VS2010, there is a way to selectively disable iterator checking. It is a fairly obscure piece of Visual Studio trivia, and I will share it with you so that it is preserved for posterity:

std::vector<int> v; 
for (int j=0;j<1024*1024*5;++j) {  
    v.push_back(j);}  
}  
std::vector<int>::iterator i = v.begin();  
std::vector<int>::iterator::_Unchecked_type ui;  
int tot = 0;  
std::vector<int>::iterator::_Unchecked_type uiend = _Unchecked(v.end());  
for (ui=_Unchecked(v.begin());ui!=uiend;++ui) {  
    tot += *ui;      
}  
Note that we can uncheck the end iterator outside the loop, as the size of the vector does not change during the iteration. I encourage you to try it with checked and unchecked iterators to get a feel for the difference in performance. Be aware however that the inner loop here is very thin: performance gains will be inflated since the inner loop goes from doing mainly range checking to doing mainly nothing!

Readers are encouraged to contribute a set of macros that abstract iterator unchecking in a convenient way.