title: Is new[] faster than Win32's VirtualAlloc?
body: <p>I was testing the performance of some string pool allocators: I considered the one presented <a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/05/19/420038.aspx" rel="noreferrer">here</a> that calls <code>Virtual­Alloc</code> and then carves out sub-allocations, and a similar implementation using standard C++ (without directly calling any Win32 API) and <code>new[]</code>.</p>
  
  <p>I expected the <code>Virtual­Alloc</code> version to be faster, since I thought there should be less overhead than C++ <code>new[]</code>; but the results I observed are the opposite: using <code>new[]</code> seems to result in faster code than using the lower-level <code>Virtual­Alloc</code>.</p>
  
  <p>I ran the test several times (the code is compiled with VS2010 SP1), and the output is something like this:</p>
  
  <blockquote>
  <pre><code>String pool using VirtualAlloc: 1280.07 ms
  String pool using new[]: 799.193 ms
  </code></pre>
  </blockquote>
  
  <p>Why is this? Why does <code>new[]</code> seem to be faster than <code>VirtualAlloc</code>?</p>
  
  <p>Test source code follows:</p>
  
  <pre><code>////////////////////////////////////////////////////////////////////////////
  // Testing VirtualAlloc vs. new[].
  ////////////////////////////////////////////////////////////////////////////
  
  
  #include &lt;string.h&gt;
  #include &lt;wchar.h&gt;
  #include &lt;algorithm&gt;
  #include &lt;exception&gt;
  #include &lt;iostream&gt;
  #include &lt;new&gt;
  #include &lt;ostream&gt;
  #include &lt;stdexcept&gt;
  #include &lt;string&gt;
  #include &lt;vector&gt;
  #include &lt;windows.h&gt;
  using namespace std;
  
  
  //--------------------------------------------------------------------------
  // String pool allocator using VirtualAlloc, based on this:
  // http://blogs.msdn.com/oldnewthing/archive/2005/05/19/420038.aspx
  //--------------------------------------------------------------------------
  class StringPoolUsingVirtualAlloc
  {
  public:
  
      StringPoolUsingVirtualAlloc()
          : m_pchNext(nullptr), 
            m_pchLimit(nullptr), 
            m_phdrCur(nullptr)
      {
          SYSTEM_INFO si;
          GetSystemInfo(&amp;si);
          m_dwGranularity = static_cast&lt;DWORD&gt;( 
              RoundUp( sizeof(HEADER) + MIN_CBCHUNK, si.dwAllocationGranularity 
              ));
      }
  
      ~StringPoolUsingVirtualAlloc()
      {
          HEADER* phdr = m_phdrCur;
          while (phdr) 
          {
              HEADER * phdrPrev = phdr-&gt;m_phdrPrev;
              VirtualFree(phdr, 0, MEM_RELEASE);
              phdr = phdrPrev;
          }
      }
  
      wchar_t* DuplicateString(const wstring&amp; source)
      {
          return AllocString(source.c_str(), source.c_str() + source.length());
      }
  
  private:
      union HEADER 
      {
          struct 
          {
              HEADER* m_phdrPrev;
              SIZE_T  m_cb;
          };
          wchar_t alignment;
      };
  
      enum 
      { 
          MIN_CBCHUNK = 32000,
          MAX_CHARALLOC = 1024*1024
      };
  
      wchar_t*  m_pchNext;
      wchar_t*  m_pchLimit;
      HEADER*   m_phdrCur;
      DWORD     m_dwGranularity;
  
      static SIZE_T RoundUp(SIZE_T cb, SIZE_T units)
      {
          return ((cb + units - 1) / units) * units;
      }
  
      wchar_t* AllocString(const wchar_t* pchBegin, const wchar_t* pchEnd)
      {
          SIZE_T cchTotal = pchEnd - pchBegin + 1;
          if (cchTotal &gt; MAX_CHARALLOC) 
              throw length_error("String too big.");
  
          wchar_t* psz = m_pchNext;
          if (m_pchNext + cchTotal &lt;= m_pchLimit) 
          {
              m_pchNext += cchTotal;
              lstrcpynW(psz, pchBegin, static_cast&lt;int&gt;(cchTotal));
              return psz;
          }
  
          SIZE_T cbAlloc = RoundUp(cchTotal * sizeof(wchar_t) + sizeof(HEADER), m_dwGranularity);
          BYTE* pbNext = reinterpret_cast&lt;BYTE*&gt;(
              VirtualAlloc(nullptr, cbAlloc, MEM_COMMIT, PAGE_READWRITE));
          if (pbNext == nullptr) 
              throw bad_alloc();
  
          m_pchLimit = reinterpret_cast&lt;wchar_t*&gt;(pbNext + cbAlloc);
          HEADER* phdrCur = reinterpret_cast&lt;HEADER*&gt;(pbNext);
          phdrCur-&gt;m_phdrPrev = m_phdrCur;
          phdrCur-&gt;m_cb = cbAlloc;
          m_phdrCur = phdrCur;
          m_pchNext = reinterpret_cast&lt;wchar_t*&gt;(phdrCur + 1);
          return AllocString(pchBegin, pchEnd);
      }
  
      StringPoolUsingVirtualAlloc(const StringPoolUsingVirtualAlloc &amp;);
      StringPoolUsingVirtualAlloc &amp; operator=(const StringPoolUsingVirtualAlloc &amp;);
  };
  
  
  //--------------------------------------------------------------------------
  // String pool allocator that uses standard C++ (no Win32 stuff) and new[].
  //--------------------------------------------------------------------------
  class StringPoolUsingNew
  {
  public:
  
      StringPoolUsingNew()
          : m_pchNext(NULL), 
            m_pchLimit(NULL), 
            m_currChunk(NULL)
      {
      }
  
      ~StringPoolUsingNew()
      {
          for (auto it = m_chunks.begin(); it != m_chunks.end(); ++it)
              delete *it;
      }
  
      wchar_t* DuplicateString(const wstring&amp; source)
      {
          return AllocString(source.c_str(), source.c_str() + source.length());
      }
  
  private:
  
      class Chunk
      {
      public:
          explicit Chunk(size_t maxCharCount)
          {
              m_data = new wchar_t[maxCharCount];
              m_maxCharCount = maxCharCount;
          }
  
          ~Chunk()
          {
              delete [] m_data;
          }
  
          wchar_t* Begin()             { return m_data; }
          const wchar_t* Begin() const { return m_data; }
          size_t Length() const        { return m_maxCharCount; }
  
      private:
          Chunk(const Chunk&amp;);
          Chunk&amp; operator=(const Chunk&amp;);
  
          wchar_t * m_data;
          size_t m_maxCharCount;
      };
  
      static const size_t kMinChunkCharCount = 16000;
      static const size_t kMaxCharAlloc = 1024*1024;
  
      wchar_t*  m_pchNext;
      wchar_t*  m_pchLimit;
      Chunk*    m_currChunk;
      vector&lt;Chunk*&gt; m_chunks;
  
      wchar_t* AllocString(const wchar_t* pchBegin, const wchar_t* pchEnd)
      {
          const size_t cchTotal = pchEnd - pchBegin + 1;
          if (cchTotal &gt; kMaxCharAlloc) 
              throw length_error("String too big.");
  
          wchar_t* dest = m_pchNext;
          if (m_pchNext + cchTotal &lt;= m_pchLimit) 
          {
              m_pchNext += cchTotal;
              const size_t copyCount = cchTotal - 1;
              if (copyCount != 0)
                  wmemcpy(dest, pchBegin, copyCount);
              dest[copyCount] = L'\0';
              return dest;
          }
  
          const size_t newChunkSize = max(cchTotal, kMinChunkCharCount);
          Chunk* newChunk = new Chunk(newChunkSize);
          m_chunks.push_back(newChunk);
  
          m_pchNext = newChunk-&gt;Begin();
          m_pchLimit = newChunk-&gt;Begin() + newChunk-&gt;Length();
          m_currChunk = newChunk;
  
          return AllocString(pchBegin, pchEnd);
      }
  
      StringPoolUsingNew(const StringPoolUsingNew&amp;);
      StringPoolUsingNew&amp; operator=(const StringPoolUsingNew&amp;);
  };
  
  
  //------------------------------------------------------------------------
  //                          Perf Measurement
  //------------------------------------------------------------------------
  
  long long Counter() 
  {
      LARGE_INTEGER li;
      QueryPerformanceCounter(&amp;li);
      return li.QuadPart;
  }
  
  long long Frequency() 
  {
      LARGE_INTEGER li;
      QueryPerformanceFrequency(&amp;li);
      return li.QuadPart;
  }
  
  void PrintTime(long long start, long long finish, const char * s) 
  {
      cout &lt;&lt; s &lt;&lt; ": " &lt;&lt; (finish - start) * 1000.0 / Frequency() &lt;&lt; " ms" &lt;&lt; endl;
  }
  
  
  //--------------------------------------------------------------------------
  // Test
  //--------------------------------------------------------------------------
  int main()
  {
      static const int kExitOk = 0;
      static const int kExitError = 1;
      try
      {
          long long start = 0;
          long long finish = 0;
  
          const auto shuffled = []() -&gt; vector&lt;wstring&gt; 
          {
              const wstring lorem[] = {
                  L"Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",
                  L"Maecenas porttitor congue massa. Fusce posuere, magna sed",
                  L"pulvinar ultricies, purus lectus malesuada libero,",
                  L"sit amet commodo magna eros quis urna.",
                  L"Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.",
                  L"Pellentesque habitant morbi tristique senectus et netus et",
                  L"malesuada fames ac turpis egestas. Proin pharetra nonummy pede.",
                  L"Mauris et orci."
              };
  
              vector&lt;wstring&gt; v;
              for (long long i = 0; i &lt; 400*1000; ++i) 
              {
                  for (auto it = begin(lorem); it != end(lorem); ++it) 
                  {
                      v.push_back((*it) + L" (#" + to_wstring(i) + L")");
                  }
              }
              random_shuffle(v.begin(), v.end());
  
              return v;
          }();
  
          start = Counter();
          {
              StringPoolUsingVirtualAlloc pool;
              vector&lt;const wchar_t*&gt; v;
              for (auto it = shuffled.begin(); it != shuffled.end(); ++it)
              {
                  v.push_back( pool.DuplicateString(*it) );
              }
          }
          finish = Counter();
          PrintTime(start, finish, "String pool using VirtualAlloc");
  
          start = Counter();
          {
              StringPoolUsingNew pool;
              vector&lt;const wchar_t*&gt; v;
              for (auto it = shuffled.begin(); it != shuffled.end(); ++it)
              {
                  v.push_back( pool.DuplicateString(*it) );
              }
          }
          finish = Counter();
          PrintTime(start, finish, "String pool using new[]");
  
          return kExitOk;
      }
      catch (const exception&amp; e)
      {
          cerr &lt;&lt; "*** ERROR: " &lt;&lt; e.what() &lt;&lt; endl;
          return kExitError;
      }
  }
  
  ////////////////////////////////////////////////////////////////////////////
  </code></pre>