Is the new [] faster than Win32 VirtualAlloc?

I tested the performance of some row pool allocators: I examined the one presented here , which calls Virtual­Allocand then cuts out sub-distributions and a similar implementation using standard C ++ (without directly calling any Win32 API) and new[].

I expected the version to Virtual­Allocbe faster as I thought there should be less overhead than C ++ new[]; but the results that I observed are the opposite: using new[]seems to lead to faster code than to the lower level Virtual­Alloc.

I checked the test several times (the code was compiled with VS2010 SP1), and the result looks something like this:

String pool using VirtualAlloc: 1280.07 ms
String pool using new[]: 799.193 ms

Why is this? Why new[]does it seem faster than VirtualAlloc?

The following is a sample source code:

////////////////////////////////////////////////////////////////////////////
// Testing VirtualAlloc vs. new[].
////////////////////////////////////////////////////////////////////////////


#include <string.h>
#include <wchar.h>
#include <algorithm>
#include <exception>
#include <iostream>
#include <new>
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <windows.h>
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(&si);
        m_dwGranularity = static_cast<DWORD>( 
            RoundUp( sizeof(HEADER) + MIN_CBCHUNK, si.dwAllocationGranularity 
            ));
    }

    ~StringPoolUsingVirtualAlloc()
    {
        HEADER* phdr = m_phdrCur;
        while (phdr) 
        {
            HEADER * phdrPrev = phdr->m_phdrPrev;
            VirtualFree(phdr, 0, MEM_RELEASE);
            phdr = phdrPrev;
        }
    }

    wchar_t* DuplicateString(const wstring& 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 > MAX_CHARALLOC) 
            throw length_error("String too big.");

        wchar_t* psz = m_pchNext;
        if (m_pchNext + cchTotal <= m_pchLimit) 
        {
            m_pchNext += cchTotal;
            lstrcpynW(psz, pchBegin, static_cast<int>(cchTotal));
            return psz;
        }

        SIZE_T cbAlloc = RoundUp(cchTotal * sizeof(wchar_t) + sizeof(HEADER), m_dwGranularity);
        BYTE* pbNext = reinterpret_cast<BYTE*>(
            VirtualAlloc(nullptr, cbAlloc, MEM_COMMIT, PAGE_READWRITE));
        if (pbNext == nullptr) 
            throw bad_alloc();

        m_pchLimit = reinterpret_cast<wchar_t*>(pbNext + cbAlloc);
        HEADER* phdrCur = reinterpret_cast<HEADER*>(pbNext);
        phdrCur->m_phdrPrev = m_phdrCur;
        phdrCur->m_cb = cbAlloc;
        m_phdrCur = phdrCur;
        m_pchNext = reinterpret_cast<wchar_t*>(phdrCur + 1);
        return AllocString(pchBegin, pchEnd);
    }

    StringPoolUsingVirtualAlloc(const StringPoolUsingVirtualAlloc &);
    StringPoolUsingVirtualAlloc & operator=(const StringPoolUsingVirtualAlloc &);
};


//--------------------------------------------------------------------------
// 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& 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&);
        Chunk& operator=(const Chunk&);

        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<Chunk*> m_chunks;

    wchar_t* AllocString(const wchar_t* pchBegin, const wchar_t* pchEnd)
    {
        const size_t cchTotal = pchEnd - pchBegin + 1;
        if (cchTotal > kMaxCharAlloc) 
            throw length_error("String too big.");

        wchar_t* dest = m_pchNext;
        if (m_pchNext + cchTotal <= 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->Begin();
        m_pchLimit = newChunk->Begin() + newChunk->Length();
        m_currChunk = newChunk;

        return AllocString(pchBegin, pchEnd);
    }

    StringPoolUsingNew(const StringPoolUsingNew&);
    StringPoolUsingNew& operator=(const StringPoolUsingNew&);
};


//------------------------------------------------------------------------
//                          Perf Measurement
//------------------------------------------------------------------------

long long Counter() 
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return li.QuadPart;
}

long long Frequency() 
{
    LARGE_INTEGER li;
    QueryPerformanceFrequency(&li);
    return li.QuadPart;
}

void PrintTime(long long start, long long finish, const char * s) 
{
    cout << s << ": " << (finish - start) * 1000.0 / Frequency() << " ms" << 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 = []() -> vector<wstring> 
        {
            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<wstring> v;
            for (long long i = 0; i < 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<const wchar_t*> 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<const wchar_t*> 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& e)
    {
        cerr << "*** ERROR: " << e.what() << endl;
        return kExitError;
    }
}

////////////////////////////////////////////////////////////////////////////
+5
source share
3 answers

So, @JamesMcNellis found the main problem , i.e. the fact that it was lstrcpynWused in the pool allocator VirtualAlloc, instead it wmemcpywas used in the pool allocator new[].

I changed the source code with uniform use wmemcpyand skipped tests several times and calculated the average execution time for each test (excluding the first run).

I also added a pool allocator to the test tests HeapAllocand a simple one vector<wstring>.

Now the results:

--- Tests summary ---
VirtualAlloc : 781.671 ms
HeapAlloc    : 806.597 ms
new[]        : 889.792 ms
STL strings  : 1491.36 ms

So it seems the fastest (as expected). VirtualAlloc

( VS2010 SP1/VC10):

////////////////////////////////////////////////////////////////////////////
// Testing VirtualAlloc vs. HeapAlloc vs. new[] vs. STL strings.
////////////////////////////////////////////////////////////////////////////


#include <string.h>
#include <wchar.h>
#include <algorithm>
#include <exception>
#include <iostream>
#include <new>
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <windows.h>
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(&si);
        m_dwGranularity = static_cast<DWORD>( 
            RoundUp( sizeof(HEADER) + MIN_CBCHUNK, si.dwAllocationGranularity 
            ));
    }

    ~StringPoolUsingVirtualAlloc()
    {
        HEADER* phdr = m_phdrCur;
        while (phdr) 
        {
            HEADER * phdrPrev = phdr->m_phdrPrev;
            VirtualFree(phdr, 0, MEM_RELEASE);
            phdr = phdrPrev;
        }
    }

    const wchar_t* DuplicateString(const wstring& 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 > MAX_CHARALLOC) 
            throw length_error("String too big.");

        wchar_t* psz = m_pchNext;
        if (m_pchNext + cchTotal <= m_pchLimit) 
        {
            m_pchNext += cchTotal;
            wmemcpy(psz, pchBegin, cchTotal);
            return psz;
        }

        SIZE_T cbAlloc = RoundUp(cchTotal * sizeof(wchar_t) + sizeof(HEADER), m_dwGranularity);
        BYTE* pbNext = reinterpret_cast<BYTE*>(
            VirtualAlloc(nullptr, cbAlloc, MEM_COMMIT, PAGE_READWRITE));
        if (pbNext == nullptr) 
            throw bad_alloc();

        m_pchLimit = reinterpret_cast<wchar_t*>(pbNext + cbAlloc);
        HEADER* phdrCur = reinterpret_cast<HEADER*>(pbNext);
        phdrCur->m_phdrPrev = m_phdrCur;
        phdrCur->m_cb = cbAlloc;
        m_phdrCur = phdrCur;
        m_pchNext = reinterpret_cast<wchar_t*>(phdrCur + 1);
        return AllocString(pchBegin, pchEnd);
    }

    StringPoolUsingVirtualAlloc(const StringPoolUsingVirtualAlloc &);
    StringPoolUsingVirtualAlloc & operator=(const StringPoolUsingVirtualAlloc &);
};


//--------------------------------------------------------------------------
// String pool allocator using HeapAlloc, 
// based on the VirtualAlloc allocator.
//--------------------------------------------------------------------------
class StringPoolUsingHeapAlloc
{
public:

    StringPoolUsingHeapAlloc()
        : m_pchNext(nullptr), 
        m_pchLimit(nullptr), 
        m_phdrCur(nullptr)
    {
        m_heap = HeapCreate(0, 0, 0);
        if (m_heap == nullptr)
            throw runtime_error("Can't create an heap with HeapCreate().");

        SYSTEM_INFO si;
        GetSystemInfo(&si);
        m_dwGranularity = static_cast<DWORD>( 
            RoundUp( sizeof(HEADER) + MIN_CBCHUNK, si.dwAllocationGranularity 
            ));
    }

    ~StringPoolUsingHeapAlloc()
    {
        HEADER* phdr = m_phdrCur;
        while (phdr) 
        {
            HEADER * phdrPrev = phdr->m_phdrPrev;
            HeapFree(m_heap, 0, phdr);
            phdr = phdrPrev;
        }
        HeapDestroy(m_heap);
    }

    const wchar_t* DuplicateString(const wstring& 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
    };

