Boost logo

Boost :

From: davidbien_at_[hidden]
Date: 2000-04-17 20:31:55


I have done a performance test of shared_ptr<int> versus _gcr<int>
( my implementation of reference counted objects ) optimized for
speed using intel v4.0 on a Compaq Pentium 266 laptop :-). I expected
the results to be either inconclusive or say that my implementation
was slower.

Interestingly enough, the size of the executable was exactly the same
( now perhaps this is less significant than it seems ) 53,248 bytes.
But, the running time of the executable, using 100 iterations of
10000 random elements inserted into an STL set and the same random
seed, indicates that the reference counted objects run at about 80%
of the runtime of the shared smart pointers. This is likely because
there is one dynamic memory object per reference in the case of the
referenced object implementation, whereas there are two dynamic
memory objects per reference object in the shared smart pointer
implementation.

Now, granted, the paradigms presented by the two different
implementations are different ( shared_ptr<T> being arguably more
flexible than _gcr ), though neither is a replacement for the other.
In some cases, where speed is of special concern, perhaps reference
counted objects are a better choice.

Also, shared_ptr<T>'s inherently support polymorphism ( since they
contain a pointer they did not create themselves and call delete() on
that pointer - i.e. they don't need to know the size of the most
derived object when deallocating - that is maintained by the heap ).
I haven't tested the polymorphic version of reference counted objects
versus shared_ptr<T>. It will be interesting to see the performance
results on that. I will work it up and post it soon.

Here are some sample runs:

Reference counted object using SGI STL's global pool allocator
( allocator< char > )
C:\src\testtmpl\Release>testrco 100 10000 10000
Total: [18215] Average[182].
C:\src\testtmpl\Release>testrco 100 10000 10000
Total: [16916] Average[169].
C:\src\testtmpl\Release>testrco 100 10000 10000
Total: [17315] Average[173].
C:\src\testtmpl\Release>testrco 100 10000 10000
Total: [17592] Average[175].

shared_ptr<int> using SGI STL's pool allocator for set<>:
C:\src\testtmpl\Release>testboost 100 10000 10000
Total: [24932] Average[249].
C:\src\testtmpl\Release>testboost 100 10000 10000
Total: [24157] Average[241].
C:\src\testtmpl\Release>testboost 100 10000 10000
Total: [23904] Average[239].
C:\src\testtmpl\Release>testboost 100 10000 10000
Total: [25960] Average[259].

Reference counted object using malloc/free allocator.
C:\src\testtmpl\Release>testrco_malloc.exe 100 10000 10000
Total: [19742] Average[197].
C:\src\testtmpl\Release>testrco_malloc.exe 100 10000 10000
Total: [20607] Average[206].
C:\src\testtmpl\Release>testrco_malloc.exe 100 10000 10000
Total: [22295] Average[222].
C:\src\testtmpl\Release>testrco_malloc.exe 100 10000 10000
Total: [20112] Average[201].

shared_ptr<int> using malloc/free allocator for set<>.
C:\src\testtmpl\Release>testboost_malloc 100 10000 10000
Total: [24880] Average[248].
C:\src\testtmpl\Release>testboost_malloc 100 10000 10000
Total: [24665] Average[246].
C:\src\testtmpl\Release>testboost_malloc 100 10000 10000
Total: [24591] Average[245].
C:\src\testtmpl\Release>testboost_malloc 100 10000 10000
Total: [24131] Average[241].

Here is the code ( header inclusion excluded ):

This code shared by both programs:

int
main( int argc, char ** argv )
{
  if ( argc < 4 )
  {
    cerr << "main(): usage\n" << argv[0] << "<num iterations> <num
elements> <rand seed>\n";
    return -1;
  }

  int iIterations = atoi( argv[1] );
  int iNumElems = atoi( argv[2] );
  int iRandSeed = atoi( argv[3] );
  srand( iRandSeed );

  // Switch between allocators:
  typedef _TyMallocAllocator _TyAllocator;
//typedef allocator< char > _TyAllocator;

Here is the shared_ptr<int> test:

  typedef shared_ptr< int > _TySharedInt;
  typedef set< _TySharedInt,
    less< _TySharedInt >, _TyAllocator > _TySetInt;
  typedef pair< _TySetInt::iterator, bool > _TyPibInt;

  DWORD dwStart = GetTickCount();
  int iNumIter = iIterations;
  while ( iIterations-- )
  {
    _TySetInt setInts;
    _TySharedInt si;
    for ( int _i = 0; _i < iNumElems; ++_i )
    {
      int * ip = new int( rand() );
      si.reset( ip );
      setInts.insert( si );
    }
  }

  DWORD dwTotal = GetTickCount() - dwStart;
  cout << "Total: [" << dwTotal << "] Average["
                      << ( dwTotal / iNumIter ) << "].\n";
  return 0;
}
namespace std {
// We specialize less for shared_ptr<int> to
// compare the contained ints.
template<>
  struct less< boost::shared_ptr<int> >
    : binary_function<boost::shared_ptr<int>, boost::shared_ptr<int>,
bool>
  {
    bool operator()(const boost::shared_ptr<int>& a,
        const boost::shared_ptr<int>& b) const
      { return less<int>()(*a.get(),*b.get()); }
  };
}

This is the test code for the reference counted objects:

  const bool kfCopyOnWrite = true;
  typedef _gco< int, _TyAllocator, true, true > _TyGcoInt;
  typedef _gcp< int, _TyGcoInt, kfCopyOnWrite > _TyGcpInt;
  typedef _gcr< int, _TyGcoInt, kfCopyOnWrite > _TyGcrInt;

  typedef set< _TyGcrInt, less< _TyGcrInt >, _TyAllocator > _TySetInt;
  typedef pair< _TySetInt::iterator, bool > _TyPibInt;

  DWORD dwStart = GetTickCount();

  int iNumIter = iIterations;
  while ( iIterations-- )
  {
    _TySetInt setInts;
    _TyGcrInt gcrInt;
    for ( int _i = 0; _i < iNumElems; ++_i )
    {
      // Release the current element and create a new in its place:
      gcrInt.Create1( rand() );
      setInts.insert( gcrInt );
    }
  }

  DWORD dwTotal = GetTickCount() - dwStart;

  cout << "Total: [" << dwTotal << "] Average["
                      << ( dwTotal / iNumIter ) << "].\n";

  return 0;
}

bien


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk