Home
Fractals
Tutorials
Books
My blog
My LinkedIn Profile

BOOKS i'm reading

Napoleon Hill Keys to Success: The 17 Principles of Personal Achievement, Napoleon Hill, ISBN: 978-0452272811
The 4-Hour Workweek: Escape 9-5, Live Anywhere, and Join the New Rich (Expanded and Updated), Timothy Ferriss, ISBN: 978-0307465351
The Fountainhead, Ayn Rand, ISBN: 0452273331

Enhance your dynamic memory allocation in C++ with an undocumented MFC class (CFixedAlloc)

Olivier Langlois, IT superstar coach, Dominate LinkedIn Formula author
by Olivier Langlois

Contents





The demo program

The demo program is very simple. It just declares two almost identical classes: one without CFixedAlloc and the other using it. Then, a bunch of objects is created. This operation is timed, and once completed, the result is displayed. For computing the size overhead of the class without CFixedAlloc, I used the overhead you would have if your program was using the __V6_HEAP which is very unlikely and thus the computed value might not be totally exact. However, from experience, it seems to be quite accurate.

#define ITER_NUM 1000*1024

class A
{
public:
    A( A *next ) : m_next(next) {}
    A  *m_next;
    int dummy1;
    int dummy2;
};

class B
{
public:
    B( B *next ) : m_next(next) {}
    B  *m_next;
    int dummy1;
    int dummy2;

    DECLARE_FIXED_ALLOC(B);
};

IMPLEMENT_FIXED_ALLOC(B,ITER_NUM);

void CCFixedAllocDemoDlg::OnTestTimebutton()
{
    // TODO: Add your control notification
    // handler code here
    A *curAObj, *firstAObj = NULL;
    B *curBObj, *firstBObj = NULL;
    DWORD startA, endA, startB, endB;
    register int i;
    {
        CWaitCursor wait;

        startA = GetTickCount();

        for( i = 0; i < ITER_NUM; i++ )
        {
            firstAObj = new A(firstAObj);
        }
        while( firstAObj )
        {
            curAObj = firstAObj->m_next;
            delete firstAObj;
            firstAObj = curAObj;
        }

        startB = endA = GetTickCount();

        for( i = 0; i < ITER_NUM; i++ )
        {
            firstBObj = new B(firstBObj);
        }
        while( firstBObj )
        {
            curBObj = firstBObj->m_next;
            delete firstBObj;
            firstBObj = curBObj;
        }

        endB = GetTickCount();
    }

    displayResult( endA-startA,endB-startB );
}

#define BYTES_PER_PARA 16

void CCFixedAllocDemoDlg::displayResult( DWORD timeA,
                                          DWORD timeB )
{
    TCHAR buf[1024];

    /*
     * Each object A takes 32 bytes. See article for details
     */
    int overheadA = (32-sizeof(A))*ITER_NUM;

    /*
     * First compute the allocated size then substract the
     * requested size
     */
    int overheadB =
      (8+sizeof(B)*ITER_NUM + sizeof(CPlex) + (BYTES_PER_PARA-1))
                    & ~(BYTES_PER_PARA - 1);
    overheadB    -= sizeof(B)*ITER_NUM;

    wsprintf( buf, __TEXT("Creating and destroying %d objects\n")
                   __TEXT("without CFixedAlloc\t: %4d ms\n")
                   __TEXT("with CFixedAlloc\t: %4d ms\n")
                   __TEXT("You saved %d bytes with CFixedAlloc"),
                   ITER_NUM, timeA, timeB,
                   overheadA - overheadB );
    MessageBox(buf,__TEXT("Results"));
}

Suggestions

You could make a single thread version of CFixedAlloc just by making a private copy of the class and remove the critical section stuff to further increase the performance gain. Johan Strand pointed out that the single thread support has already been added to MFC7 (VC++.NET). If you want to use the single thread version of CFixedAlloc, you can use the following macros:

// DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC_NOSYNC(class_name) \
public: \
    void* operator new(size_t size) \
    { \
        ASSERT(size == s_alloc.GetAllocSize()); \
        UNUSED(size); \
        return s_alloc.Alloc(); \
    } \
    void* operator new(size_t, void* p) \
        { return p; } \
    void operator delete(void* p) { s_alloc.Free(p); } \
    void* operator new(size_t size, LPCSTR, int) \
    { \
        ASSERT(size == s_alloc.GetAllocSize()); \
        UNUSED(size); \
        return s_alloc.Alloc(); \
    } \
protected: \
    static CFixedAllocNoSync s_alloc \

// IMPLEMENT_FIXED_ALLOC_NOSYNC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC_NOSYNC(class_nbame, block_size) \
CFixedAllocNoSync class_name::s_alloc(sizeof(class_name), block_size) \

There is a mistake in the IMPLEMENT_FIXED_ALLOC_NOSYNC() macro. The macro parameter class_nbame should be class_name. These macros do not appear to be used by MFC which makes sense since there is a syntax error in the macro. That means that the no sync version has probably not been tested by the MFC team. However, once the syntax error is fixed and since the change was trivial, it should work fine. You can fix the syntax error and use the fixed fixalloc.h in your program without having to recompile MFC (the macro isn't used anywhere in MFC). I have modified the demo program so that you can test out the single thread version. All you have to do is:

  1. Fix the syntax error in your fixalloc.h file.
  2. Uncomment the following define statement.
/*
 * Uncomment the next define statement
 * to use the single thread version of
 * CFixedAlloc. Prior to recompilation,
 * you must fix a syntax error in
 * fixalloc.h. The IMPLEMENT_FIXED_ALLOC_NOSYNC()
 * macro parameter class_nbame
 * should be rename to class_name.
 *
 * Note: The single thread version is only
 * available in VC++.NET and up.
 */
//#define _USE_SINGLE_THREAD

#ifndef _USE_SINGLE_THREAD
class B
{
public:
    B( B *next ) : m_next(next) {}
    B  *m_next;
    int dummy1;
    int dummy2;

    DECLARE_FIXED_ALLOC(B);
};

IMPLEMENT_FIXED_ALLOC(B,ITER_NUM);
#else
class B
{
public:
    B( B *next ) : m_next(next) {}
    B  *m_next;
    int dummy1;
    int dummy2;

    DECLARE_FIXED_ALLOC_NOSYNC(B);
};

IMPLEMENT_FIXED_ALLOC_NOSYNC(B,ITER_NUM);
#endif

My testing of the no sync version has shown a speedup improvement of 40% over the original CFixedAlloc version. The bottom line is use the no sync version if you are not developing a multithread application.

Conclusion

That is it! I hope you enjoyed this C++ Windows programming tutorial and that you have found it useful.  In the next section, you will find the books that I have consulted to build this C++ Windows programming tutorial. If you liked this tutorial then you might be interested in the follow-up tutorial that presents a real case on how to apply the CFixedAlloc optimzation on an existing MFC software.

Bibliography

History

  • 01-27-2006
    • Include comments on VC++2005 warnings reported by Allan Braun
    • Table of content added
  • 01-20-2006
    • Expand the "How to use" section based on the comments from readers.
    • Expand the "Suggestions" section based on the comments from Johan Strand.
  • 11-28-2005
    • Original article.


Page 1 2 3 4

Back to the tutorials list

Home :: Fractals :: Tutorials :: Books :: My blog :: My LinkedIn Profile :: Contact