optimize shared_vector for storing non-POD types

pass values by reference where appropriate.
When reallocating arrays of shared_ptr
"move" with swap() instead of operator=
to avoid ref counter inc and dec for each
element.
This commit is contained in:
Michael Davidsaver
2013-06-03 15:04:38 -04:00
parent 4294710d9e
commit bc3187a3f6
2 changed files with 70 additions and 9 deletions

View File

@@ -36,6 +36,31 @@ namespace detail {
template<typename T> struct decorate_const { typedef const T type; };
template<typename T> struct decorate_const<const T> { typedef const T type; };
// How values should be passed as arguments to shared_vector methods
// really should use boost::call_traits
template<typename T> struct call_with { typedef T type; };
template<typename T> struct call_with<std::tr1::shared_ptr<T> >
{ typedef const std::tr1::shared_ptr<T>& type; };
template<> struct call_with<std::string> { typedef const std::string& type; };
// For lack of C++11's std::move do our own special handling of shared_ptr
template<typename T>
struct moveme {
template<typename arg>
static void op(const arg& a, const arg& b, const arg& c)
{std::copy(a,b,c);}
};
template<typename T>
struct moveme<std::tr1::shared_ptr<T> > {
template<typename arg>
static void op(arg a, arg b, arg c)
{
// "move" with swap to avoid ref counter operations
for(;a!=b;a++,c++)
c->swap(*a);
}
};
/* All the parts of shared_vector which
* don't need special handling for E=void
*/
@@ -194,6 +219,7 @@ template<typename E>
class shared_vector : public detail::shared_vector_base<E>
{
typedef detail::shared_vector_base<E> base_t;
typedef typename detail::call_with<E>::type param_type;
public:
typedef E value_type;
typedef E& reference;
@@ -222,7 +248,7 @@ public:
{}
//! @brief Allocate (with new[]) a new vector of size c and fill with value e
shared_vector(size_t c, E e)
shared_vector(size_t c, param_type e)
:base_t(new E[c], 0, c)
{
std::fill_n(this->m_data.get(), this->m_count, e);
@@ -277,8 +303,8 @@ public:
return;
pointer temp=new E[i];
try{
std::copy(begin(), end(), temp);
this->m_data.reset(temp, detail::default_array_deleter<E*>());
detail::moveme<E>::op(begin(), end(), temp);
this->m_data.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
delete[] temp;
throw;
@@ -314,10 +340,10 @@ public:
try{
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
detail::moveme<E>::op(begin(),
begin()+std::min(i,this->size()),
temp);
this->m_data.reset(temp, detail::default_array_deleter<E*>());
this->m_data.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
delete[] temp;
throw;
@@ -331,7 +357,7 @@ public:
*
* see @ref resize(size_t)
*/
void resize(size_t i, E v) {
void resize(size_t i, param_type v) {
size_t oldsize=this->size();
resize(i);
if(this->size()>oldsize) {
@@ -360,7 +386,7 @@ public:
if(this->unique())
return;
shared_pointer_type d(new E[this->m_total], detail::default_array_deleter<E*>());
std::copy(this->m_data.get()+this->m_offset,
detail::moveme<E>::op(this->m_data.get()+this->m_offset,
this->m_data.get()+this->m_offset+this->m_count,
d.get());
this->m_data.swap(d);
@@ -387,7 +413,7 @@ public:
// Modifications
void push_back(const E& v)
void push_back(param_type v)
{
resize(this->size()+1);
back() = v;

View File

@@ -160,12 +160,14 @@ static void testShare()
one.make_unique();
testOk1(one[1]==43);
one[1] = 143;
testOk1(two[1]==43);
testOk1(three[1]==43);
two.resize(two.size());
testOk1(two[1]==43);
two[1] = 243;
testOk1(one[1]==143);
testOk1(three[1]==43);
@@ -177,11 +179,13 @@ static void testShare()
one.resize(2);
testOk1(one.size()==2);
testOk1(one[1]==143);
testOk1(two.size()==15);
testOk1(three.size()==15);
two.resize(20, 5000);
testOk1(two[1]==243);
testOk1(one.size()==2);
testOk1(two.size()==20);
testOk1(three.size()==15);
@@ -335,9 +339,39 @@ static void testVoid()
testOk1(typed.size()==2);
}
struct dummyStruct {};
static void testNonPOD()
{
testDiag("Test vector of non-POD types");
epics::pvData::shared_vector<std::string> strings(6);
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
testOk1(strings[0].empty());
testOk1(structs[0].get()==NULL);
structs[1].reset(new dummyStruct);
dummyStruct *temp = structs[1].get();
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
testOk1(!structs.unique());
testOk1(structs[1].unique());
testOk1(structs2[1].get()==temp);
structs2.make_unique();
testOk1(structs.unique());
testOk1(!structs[1].unique());
testOk1(structs2[1].get()==temp);
}
MAIN(testSharedVector)
{
testPlan(101);
testPlan(113);
testDiag("Tests for shared_vector");
testDiag("sizeof(shared_vector<int>)=%lu",
@@ -351,5 +385,6 @@ MAIN(testSharedVector)
testConst();
testSlice();
testVoid();
testNonPOD();
return testDone();
}