//----------------------------------------------------------------------------
|
/// @file merge_blocks.hpp
|
/// @brief contains the class merge_blocks, which is part of the
|
/// block_indirect_sort algorithm
|
///
|
/// @author Copyright (c) 2016 Francisco Jose Tapia (fjtapia@gmail.com )\n
|
/// Distributed under the Boost Software License, Version 1.0.\n
|
/// ( See accompanying file LICENSE_1_0.txt or copy at
|
/// http://www.boost.org/LICENSE_1_0.txt )
|
/// @version 0.1
|
///
|
/// @remarks
|
//-----------------------------------------------------------------------------
|
#ifndef __BOOST_SORT_PARALLEL_DETAIL_MERGE_BLOCKS_HPP
|
#define __BOOST_SORT_PARALLEL_DETAIL_MERGE_BLOCKS_HPP
|
|
#include <atomic>
|
#include <boost/sort/block_indirect_sort/blk_detail/backbone.hpp>
|
#include <boost/sort/common/range.hpp>
|
#include <future>
|
#include <iostream>
|
#include <iterator>
|
|
namespace boost
|
{
|
namespace sort
|
{
|
namespace blk_detail
|
{
|
//----------------------------------------------------------------------------
|
// USING SENTENCES
|
//----------------------------------------------------------------------------
|
namespace bsc = boost::sort::common;
|
namespace bscu = bsc::util;
|
using bsc::range;
|
using bsc::is_mergeable;
|
using bsc::merge_uncontiguous;
|
//
|
///---------------------------------------------------------------------------
|
/// @struct merge_blocks
|
/// @brief This class merge the blocks. The blocks to merge are defined by two
|
/// ranges of positions in the index of the backbone
|
//----------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
struct merge_blocks
|
{
|
//-----------------------------------------------------------------------
|
// D E F I N I T I O N S
|
//-----------------------------------------------------------------------
|
typedef typename std::iterator_traits<Iter_t>::value_type value_t;
|
typedef std::atomic<uint32_t> atomic_t;
|
typedef range<size_t> range_pos;
|
typedef range<Iter_t> range_it;
|
typedef range<value_t *> range_buf;
|
typedef std::function<void(void)> function_t;
|
typedef backbone<Block_size, Iter_t, Compare> backbone_t;
|
typedef compare_block_pos<Block_size, Iter_t, Compare> compare_block_pos_t;
|
|
//------------------------------------------------------------------------
|
// V A R I A B L E S
|
//------------------------------------------------------------------------
|
// Object with the elements to sort and all internal data structures of the
|
// algorithm
|
backbone_t &bk;
|
//
|
//------------------------------------------------------------------------
|
// F U N C T I O N S
|
//------------------------------------------------------------------------
|
merge_blocks(backbone_t &bkb, size_t pos_index1, size_t pos_index2,
|
size_t pos_index3);
|
|
void tail_process(std::vector<block_pos> &vblkpos1,
|
std::vector<block_pos> &vblkpos2);
|
|
void cut_range(range_pos rng);
|
|
void merge_range_pos(range_pos rng);
|
|
void extract_ranges(range_pos range_input);
|
//
|
//------------------------------------------------------------------------
|
// function : function_merge_range_pos
|
/// @brief create a function_t with a call to merge_range_pos, and insert
|
/// in the stack of the backbone
|
//
|
/// @param rng_input : range of positions of blocks in the index to merge
|
/// @param son_counter : atomic variable which is decremented when finish
|
/// the function. This variable is used for to know
|
/// when are finished all the function_t created
|
/// inside an object
|
/// @param error : global indicator of error.
|
///
|
//------------------------------------------------------------------------
|
void function_merge_range_pos(const range_pos &rng_input, atomic_t &counter,
|
bool &error)
|
{
|
bscu::atomic_add(counter, 1);
|
function_t f1 = [this, rng_input, &counter, &error]( ) -> void
|
{
|
if (not error)
|
{
|
try
|
{
|
this->merge_range_pos (rng_input);
|
}
|
catch (std::bad_alloc &ba)
|
{
|
error = true;
|
};
|
}
|
bscu::atomic_sub (counter, 1);
|
};
|
bk.works.emplace_back(f1);
|
}
|
;
|
//
|
//------------------------------------------------------------------------
|
// function : function_cut_range
|
/// @brief create a function_t with a call to cut_range, and inser in
|
/// the stack of the backbone
|
//
|
/// @param rng_input : range of positions in the index to cut
|
/// @param counter : atomic variable which is decremented when finish
|
/// the function. This variable is used for to know
|
/// when are finished all the function_t created
|
/// inside an object
|
/// @param error : global indicator of error.
|
//------------------------------------------------------------------------
|
void function_cut_range(const range_pos &rng_input, atomic_t &counter,
|
bool &error)
|
{
|
bscu::atomic_add(counter, 1);
|
function_t f1 = [this, rng_input, &counter, &error]( ) -> void
|
{
|
if (not error)
|
{
|
try
|
{
|
this->cut_range (rng_input);
|
}
|
catch (std::bad_alloc &)
|
{
|
error = true;
|
};
|
}
|
bscu::atomic_sub (counter, 1);
|
};
|
bk.works.emplace_back(f1);
|
}
|
|
|
//----------------------------------------------------------------------------
|
};
|
// end struct merge_blocks
|
//----------------------------------------------------------------------------
|
//
|
//############################################################################
|
// ##
|
// ##
|
// N O N I N L I N E F U N C T I O N S ##
|
// ##
|
// ##
|
//############################################################################
|
//
|
//-------------------------------------------------------------------------
|
// function : merge_blocks
|
/// @brief make the indirect merge of the two range_pos defined by their index
|
/// position [pos_index1, pos_index2 ) and [ pos_index2, pos_index3 )
|
//
|
/// @param bkb : backbone with all the data to sort , and the internal data
|
/// structures of the algorithm
|
/// @param pos_index1 : first position of the first range in the index
|
/// @param pos_index2 : last position of the first range and first position
|
/// of the second range in the index
|
/// @param pos_index3 : last position of the second range in the index
|
//-------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
merge_blocks<Block_size, Group_size, Iter_t, Compare>
|
::merge_blocks( backbone_t &bkb, size_t pos_index1, size_t pos_index2,
|
size_t pos_index3) : bk(bkb)
|
{
|
size_t nblock1 = pos_index2 - pos_index1;
|
size_t nblock2 = pos_index3 - pos_index2;
|
if (nblock1 == 0 or nblock2 == 0) return;
|
|
//-----------------------------------------------------------------------
|
// Merging of the two intervals
|
//-----------------------------------------------------------------------
|
std::vector<block_pos> vpos1, vpos2;
|
vpos1.reserve(nblock1 + 1);
|
vpos2.reserve(nblock2 + 1);
|
|
for (size_t i = pos_index1; i < pos_index2; ++i)
|
{
|
vpos1.emplace_back(bk.index[i].pos(), true);
|
};
|
|
for (size_t i = pos_index2; i < pos_index3; ++i)
|
{
|
vpos2.emplace_back(bk.index[i].pos(), false);
|
};
|
//-------------------------------------------------------------------
|
// tail process
|
//-------------------------------------------------------------------
|
if (vpos2.back().pos() == (bk.nblock - 1)
|
and bk.range_tail.first != bk.range_tail.last)
|
{
|
tail_process(vpos1, vpos2);
|
nblock1 = vpos1.size();
|
nblock2 = vpos2.size();
|
};
|
|
compare_block_pos_t cmp_blk(bk.global_range.first, bk.cmp);
|
if (bk.error) return;
|
bscu::merge(vpos1.begin(), vpos1.end(), vpos2.begin(), vpos2.end(),
|
bk.index.begin() + pos_index1, cmp_blk);
|
if (bk.error) return;
|
// Extracting the ranges for to merge the elements
|
extract_ranges(range_pos(pos_index1, pos_index1 + nblock1 + nblock2));
|
}
|
|
|
//
|
//-------------------------------------------------------------------------
|
// function : tail_process
|
/// @brief make the process when the second vector of block_pos to merge is
|
/// the last, and have an incomplete block ( tail)
|
//
|
/// @param vblkpos1 : first vector of block_pos elements to merge
|
/// @param vblkpos2 : second vector of block_pos elements to merge
|
//-------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
void merge_blocks<Block_size, Group_size, Iter_t, Compare>
|
::tail_process( std::vector<block_pos> &vblkpos1,
|
std::vector<block_pos> &vblkpos2 )
|
{
|
if (vblkpos1.size() == 0 or vblkpos2.size() == 0) return;
|
|
vblkpos2.pop_back();
|
|
size_t posback1 = vblkpos1.back().pos();
|
range_it range_back1 = bk.get_range(posback1);
|
|
if (bsc::is_mergeable(range_back1, bk.range_tail, bk.cmp))
|
{
|
bsc::merge_uncontiguous(range_back1, bk.range_tail, bk.get_range_buf(),
|
bk.cmp);
|
if (vblkpos1.size() > 1)
|
{
|
size_t pos_aux = vblkpos1[vblkpos1.size() - 2].pos();
|
range_it range_aux = bk.get_range(pos_aux);
|
|
if (bsc::is_mergeable(range_aux, range_back1, bk.cmp))
|
{
|
vblkpos2.emplace_back(posback1, false);
|
vblkpos1.pop_back();
|
};
|
};
|
};
|
}
|
|
//
|
//-------------------------------------------------------------------------
|
// function : cut_range
|
/// @brief when the rng_input is greather than Group_size, this function divide
|
/// it in several parts creating function_t elements, which are inserted
|
/// in the concurrent stack of the backbone
|
//
|
/// @param rng_input : range to divide
|
//-------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
void merge_blocks<Block_size, Group_size, Iter_t, Compare>
|
::cut_range(range_pos rng_input)
|
{
|
if (rng_input.size() < Group_size)
|
{
|
merge_range_pos(rng_input);
|
return;
|
};
|
|
atomic_t counter(0);
|
size_t npart = (rng_input.size() + Group_size - 1) / Group_size;
|
size_t size_part = rng_input.size() / npart;
|
|
size_t pos_ini = rng_input.first;
|
size_t pos_last = rng_input.last;
|
|
while (pos_ini < pos_last)
|
{
|
size_t pos = pos_ini + size_part;
|
while (pos < pos_last
|
and bk.index[pos - 1].side() == bk.index[pos].side())
|
{
|
++pos;
|
};
|
if (pos < pos_last)
|
{
|
merge_uncontiguous(bk.get_range(bk.index[pos - 1].pos()),
|
bk.get_range(bk.index[pos].pos()),
|
bk.get_range_buf(), bk.cmp);
|
}
|
else pos = pos_last;
|
if ((pos - pos_ini) > 1)
|
{
|
range_pos rng_aux(pos_ini, pos);
|
function_merge_range_pos(rng_aux, counter, bk.error);
|
};
|
pos_ini = pos;
|
};
|
bk.exec(counter); // wait until finish all the ranges
|
}
|
|
|
//
|
//-------------------------------------------------------------------------
|
// function : merge_range_pos
|
/// @brief make the indirect merge of the blocks inside the rng_input
|
//
|
/// @param rng_input : range of positions of the blocks to merge
|
//-------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
void merge_blocks<Block_size, Group_size, Iter_t, Compare>
|
::merge_range_pos(range_pos rng_input)
|
{
|
if (rng_input.size() < 2) return;
|
range_buf rbuf = bk.get_range_buf();
|
|
range_it rng_prev = bk.get_range(bk.index[rng_input.first].pos());
|
move_forward(rbuf, rng_prev);
|
range_it rng_posx(rng_prev);
|
|
for (size_t posx = rng_input.first + 1; posx != rng_input.last; ++posx)
|
{
|
rng_posx = bk.get_range(bk.index[posx].pos());
|
bsc::merge_flow(rng_prev, rbuf, rng_posx, bk.cmp);
|
rng_prev = rng_posx;
|
|
};
|
move_forward(rng_posx, rbuf);
|
}
|
//
|
//-------------------------------------------------------------------------
|
// function : extract_ranges
|
/// @brief from a big range of positions of blocks in the index. Examine which
|
/// are mergeable, and generate a couple of ranges for to be merged.
|
/// With the ranges obtained generate function_t elements and are
|
/// inserted in the concurrent stack.
|
/// When the range obtained is smaller than Group_size, generate a
|
/// function_t calling to merge_range_pos, when is greater, generate a
|
/// function_t calling to cut_range
|
//
|
/// @param rpos range_input : range of the position in the index, where must
|
/// extract the ranges to merge
|
//-------------------------------------------------------------------------
|
template<uint32_t Block_size, uint32_t Group_size, class Iter_t, class Compare>
|
void merge_blocks<Block_size, Group_size, Iter_t, Compare>
|
::extract_ranges(range_pos range_input)
|
{
|
if (range_input.size() < 2) return;
|
atomic_t counter(0);
|
|
// The names with x are positions of the index
|
size_t posx_ini = range_input.first;
|
block_pos bp_posx_ini = bk.index[posx_ini];
|
|
range_it rng_max = bk.get_range(bp_posx_ini.pos());
|
bool side_max = bp_posx_ini.side();
|
|
block_pos bp_posx;
|
range_it rng_posx = rng_max;
|
bool side_posx = side_max;
|
|
for (size_t posx = posx_ini + 1; posx <= range_input.last; ++posx)
|
{
|
bool final = (posx == range_input.last);
|
bool mergeable = false;
|
|
if (not final)
|
{
|
bp_posx = bk.index[posx];
|
rng_posx = bk.get_range(bp_posx.pos());
|
side_posx = bp_posx.side();
|
mergeable = (side_max != side_posx
|
and is_mergeable(rng_max, rng_posx, bk.cmp));
|
};
|
if (bk.error) return;
|
if (final or not mergeable)
|
{
|
range_pos rp_final(posx_ini, posx);
|
if (rp_final.size() > 1)
|
{
|
if (rp_final.size() > Group_size)
|
{
|
function_cut_range(rp_final, counter, bk.error);
|
}
|
else
|
{
|
function_merge_range_pos(rp_final, counter, bk.error);
|
};
|
};
|
posx_ini = posx;
|
if (not final)
|
{
|
rng_max = rng_posx;
|
side_max = side_posx;
|
};
|
}
|
else
|
{
|
if (bk.cmp(*(rng_max.back()), *(rng_posx.back())))
|
{
|
rng_max = rng_posx;
|
side_max = side_posx;
|
};
|
};
|
};
|
bk.exec(counter);
|
}
|
//
|
//****************************************************************************
|
}; // End namespace blk_detail
|
}; // End namespace sort
|
}; // End namespace boost
|
//****************************************************************************
|
//
|
#endif
|