Is Copy-on-write Really a Pessimization Under Multithreading?

Copy-on-write (COW) is an optimization technique used by several C++ classes, including the ATL’s CString class, and many classes in the Qt framework.

However, it seems that the C++11 standard bans the use of COW in std::string.

Many developers consider COW a “pessimization” under multi-threading, and to support their argument they usually point to a piece written by Herb Sutter (GotW#45).

I’m an intellectually curious person, so I wanted to test that code on modern systems. I downloaded the original GotW#45 code, did some adjustments to make it compile cleanly with Visual Studio 2015 at warning level 4, changed some stuff like using Windows high-performance counters to measure time (instead of GetTickCount), and added a couple of tests for STL’s std::string and ATL’s CStringA.

You can download the modified code here from GitHub.

On a modern Intel i7-based workstation, the results for 100-char-length strings seem to show that CString (which is COW-based) actually performs better than std::string in those tests.

100-char STL string slower than COW ATL CString
100-char-length std::string’s performance worse than COW-based ATL CString.

However, when strings of shorter lengths are tested (e.g. 10 chars), std::string wins.

So, probably COW is not always a pessimization: there are cases in which the size of the data to copy can have a significant impact.

It’s also interesting that the fbstring class, which is a drop-in replacement for std::string, claiming significantly increased performance, uses COW for large strings.

fbstring uses COW for large strings
fbstring uses COW for large strings

 

P.S. Of course, I’m aware that are several other aspects to consider from a performance perspective, including data locality, etc. Still, I think these results are interesting.

Checked Iterators

Microsoft Visual Studio versions since VC8 (VS2005) offer a feature called “checked iterators”. The MSDN documentation clearly states: “Checked iterators ensure that you do not overwrite the bounds of your container. Checked iterators apply to release builds and debug builds.Checked iterators can be disabled #defining the _SECURE_SCL symbol to 0.

In VC8 (VS2005) and VC9 (VS2008), checked iterators are enabled by default in both debug and release builds. While I agree that having checked iterators enabled by default in debug builds is a good thing (because debug builds are designed to catch as much bugs as possible), I think the default behavior in release builds should be to switch checked iterators off. In fact, I do like speed in release builds.

As a simple benchmark (attached to this blog post), on an Intel Core 2 Duo @ 2.33 GHz, switching checked iterators off improved a simple 1,000×1,000 matrix multiplication time from 20 seconds to about 12 seconds (a 40% improvement).

I like putting the following lines in precompiled header file, to switch checked iterators off in release builds:

// Disable checked iterators in release builds

#ifndef _DEBUG

#define _SECURE_SCL 0

#endif

 

Fortunately, in VS2010, in release mode, the default value for _SECURE_SCL is 0.