Visual C++: Iterator Checks and Slow STL Code

Visual C++ turns on a lot of checking on STL iterators when compiled in Debug mode. This is very helpful since any mistake with an iterator leads immediately to an exception with a helpful exception message. This can catch latent bugs much much earlier while programming.

However, this huge amount of security is sometimes not tolerable by everyone. For certain applications the debug mode with all the iterator checking turned on can be extremely slow, to the point of being useless. I faced a similar problem and so had to investigate to find the reason for the slow behaviour. I discovered that there are 2 mutually exclusive checking mechanisms in the STL of Visual C++: checked iterators and iterator debugging.

Checked Iterators

Checked iterators is controlled by the preprocessor definition _SECURE_SCL. SCL is Microsoft jargon for Standard C++ Library. So, this is a feature by Microsoft to provide some amount of minimal security on the usage of STL iterators. The overhead of _SECURE_SCL is so low that by default it is ON for both Release and Debug modes. So, _SECURE_SCL is rarely the cause of your program being slow. It should be left at its default options.

Iterator Debugging

Iterator debugging is a whole another beast. It involves a lot more intensive checking on the validity of iterators. It does this both when iterators are dereferenced and when containers change internally. This was implemented by Dinkumware, who are the creators of the STL implementation that Visual C++ ships with.

By default, iterator debugging is turned ON for Debug mode and OFF for Release mode. Typically it is not slow for Debug mode, but if the C++ code has a lot of loops over STL containers and uses iterators heavily, then it can make everything very slow. How slow can it get? This is the difference I saw in one of my programs compiled in Debug mode:

Iterator Debugging ON: 51.83 sec
Iterator Debugging OFF: 0.27 sec

Yes, iterator debugging was a jaw-dropping 192 times slower! Not just that, at ~52 seconds of execution, this program was unusable for my purpose!

Iterator debugging is controlled by the preprocessor definition _HAS_ITERATOR_DEBUGGING. To turn off iterator debugging, add _HAS_ITERATOR_DEBUGGING=0 to the Preprocessor Definitions in C++ → Preprocessor section of the project properties.

References:

  • STL Iterator Debugging and Secure SCL video on Channel 9 by Stephan T. Lavavej. A must watch video for anyone using STL on Visual C++. Stephan explains both Secure SCL and Iterator Debugging and how they are implemented internally in the library. Also explained is when and which of these options to turn ON or OFF.

Visual C++: C4996 Warning on Copy with Array Parameters

Visual C++ throws a C4996 warning if std::copy is called with array parameters (instead of the conventional iterator parameters). For example, if you have code of the form:

std::copy( arr0, arr0 + 5, arr1 );

where arr0 and arr1 are pointers to arrays or valid memory locations.

The C4996 warning is of the form:

warning C4996: ‘std::_Copy_impl’: Function call with parameters that may be unsafe – this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ ‘Checked Iterators’

Visual C++ uses checked iterators by default everywhere for security reasons. This warning is a manifestation of that design decision. There are a few ways to deal with it:

  • checked_array_iterator: A checked_array_iterator can be used to wrap the unsafe array pointer. It is a Microsoft extension to the C++ Standard and is defined in the stdext namespace. It results in an assertion check at runtime, which throws an exception if it fails. Applying a checked_array_iterator on the above code:
    #include <iterator>
    std::copy( arr0, arr0 + 5, stdext::checked_array_iterator<int*>( arr1, 5 ) );
    
  • unchecked_copy: The copy call can be replaced with an unchecked_copy if you want to forgo the checking. This is defined in the stdext namespace. Note that this is usable in Visual C++ 2008 and not later. It was removed in Visual C++ 2010. Using unchecked_copy instead of copy:
    stdext::unchecked_copy( arr0, arr0 + 5, arr1 );
    
  • vector: Another solution is to wrap the destination array in a vector and use the vector as the destination of copy. Not a great solution, but it might work for you.
  • pragma warning: Turn off the C4996 warning for the call to copy using pragma warning. This can be applied at the source file level to mask all C4996 warnings. Note that this is a really bad move since it masks all deprecation warnings!
  • _SCL_SECURE_NO_WARNINGS: Define the _SCL_SECURE_NO_WARNINGS macro to turn off all C4996 warnings. SCL here stands for Standard C++ Library. Again, turning off this warning not a good move! If you want to do this anyway, add -D_SCL_SECURE_NO_WARNINGS to your project settings or #define _SCL_SECURE_NO_WARNINGS in your code.