Subject: [Boost-bugs] [Boost C++ Libraries] #2810: Optimization for list::merge performance
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2009-02-27 20:13:04
#2810: Optimization for list::merge performance
------------------------------------+---------------------------------------
Reporter: andysem | Owner: igaztanaga
Type: Patches | Status: new
Milestone: Boost 1.39.0 | Component: intrusive
Version: Boost 1.38.0 | Severity: Optimization
Keywords: list merge performance |
------------------------------------+---------------------------------------
The current implementation of the list::merge method is quite inefficient
since it always iterates through the *this sequence, even if there are no
more elements to insert. It also inserts elements from the other sequence
one by one, which may generate a lot of overhead for node pointers
adjustments in case if there are equivalent nodes in either of the
sequences.
I suggest the following optimized version of the merge method (see the
my_merge function):
{{{
#include <cstdlib>
#include <iostream>
#include <boost/checked_delete.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/intrusive/options.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/list_hook.hpp>
namespace bi = boost::intrusive;
using boost::posix_time::ptime;
typedef bi::list_base_hook<
bi::link_mode< bi::auto_unlink >
> BaseHook_t;
struct MyData :
public BaseHook_t
{
struct OrderByKey
{
typedef bool result_type;
result_type operator() (MyData const& left, MyData const& right)
const
{
return left.m_Key < right.m_Key;
}
};
struct Cloner
{
typedef MyData* result_type;
result_type operator() (MyData const& that) const
{
return new MyData(that);
}
};
int m_Key;
explicit MyData(int k) : m_Key(k) {}
};
typedef bi::list<
MyData,
bi::base_hook< BaseHook_t >,
bi::constant_time_size< false >
> List_t;
template< typename PredicateT >
void my_merge(List_t& this_, List_t& x, PredicateT p)
{
typedef List_t::const_iterator const_iterator;
typedef List_t::size_type size_type;
const_iterator b = this_.cbegin(), e = this_.cend();
const_iterator ex = x.cend();
while (!x.empty())
{
const_iterator bx = x.cbegin();
while (b != e && p(*bx, *b))
{
++b;
}
if (b != e)
{
// determine the end of the range of x to inject at b
size_type n = 0;
do
{
++bx;
++n;
}
while (bx != ex && p(*bx, *b));
this_.splice(b, x, x.cbegin(), bx, n);
}
else
{
// the rest of the list is to be appended to the end
this_.splice(e, x);
break;
}
}
}
int main(int, char*[])
{
enum
{
LIST1_SIZE = 1000000,
LIST2_SIZE = LIST1_SIZE
};
List_t l11, l12;
for (unsigned int i = 0; i < LIST1_SIZE; ++i)
{
MyData* p = new MyData(std::rand());
l11.push_back(*p);
}
l11.sort(MyData::OrderByKey());
for (unsigned int i = 0; i < LIST2_SIZE; ++i)
{
MyData* p = new MyData(std::rand());
l12.push_back(*p);
}
l12.sort(MyData::OrderByKey());
List_t l21, l22;
l21.clone_from(l11, MyData::Cloner(), boost::checked_deleter< MyData
>());
l22.clone_from(l12, MyData::Cloner(), boost::checked_deleter< MyData
>());
ptime start1, end1, start2, end2;
start1 = boost::date_time::microsec_clock< ptime >::universal_time();
l11.merge(l12, MyData::OrderByKey());
end1 = boost::date_time::microsec_clock< ptime >::universal_time();
start2 = boost::date_time::microsec_clock< ptime >::universal_time();
my_merge(l21, l22, MyData::OrderByKey());
end2 = boost::date_time::microsec_clock< ptime >::universal_time();
unsigned int duration1 = end1.time_of_day().total_microseconds() -
start1.time_of_day().total_microseconds();
unsigned int duration2 = end2.time_of_day().total_microseconds() -
start2.time_of_day().total_microseconds();
std::cout << "Boost.Intrusive version: " << duration1 << " us,
optimized: " << duration2 << " us" << std::endl;
l11.clear_and_dispose(boost::checked_deleter< MyData >());
l12.clear_and_dispose(boost::checked_deleter< MyData >());
l21.clear_and_dispose(boost::checked_deleter< MyData >());
l22.clear_and_dispose(boost::checked_deleter< MyData >());
return 0;
}
}}}
This test case shows that my_merge is faster than list::merge by an order
of magnitude and even more if LIST1_SIZE != LIST2_SIZE.
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/2810> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:49:59 UTC