    HANDLE    m_heap;
    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 > MAX_CHARALLOC) 
            throw length_error("String too big.");

        wchar_t* psz = m_pchNext;
        if (m_pchNext + cchTotal <= m_pchLimit) 
        {
            m_pchNext += cchTotal;
            wmemcpy(psz, pchBegin, cchTotal);
            return psz;
        }

        SIZE_T cbAlloc = RoundUp(cchTotal * sizeof(wchar_t) + sizeof(HEADER), m_dwGranularity);
        BYTE* pbNext = static_cast<BYTE*>( HeapAlloc(m_heap, 0, cbAlloc) );
        if (pbNext == nullptr) 
            throw bad_alloc();

        m_pchLimit = reinterpret_cast<wchar_t*>(pbNext + cbAlloc);
        HEADER* phdrCur = reinterpret_cast<HEADER*>(pbNext);
        phdrCur->m_phdrPrev = m_phdrCur;
        phdrCur->m_cb = cbAlloc;
        m_phdrCur = phdrCur;
        m_pchNext = reinterpret_cast<wchar_t*>(phdrCur + 1);
        return AllocString(pchBegin, pchEnd);
    }

    StringPoolUsingHeapAlloc(const StringPoolUsingHeapAlloc &);
    StringPoolUsingHeapAlloc & operator=(const StringPoolUsingHeapAlloc &);
};


//--------------------------------------------------------------------------
// 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;
    }

    const wchar_t* DuplicateString(const wstring& 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&);
        Chunk& operator=(const Chunk&);

        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<Chunk*> m_chunks;

    wchar_t* AllocString(const wchar_t* pchBegin, const wchar_t* pchEnd)
    {
        const size_t cchTotal = pchEnd - pchBegin + 1;
        if (cchTotal > kMaxCharAlloc) 
            throw length_error("String too big.");

        wchar_t* dest = m_pchNext;
        if (m_pchNext + cchTotal <= 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->Begin();
        m_pchLimit = newChunk->Begin() + newChunk->Length();
        m_currChunk = newChunk;

        return AllocString(pchBegin, pchEnd);
    }

    StringPoolUsingNew(const StringPoolUsingNew&);
    StringPoolUsingNew& operator=(const StringPoolUsingNew&);
};


//--------------------------------------------------------------------------
// This is just a simple vector<wstring>, to compare performance of this 
// simple and easy approach vs. the other pool allocators.
//--------------------------------------------------------------------------
class StringPoolVectorOfString
{
public:

    StringPoolVectorOfString()
    {
    }

    ~StringPoolVectorOfString()
    {
    }

    const wchar_t* DuplicateString(const wstring& source)
    {
        m_strings.push_back(source);
        return m_strings.back().c_str();
    }

private:
    // Simplest case: a STL vector of STL strings
    vector<wstring> m_strings;

    StringPoolVectorOfString(const StringPoolVectorOfString&);
    StringPoolVectorOfString& operator=(const StringPoolVectorOfString&);
};


//------------------------------------------------------------------------
//                          Perf Measurement
//------------------------------------------------------------------------

long long Counter() 
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return li.QuadPart;
}

long long Frequency() 
{
    LARGE_INTEGER li;
    QueryPerformanceFrequency(&li);
    return li.QuadPart;
}


//--------------------------------------------------------------------------
// Tests
//--------------------------------------------------------------------------

// Prints the first N strings in a vector-like container.
template <typename Container>
void PrintFirst(const Container & c, const size_t firstN)
{
    const size_t n = min(firstN, c.size());
    for (size_t i = 0; i < n; i++)
        wcout << "#" << (i+1) << ": " << c[i] << '\n';
    wcout << endl;
}

// Prints the first N strings using the specified allocator.
template <typename Allocator>
void VerifyAllocator(const vector<wstring>& source, const size_t firstN, const char* allocatorName)
{
    const size_t n = min(firstN, source.size());

    Allocator alloc;
    vector<const wchar_t*> v;

    for (size_t i = 0; i < n; i++)
    {
        v.push_back( alloc.DuplicateString(source[i]) );
    }

    wcout << allocatorName << " :\n";
    PrintFirst(v, n);
}

// Tests a given allocator, returning the execution time in ms.
template <typename Allocator>
double TestAllocator(const vector<wstring>& source, const char* allocatorName)
{
    wcout << "Testing " << allocatorName << " : ";
    long long start = Counter();
    {
        Allocator alloc;
        vector<const wchar_t*> v;

        for (auto it = source.begin(); it != source.end(); ++it)
        {
            v.push_back( alloc.DuplicateString(*it) );
        }
    }
    long long finish = Counter();
    const double time = (finish - start) * 1000.0 / Frequency(); // ms

    wcout << time << " ms\n";
    return time;
}

// Calculates the average in a vector of doubles.
double Average(const vector<double>& data)
{
    if (data.empty())
        throw invalid_argument("Can't compute average of empty vector.");

    double sum = data[0];
    const size_t count = data.size();
    for (size_t i = 1; i < count; ++i)
    {
        sum += data[i];
    }
    return (sum / count);
}

// App entry-point ("test driver").
int main()
{
    static const int kExitOk = 0;
    static const int kExitError = 1;
    try
    {
        wcout << '\n';
        wcout << "Testing VirtualAlloc vs. HeapAlloc vs. new[] allocators vs STL strings.\n";
        wcout << "-----------------------------------------------------------------------\n\n"; 

        wcout << "Preparing some strings for testing...\n";

        const auto shuffled = []() -> vector<wstring> 
        {
            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<wstring> v;
#ifdef _DEBUG
            static const int kLoopCount = 10;
#else
            static const int kLoopCount = 400*1000;
#endif
            for (long long i = 0; i < kLoopCount; ++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;
        }();

        wcout << "Total string count: " << shuffled.size() << "\n\n";
        wcout << "Some verification output ...\n\n";
        wcout << "Original array of strings :\n";
        PrintFirst(shuffled, 5);

        VerifyAllocator<StringPoolUsingVirtualAlloc>(shuffled, 5, "VirtualAlloc");
        VerifyAllocator<StringPoolUsingHeapAlloc>(shuffled, 5, "HeapAlloc");
        VerifyAllocator<StringPoolUsingNew>(shuffled, 5, "new[]");
        VerifyAllocator<StringPoolVectorOfString>(shuffled, 5, "vector<wstring>");

        vector<double> timeVirtualAlloc;
        vector<double> timeHeapAlloc;
        vector<double> timeNew;
        vector<double> timeStlString;

        static const int kTestCount = 10;

        // First execution tests are discarded.
        wcout << "\nWarm up... discard first tests execution.\n";
        TestAllocator<StringPoolUsingVirtualAlloc>(shuffled, "VirtualAlloc");
        TestAllocator<StringPoolUsingHeapAlloc>(shuffled, "HeapAlloc");
        TestAllocator<StringPoolUsingNew>(shuffled, "new[]");
        TestAllocator<StringPoolVectorOfString>(shuffled, "vector<wstring>");

        // Run the tests several times and compute the average for each test.
        for (int i = 0; i < kTestCount; i++)
        {
            wcout << "\nTest loop #" << (i+1) << ":\n";
            timeVirtualAlloc.push_back( TestAllocator<StringPoolUsingVirtualAlloc>(shuffled, "VirtualAlloc") );
            timeHeapAlloc.push_back( TestAllocator<StringPoolUsingHeapAlloc>(shuffled, "HeapAlloc") );
            timeNew.push_back( TestAllocator<StringPoolUsingNew>(shuffled, "new[]") );
            timeStlString.push_back( TestAllocator<StringPoolVectorOfString>(shuffled, "vector<wstring>") );
        }

        // Print average times
        wcout << "\n\n--- Tests summary ---\n";
        wcout << "VirtualAlloc : " << Average(timeVirtualAlloc) << " ms\n";
        wcout << "HeapAlloc    : " << Average(timeHeapAlloc) << " ms\n";
        wcout << "new[]        : " << Average(timeNew) << " ms\n";
        wcout << "STL strings  : " << Average(timeStlString) << " ms\n";
        wcout << '\n';

        return kExitOk;
    }
    catch (const exception& e)
    {
        wcerr << "\n*** ERROR: " << e.what() << '\n';
        return kExitError;
    }
}


////////////////////////////////////////////////////////////////////////////
+3

, new[] , VirtualAlloc.

-, , new T[N]. new , operator new[]. , Visual ++ 2010, operator new[] malloc, Windows API HeapAlloc, CRT, Visual ++ 2012 CRT , HeapCreate. Visual ++ 2012 CRT , GetProcessHeap. , , .

VirtualAlloc . , . , , VirtualAlloc, , . VirtualAlloc .

, , . , (, , ).

, :

String pool using VirtualAlloc: 1162.45 ms
String pool using new[]: 625.842 ms

VirtualAlloc HeapAlloc. HeapCreate(0, 0, 0), VirtualAlloc VirtualFree HeapAlloc HeapFree . ( , , , , new[] , new[].) :

String pool using HeapAlloc: 919.853 ms
String pool using new[]: 636.515 ms

, ! 21%, , new[]. ?

, : . new[] wmemcpy , VirtualAlloc lstrcpyn. wmemcpy memcpy, , . lstrcpyn - Windows API, . VirtualAlloc !

lstrcpyn wmemcpy. :

String pool using HeapAlloc: 636.149 ms
String pool using new[]: 655.479 ms

, : , new[] , , - operator new malloc.

+13

new VirtualAlloc (, , HeapAlloc) , new, VirtualAlloc , , alloocate , . delete , VirtualFree, .

This is exactly the same as using it fgetcfaster than ReadFile- of course, if you read one gigabyte at once, it ReadFilewill probably be an honest bit faster than calling fgetcgazillion times, but if you read one byte at a time, there ReadFilewill be a lot of heaver in the system than with a help fgetcthat will read several (probably 4 Kbytes) of data at a time, and then print a character at a time from this buffer while it is empty.

+7
source

All Articles