Boost logo

Boost :

From: Slawomir Lisznianski (slisznianski_at_[hidden])
Date: 2004-02-07 13:31:59


I apologize if my observation has already been discussed and course agreed.

When using Boost with multi-threaded programs, shared_ptr synchronizes
access to internal usage count variable. I understand the rationale for
guarding the shared resource as default behavior in MT environment,
however I think users might appreciate the ability to override it.

Currently, users either ignore, or are unaware of, performance cost
imposed by locking, or choose, with all it's implications, an option of
using non-owning observer of an object owned by shared_ptr.

During tests involving manipulations of shared_ptr variables similar to
those performed by algorithms on container elements – their copying and
removal – a significant overhead was measured. During tests, care was
taken to assure that timing did not include creation of new objects
managed by shared_ptrs nor their destruction.

One way to customize the shared_ptr without breaking existing code would
be to introduce an optional template parameter stating locking policy.

Attached you will find test code used on a win32 platform that produced
the following results on Intel Pentium 4 2.66Mhz single processor
machine. Each line represents separate testing iteration, so averages
can be calculated:

BOOST_HAS_THREADS is: TRUE
Elapsed time: 845605 microseconds
Elapsed time: 817763 microseconds
Elapsed time: 802840 microseconds
Elapsed time: 790676 microseconds
Elapsed time: 790839 microseconds
Elapsed time: 793012 microseconds
Elapsed time: 790474 microseconds
Elapsed time: 788396 microseconds
Elapsed time: 794611 microseconds
Elapsed time: 792640 microseconds
Press any key

BOOST_HAS_THREADS is: FALSE
Elapsed time: 87914 microseconds
Elapsed time: 87753 microseconds
Elapsed time: 88958 microseconds
Elapsed time: 86546 microseconds
Elapsed time: 86437 microseconds
Elapsed time: 86520 microseconds
Elapsed time: 86433 microseconds
Elapsed time: 88519 microseconds
Elapsed time: 86420 microseconds
Elapsed time: 87535 microseconds
Press any key

The time overhead mentioned above is meant to serve as an example from a
particular win32 platform. Results will vary across platforms and
hardware configurations.

Regards,

Slawomir Lisznianski; [ www.rhapsodia.org ]


/*
 * Copyright (C) 2003-2004
 * Slawomir Lisznianski <slisznianski_at_[hidden]>
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation. Slawomir Lisznianski makes no
 * representations about the suitability of this software for any
 * purpose. It is provided "as is" without express or implied warranty.
 *
 */

#include <string>
#include <iostream>
#include <stdexcept>

#include <boost/shared_ptr.hpp>

#include <Windows.h>

// VC++ 6 library doesn't define it.
//
std::ostream& operator<<(std::ostream& os, __int64 i )
{
  char buf[20];
  sprintf(buf,"%I64d", i );
  os << buf;
  return os;
}

struct Timer
{
  Timer(std::ostream& out) :
    _M_out(out)
  {
      if (!::QueryPerformanceFrequency(&_M_frequency))
        throw std::runtime_error("Unable to retrieve frequency of the high-resolution performance counter.");
      if (!::QueryPerformanceCounter(&_M_startEvent))
        throw std::runtime_error("Unable to retrieve value of the high-resolution performance counter.");
  }

  ~Timer()
  {
    if (!::QueryPerformanceCounter(&_M_endEvent))
          _M_out << "Unable to retrieve value of the high-resolution performance counter.";
    else
    {
      LONGLONG elapsedTicks__ = _M_endEvent.QuadPart - _M_startEvent.QuadPart;
      double elapsedTime__ = 0;
      _M_out << "Elapsed time: ";
      if (_M_frequency.QuadPart > 1000000)
      {
        elapsedTime__ = (elapsedTicks__ / (_M_frequency.QuadPart / 1000000));
        _M_out << elapsedTime__ << " microseconds" << std::endl;
      }
      else if (_M_frequency.QuadPart > 1000)
      {
        elapsedTime__ = (elapsedTicks__ / (_M_frequency.QuadPart / 1000));
        _M_out << elapsedTime__ << " miliseconds" << std::endl;
      }
      else
      {
        _M_out << elapsedTicks__ << " ticks (tick: 1/" << _M_frequency.QuadPart << " of second)" << std::endl;
      }
    }
      
  }

  std::ostream& _M_out;
  LARGE_INTEGER _M_startEvent;
  LARGE_INTEGER _M_endEvent;
  LARGE_INTEGER _M_frequency;
};

void run()
{
  boost::shared_ptr<int> ptrA__(new int(0)), ptrB__, ptrC__;
  Timer timer__(std::cout);
  for (int i=0; i<4000000; ++i)
  {
    ptrB__ = ptrA__;
    ptrC__ = ptrB__;
  }
}

int
main(int, char**)
{
  try{
  std::cout << "BOOST_HAS_THREADS is: ";
#if !defined(BOOST_HAS_THREADS)
  std::cout << "FALSE" << std::endl;
#else
  std::cout << "TRUE" << std::endl;
#endif
  for (int i=0; i<10; ++i)
   run();
  } catch (std::exception& e)
  { std::cerr << e.what() << std::endl; }
  return 0;
}


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