问题描述:
1.双向队列是程序设计中最常用到的数据结构,它是依据FIFO的操作抽象而出一种数据结构;
2.本文尝试自己封装一个泛型的double queue class,它支持常见的操作;
3.这个类的底层的数据存储容器是以双向链表串起来的内存块(memory chunk )实现,这样做主要是为了性能的考量
4.它支持两边插入和删除操作,其接口功能完全等同于STL 的deque;
5.最后我又封装一个底层容器为STL的deque class,这主要是为了性能比较的考虑;
6.最后我分别比较了以下四种情形的性能:
1)AlDeque class (data container = memory chunk)2) adapter deque class (data container = list )
3) adapter deque class (data container = deque)
7. 性能测试结构发现:
1)相互之间的时间之比基本上10倍关系;2)1消耗时间等于3的十分之一,3消耗时间等于2的十分之一;
3)看过STL源代码的朋友应该知道,deque的内部实现用了相对复杂的数据结构(管理内存,以保证性能和操作),但是性能也不乐观;
4)我的测试用例比较简单,感兴趣的朋友可下载源代码扩展测试;
程序代码:
#ifndef ALDEQUE_H_
#define ALDEQUE_H_
#include <deque>
#include <vector>
#include <list>
#include "windows.h"
/*
* a simple double direction queue. it's under container is memory chunk which can be list pointer
* advantage: 1. good performance compare to stl queue
* 2. simple implementation
*/
template<class T>
class AlDeque
{
public:
AlDeque():m_backBuffer(0),
m_frontBuffer(0), m_backElemCount(0),
m_frontElemCount(0), m_totalCount(0)
{
}
~AlDeque()
{
Clear();
}
bool IsEmpty()
{
return m_totalCount == 0;
}
/*
* release all memory
*
*/
void Clear()
{
Buffer* cur = m_frontBuffer;
while( cur != m_backBuffer )
{
m_frontBuffer = m_frontBuffer->next;
delete cur;
cur = 0;
cur = m_frontBuffer;
}
delete m_backBuffer;
}
/*
* retrieve the number of elements inside queue
*
*/
size_t Size()
{
return m_totalCount;
}
/*
* push element to queue from back
*
*/
void PushBack( const T& data )
{
*PushBack() = data;
}
/*
* push element to queue from front
*
*/
void PushFront( const T& data )
{
*PushFront() = data;
}
/*
* pop element to queue from back
*
*/
void PopBack()
{
if( 0 == m_backBuffer )
return;
m_totalCount--;
m_backElemCount--;
if( 0 == m_backElemCount )
{
Buffer* cur = m_backBuffer;
m_backBuffer = m_backBuffer->prev;
delete cur;
cur = 0;
if( m_backBuffer )
{
m_backElemCount = KSlotCount;
}
else
{
m_frontBuffer = 0;
return;
}
}
}
/*
* pop element to queue from front
*
*/
void PopFront()
{
if( 0 == m_frontBuffer )
return;
m_totalCount--;
m_frontElemCount--;
if( 0 == m_frontElemCount )
{
Buffer* cur = m_frontBuffer;
m_frontBuffer = m_frontBuffer->next;
delete cur;
cur = 0;
if( m_frontBuffer )
{
m_frontElemCount = KSlotCount;
}
else
{
m_backBuffer = 0;
return;
}
}
}
/*
* retrieve element to queue from front
*
*/
T& Front()
{
assert( m_frontBuffer && m_frontElemCount );
return m_frontBuffer->buf[KSlotCount - m_frontElemCount];
}
/*
* retrieve element to queue from back
*
*/
T& Back()
{
assert( m_backBuffer && m_backElemCount );
return m_backBuffer->buf[ m_backElemCount - 1 ];
}
protected:
//disallow copy constructor and assignment operator
AlDeque( const AlDeque& rhs )
{
}
AlDeque& operator = ( const AlDeque& rhs )
{
}
// implementation of push from front
T* PushFront()
{
m_totalCount++;
if( 0 == m_frontBuffer )
{
m_frontBuffer = new Buffer;
assert( m_frontBuffer );
if( 0 == m_backBuffer )
{
m_backBuffer = m_frontBuffer;
m_backElemCount = KSlotCount; //mark invalid for back insert
}
m_frontElemCount = 0;
}
else
{
if( KSlotCount == m_frontElemCount ) //full currently buffer
{
Buffer* newBuf = new Buffer;
assert( newBuf );
newBuf->next = m_frontBuffer;
m_frontBuffer->prev = newBuf;
m_frontBuffer = newBuf;
m_frontElemCount = 0;
}
}
return &m_frontBuffer->buf[ KSlotCount - (++m_frontElemCount)];
}
// implementation of push from back
T* PushBack()
{
m_totalCount++;
if( 0 == m_backBuffer )
{
m_backBuffer = new Buffer;
if( 0 == m_frontBuffer )
{
m_frontBuffer = m_backBuffer;
m_frontElemCount = KSlotCount; //mark invalid for front insert
}
m_backElemCount = 0;
}
else
{
if( KSlotCount == m_backElemCount ) //full currently buffer
{
Buffer* newBuf = new Buffer;
assert( newBuf );
newBuf->prev = m_backBuffer;
m_backBuffer->next = newBuf;
m_backBuffer = newBuf;
m_backElemCount = 0;
}
}
return &m_backBuffer->buf[m_backElemCount++];
}
protected:
enum
{
KSlotCount = 64
};
struct Buffer
{
T buf[KSlotCount];
Buffer* next;
Buffer* prev;
Buffer():next(0), prev(0)
{
memset( buf, 0x00, sizeof(buf) );
}
};
Buffer* m_backBuffer; // data buffer corresponding to back operation
Buffer* m_frontBuffer; // data buffer corresponding to front operation
size_t m_backElemCount; //
size_t m_frontElemCount;
size_t m_totalCount; // total elements
};
/*
* adapter class
*
*/
template<class T, class Container = std::list<T> >
class DequeAdaptStl
{
public:
DequeAdaptStl():m_dataContainer()
{
}
~DequeAdaptStl()
{
m_dataContainer.clear();
}
void Clear()
{
m_dataContainer.clear();
}
size_t Size()
{
return m_dataContainer.size();
}
void PushBack( const T& data )
{
m_dataContainer.push_back( data );
}
void PushFront( const T& data )
{
m_dataContainer.push_front( data );
}
void PopBack()
{
m_dataContainer.pop_back();
}
void PopFront()
{
m_dataContainer.pop_front();
}
T& Front()
{
return m_dataContainer.front();
}
T& Back()
{
return m_dataContainer.back();
}
protected:
Container m_dataContainer;
};
/*
* unit test
*
*/
template<class container>
void UnitTestDeque( container& queObj, const char* str )
{
const int len = 64 * 10000;
unsigned long start = GetTickCount();
//unit test 1
// insert and pop in corresponding direction
{
for( int i = 0; i < len; i++ )
{
queObj.PushBack( i );
}
for( int i = 0; i < len; i++ )
{
int sel = queObj.Back();
assert( sel == len - 1 - i );
queObj.PopBack();
}
for( int i = 0; i < len; i++ )
{
queObj.PushFront( i );
}
for( int i = 0; i < len; i++ )
{
int sel = queObj.Front();
assert( sel == len - i - 1);
queObj.PopFront();
}
}
//unit test 2
// insert and pop in reverse direction
{
for( int i = 0; i < len; i++ )
{
queObj.PushBack( i );
}
for( int i = 0; i < len; i++ )
{
int sel = queObj.Front();
assert( sel == i );
queObj.PopFront();
}
for( int i = 0; i < len; i++ )
{
queObj.PushFront( i );
}
for( int i = 0; i < len; i++ )
{
int sel = queObj.Back();
assert( sel == i );
queObj.PopBack();
}
}
//unit test 2
// test release interface
{
for( int i = 0; i < len; i++ )
{
queObj.PushBack( i );
}
for( int i = 0; i < len; i++ )
{
queObj.PushFront( i );
}
queObj.Clear();
}
unsigned long interval = GetTickCount() - start;
printf( "%s consume time is %d \n", str, interval );
}
/*
* Test interface
*
*/
void TestAlDeque()
{
AlDeque<int> alDeque;
UnitTestDeque( alDeque, "the deqeue of memory chunk" );
DequeAdaptStl<int> dequeList;
UnitTestDeque( dequeList, "the deqeue of container list" );
DequeAdaptStl<int, std::deque<int> > dequeSTL;
UnitTestDeque( dequeSTL, "the deqeue really" );
getchar();
}
#endif
compile and run in visual studio 2005
test result as follows: