Fix bug in merge_blocks_bufferless and improve bufer performance with buffer_and_update_key

This commit is contained in:
Ion Gaztañaga
2018-12-31 00:56:51 +01:00
parent b0e0563fe4
commit 580ec87d09

View File

@@ -143,236 +143,6 @@ typename iterator_traits<ForwardIt>::size_type
return count;
}
template<class T, class RandRawIt = T*>
class adaptive_xbuf
{
adaptive_xbuf(const adaptive_xbuf &);
adaptive_xbuf & operator=(const adaptive_xbuf &);
public:
typedef RandRawIt iterator;
adaptive_xbuf()
: m_ptr(), m_size(0), m_capacity(0)
{}
adaptive_xbuf(RandRawIt raw_memory, std::size_t capacity)
: m_ptr(raw_memory), m_size(0), m_capacity(capacity)
{}
template<class RandIt>
void move_assign(RandIt first, std::size_t n)
{
if(n <= m_size){
boost::move(first, first+n, m_ptr);
std::size_t size = m_size;
while(size-- != n){
m_ptr[size].~T();
}
m_size = n;
}
else{
RandRawIt result = boost::move(first, first+m_size, m_ptr);
boost::uninitialized_move(first+m_size, first+n, result);
m_size = n;
}
}
template<class RandIt>
void push_back(RandIt first, std::size_t n)
{
BOOST_ASSERT(m_capacity - m_size >= n);
boost::uninitialized_move(first, first+n, m_ptr+m_size);
m_size += n;
}
template<class RandIt>
iterator add(RandIt it)
{
BOOST_ASSERT(m_size < m_capacity);
RandRawIt p_ret = m_ptr + m_size;
::new(&*p_ret) T(::boost::move(*it));
++m_size;
return p_ret;
}
template<class RandIt>
void insert(iterator pos, RandIt it)
{
if(pos == (m_ptr + m_size)){
this->add(it);
}
else{
this->add(m_ptr+m_size-1);
//m_size updated
boost::move_backward(pos, m_ptr+m_size-2, m_ptr+m_size-1);
*pos = boost::move(*it);
}
}
void set_size(std::size_t size)
{
m_size = size;
}
void shrink_to_fit(std::size_t const size)
{
if(m_size > size){
for(std::size_t szt_i = size; szt_i != m_size; ++szt_i){
m_ptr[szt_i].~T();
}
m_size = size;
}
}
void initialize_until(std::size_t const size, T &t)
{
BOOST_ASSERT(m_size < m_capacity);
if(m_size < size){
::new((void*)&m_ptr[m_size]) T(::boost::move(t));
++m_size;
for(; m_size != size; ++m_size){
::new((void*)&m_ptr[m_size]) T(::boost::move(m_ptr[m_size-1]));
}
t = ::boost::move(m_ptr[m_size-1]);
}
}
private:
template<class RIt>
static bool is_raw_ptr(RIt)
{
return false;
}
static bool is_raw_ptr(T*)
{
return true;
}
public:
template<class U>
bool supports_aligned_trailing(std::size_t size, std::size_t trail_count) const
{
if(this->is_raw_ptr(this->data()) && m_capacity){
uintptr_t u_addr_sz = uintptr_t(&*(this->data()+size));
uintptr_t u_addr_cp = uintptr_t(&*(this->data()+this->capacity()));
u_addr_sz = ((u_addr_sz + sizeof(U)-1)/sizeof(U))*sizeof(U);
return (u_addr_cp >= u_addr_sz) && ((u_addr_cp - u_addr_sz)/sizeof(U) >= trail_count);
}
return false;
}
template<class U>
U *aligned_trailing() const
{
return this->aligned_trailing<U>(this->size());
}
template<class U>
U *aligned_trailing(std::size_t pos) const
{
uintptr_t u_addr = uintptr_t(&*(this->data()+pos));
u_addr = ((u_addr + sizeof(U)-1)/sizeof(U))*sizeof(U);
return (U*)u_addr;
}
~adaptive_xbuf()
{
this->clear();
}
std::size_t capacity() const
{ return m_capacity; }
iterator data() const
{ return m_ptr; }
iterator end() const
{ return m_ptr+m_size; }
std::size_t size() const
{ return m_size; }
bool empty() const
{ return !m_size; }
void clear()
{
this->shrink_to_fit(0u);
}
private:
RandRawIt m_ptr;
std::size_t m_size;
std::size_t m_capacity;
};
template<class Iterator, class Op>
class range_xbuf
{
range_xbuf(const range_xbuf &);
range_xbuf & operator=(const range_xbuf &);
public:
typedef typename iterator_traits<Iterator>::size_type size_type;
typedef Iterator iterator;
range_xbuf(Iterator first, Iterator last)
: m_first(first), m_last(first), m_cap(last)
{}
template<class RandIt>
void move_assign(RandIt first, std::size_t n)
{
BOOST_ASSERT(size_type(n) <= size_type(m_cap-m_first));
m_last = Op()(forward_t(), first, first+n, m_first);
}
~range_xbuf()
{}
std::size_t capacity() const
{ return m_cap-m_first; }
Iterator data() const
{ return m_first; }
Iterator end() const
{ return m_last; }
std::size_t size() const
{ return m_last-m_first; }
bool empty() const
{ return m_first == m_last; }
void clear()
{
m_last = m_first;
}
template<class RandIt>
iterator add(RandIt it)
{
Iterator pos(m_last);
*pos = boost::move(*it);
++m_last;
return pos;
}
void set_size(std::size_t size)
{
m_last = m_first;
m_last += size;
}
private:
Iterator const m_first;
Iterator m_last;
Iterator const m_cap;
};
template<class RandIt, class Compare>
RandIt skip_until_merge
@@ -407,6 +177,49 @@ void swap_and_update_key
}
}
template<class RandItKeys>
void update_key
(RandItKeys const key_next
, RandItKeys const key_range2
, RandItKeys &key_mid)
{
if (key_next != key_range2) {
::boost::adl_move_swap(*key_next, *key_range2);
if (key_next == key_mid) {
key_mid = key_range2;
}
else if (key_mid == key_range2) {
key_mid = key_next;
}
}
}
template<class RandItKeys, class RandIt, class RandIt2, class Op>
RandIt2 buffer_and_update_key
(RandItKeys const key_next
, RandItKeys const key_range2
, RandItKeys &key_mid
, RandIt begin
, RandIt end
, RandIt with
, RandIt2 buffer
, Op op)
{
if (begin != with) {
while(begin != end) {
op(three_way_t(), begin++, with++, buffer++);
}
::boost::adl_move_swap(*key_next, *key_range2);
if (key_next == key_mid) {
key_mid = key_range2;
}
else if (key_mid == key_range2) {
key_mid = key_next;
}
}
return buffer;
}
///////////////////////////////////////////////////////////////////////////////
//
// MERGE BUFFERLESS
@@ -457,7 +270,7 @@ static SizeType needed_keys_count(SizeType n_block_a, SizeType n_block_b)
template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
typename iterator_traits<RandIt>::size_type
find_next_block
( RandItKeys key_first
( RandItKeys const key_first
, KeyCompare key_comp
, RandIt const first
, typename iterator_traits<RandIt>::size_type const l_block
@@ -488,7 +301,7 @@ typename iterator_traits<RandIt>::size_type
template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
void merge_blocks_bufferless
( RandItKeys key_first
( RandItKeys const key_first
, KeyCompare key_comp
, RandIt const first
, typename iterator_traits<RandIt>::size_type const l_block
@@ -543,8 +356,8 @@ void merge_blocks_bufferless
RandItKeys const key_end (key_first+n_bef_irreg2);
bool is_range1_A = true;
for( ; key_first != key_end; ++key_first){
bool is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_first, *key_mid);
for(RandItKeys key_next = key_first; key_next != key_end; ++key_next){
bool is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
first1 = is_range1_A == is_range2_A
? last1 : partial_merge_bufferless(first1, last1, last1 + l_block, &is_range1_A, comp);
last1 += l_block;
@@ -555,45 +368,6 @@ void merge_blocks_bufferless
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, last_irr2, comp));
}
///////////////////////////////////////////////////////////////////////////////
//
// BUFFERED MERGE
//
///////////////////////////////////////////////////////////////////////////////
template<class RandIt, class Compare, class Op, class Buf>
void op_buffered_merge
( RandIt first, RandIt const middle, RandIt last
, Compare comp, Op op
, Buf &xbuf)
{
if(first != middle && middle != last && comp(*middle, middle[-1])){
typedef typename iterator_traits<RandIt>::size_type size_type;
size_type const len1 = size_type(middle-first);
size_type const len2 = size_type(last-middle);
if(len1 <= len2){
first = boost::movelib::upper_bound(first, middle, *middle, comp);
xbuf.move_assign(first, size_type(middle-first));
op_merge_with_right_placed
(xbuf.data(), xbuf.end(), first, middle, last, comp, op);
}
else{
last = boost::movelib::lower_bound(middle, last, middle[-1], comp);
xbuf.move_assign(middle, size_type(last-middle));
op_merge_with_left_placed
(first, middle, last, xbuf.data(), xbuf.end(), comp, op);
}
}
}
template<class RandIt, class Compare, class XBuf>
void buffered_merge
( RandIt first, RandIt const middle, RandIt last
, Compare comp
, XBuf &xbuf)
{
op_buffered_merge(first, middle, last, comp, move_op(), xbuf);
}
// Complexity: 2*distance(first, last)+max_collected^2/2
//
// Tries to collect at most n_keys unique elements from [first, last),
@@ -847,7 +621,8 @@ void stable_merge
xbuf.clear();
}
else{
merge_bufferless(first, middle, last, comp);
//merge_bufferless(first, middle, last, comp);
merge_adaptive_ONlogN(first, middle, last, comp, xbuf.begin(), xbuf.capacity());
}
}
@@ -1457,6 +1232,7 @@ void op_merge_blocks_with_buf
RandIt first2 = last1;
RandIt const first_irr2 = first2 + n_block_left*l_block;
bool is_range1_A = true;
const size_type len = l_block * n_block_a + l_block * n_block_b + l_irreg1 + l_irreg2; (void)len;
RandItKeys key_range2(key_first);
@@ -1491,9 +1267,12 @@ void op_merge_blocks_with_buf
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((first1 == last1) || (buffer_empty ? !comp(*first_min, last1[-1]) : !comp(*first_min, buffer_end[-1])));
//If buffered, put those elements in place
RandIt res = op(forward_t(), buffer, buffer_end, first1);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_fwd: ", len);
buffer = buffer_end = buf_first;
BOOST_ASSERT(buffer_empty || res == last1); (void)res;
swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
//swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buffer = buf_first, op);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_swp: ", len);
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first2, last2, comp));
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_min, last_min, comp));
first1 = first2;
@@ -1501,18 +1280,22 @@ void op_merge_blocks_with_buf
}
else {
RandIt const unmerged = op_partial_merge_and_save(first1, last1, first2, last2, first_min, buffer, buffer_end, comp, op, is_range1_A);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_mrs: ", len);
bool const is_range_1_empty = buffer == buffer_end;
BOOST_ASSERT(is_range_1_empty || (buffer_end-buffer) == (last1+l_block-unmerged));
if(is_range_1_empty){
buffer = buffer_end = buf_first;
first_min = last_min - (last2 - first2);
//swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buf_first, op);
}
else{
first_min = last_min;
//swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
update_key(key_next, key_range2, key_mid);
}
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!is_range_1_empty || (last_min-first_min) == (last2-unmerged));
swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_swp: ", len);
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first_min, last_min, comp));
is_range1_A ^= is_range_1_empty;
first1 = unmerged;
@@ -1523,31 +1306,32 @@ void op_merge_blocks_with_buf
last1 += l_block;
first2 = last2;
}
RandIt res = op(forward_t(), buffer, buffer_end, first1); (void)res;
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, res, comp));
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_fwd: ", len);
////////////////////////////////////////////////////////////////////////////
//Process irregular B block and remaining A blocks
////////////////////////////////////////////////////////////////////////////
RandIt const last_irr2 = first_irr2 + l_irreg2;
op(forward_t(), first_irr2, first_irr2+l_irreg2, buf_first);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_fwir:", len);
buffer = buf_first;
buffer_end = buffer+l_irreg2;
reverse_iterator<RandItBuf> rbuf_beg(buffer_end);
RandIt dest = op_merge_blocks_with_irreg
((make_reverse_iterator)(key_first + n_block_b + n_block_a), (make_reverse_iterator)(key_mid), inverse<KeyCompare>(key_comp)
, (make_reverse_iterator)(first_irr2), rbuf_beg
, (make_reverse_iterator)(buffer), (make_reverse_iterator)(last_irr2)
, (make_reverse_iterator)(first_irr2), rbuf_beg, (make_reverse_iterator)(buffer), (make_reverse_iterator)(last_irr2)
, l_block, n_block_left, 0, n_block_left
, inverse<Compare>(comp), true, op).base();
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(dest, last_irr2, comp));
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_blocks_w_irg: ", len);
buffer_end = rbuf_beg.base();
BOOST_ASSERT((dest-last1) == (buffer_end-buffer));
op_merge_with_left_placed(is_range1_A ? first1 : last1, last1, dest, buffer, buffer_end, comp, op);
BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" merge_with_left_plc:", len);
BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(is_sorted(first, last_irr2, comp));
}