Commit ca032936 by Tobias Fuchs

algorithms_cpp: Fix and cleanup of MergeSort

parent 2f755525
...@@ -116,7 +116,7 @@ void ForEachRecursive(RAI first, RAI last, Function unary, ...@@ -116,7 +116,7 @@ void ForEachRecursive(RAI first, RAI last, Function unary,
block_size = 1; block_size = 1;
} }
} }
// Perform check of task number sufficiency // Check task number sufficiency
if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) {
EMBB_THROW(embb::base::ErrorException, EMBB_THROW(embb::base::ErrorException,
"Not enough MTAPI tasks available for parallel foreach"); "Not enough MTAPI tasks available for parallel foreach");
......
...@@ -60,22 +60,13 @@ class MergeSortFunctor { ...@@ -60,22 +60,13 @@ class MergeSortFunctor {
} }
void Action(mtapi::TaskContext&) { void Action(mtapi::TaskContext&) {
typedef typename std::iterator_traits<RAI>::difference_type
difference_type;
size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2; size_t chunk_split_index = (chunk_first_ + chunk_last_) / 2;
if (chunk_first_ == chunk_last_) { if (chunk_first_ == chunk_last_) {
// Leaf case: recurse into a single chunk's elements: // Leaf case: recurse into a single chunk's elements:
ChunkDescriptor<RAI> chunk = partitioner_[chunk_first_]; ChunkDescriptor<RAI> chunk = partitioner_[chunk_first_];
MergeSortChunkFunctor functor(chunk.GetFirst(), MergeSortChunk(chunk.GetFirst(), chunk.GetLast(), depth_);
chunk.GetLast(), } else {
temp_first_, // Recurse further, split chunks:
global_first_,
depth_);
functor.Action();
return;
}
// Recurse further:
// Split chunks into left / right branches:
self_t functor_l(chunk_first_, self_t functor_l(chunk_first_,
chunk_split_index, chunk_split_index,
temp_first_, temp_first_,
...@@ -101,7 +92,8 @@ class MergeSortFunctor { ...@@ -101,7 +92,8 @@ class MergeSortFunctor {
ChunkDescriptor<RAI> chunk_f = partitioner_[chunk_first_]; ChunkDescriptor<RAI> chunk_f = partitioner_[chunk_first_];
ChunkDescriptor<RAI> chunk_m = partitioner_[chunk_split_index + 1]; ChunkDescriptor<RAI> chunk_m = partitioner_[chunk_split_index + 1];
ChunkDescriptor<RAI> chunk_l = partitioner_[chunk_last_]; ChunkDescriptor<RAI> chunk_l = partitioner_[chunk_last_];
if(CloneBackToInput()) { if(CloneBackToInput(depth_)) {
// Merge from temp into input:
difference_type first = std::distance(global_first_, chunk_f.GetFirst()); difference_type first = std::distance(global_first_, chunk_f.GetFirst());
difference_type mid = std::distance(global_first_, chunk_m.GetFirst()); difference_type mid = std::distance(global_first_, chunk_m.GetFirst());
difference_type last = std::distance(global_first_, chunk_l.GetLast()); difference_type last = std::distance(global_first_, chunk_l.GetLast());
...@@ -109,86 +101,77 @@ class MergeSortFunctor { ...@@ -109,86 +101,77 @@ class MergeSortFunctor {
chunk_f.GetFirst(), chunk_f.GetFirst(),
comparison_); comparison_);
} else { } else {
// Merge from input into temp:
SerialMerge(chunk_f.GetFirst(), chunk_m.GetFirst(), chunk_l.GetLast(), SerialMerge(chunk_f.GetFirst(), chunk_m.GetFirst(), chunk_l.GetLast(),
temp_first_ + std::distance(global_first_, chunk_f.GetFirst()), temp_first_ + std::distance(global_first_, chunk_f.GetFirst()),
comparison_); comparison_);
} }
} }
/**
* Determines the input and output arrays for one level in merge sort.
*
* \return \c true if the temporary data range is input and the array to be
* sorted is output. \c false, if the other way around.
*/
bool CloneBackToInput() {
return depth_ % 2 == 0 ? true : false;
} }
private:
typedef MergeSortFunctor<RAI, RAITemp, ComparisonFunction> self_t;
private:
/** /**
* Non-parallelized part of merge sort on elements within a single chunk. * Serial merge sort of elements within a single chunk.
*/ */
class MergeSortChunkFunctor { void MergeSortChunk(RAI first,
public: RAI last,
MergeSortChunkFunctor(RAI first, RAI last, int depth) {
RAITemp temp_first,
const RAI & global_first,
int depth)
: first_(first), last_(last),
temp_first_(temp_first), global_first_(global_first),
depth_(depth) {
}
void Action() {
size_t distance = static_cast<size_t>( size_t distance = static_cast<size_t>(
std::distance(first_, last_)); std::distance(first, last));
if (distance <= 1) { if (distance <= 1) {
// Leaf case: // Leaf case:
if(!CloneBackToInput() && distance != 0) { if (!CloneBackToInput(depth) && distance != 0) {
RAITemp temp_first = temp_first_; RAITemp temp_first = temp_first_;
std::advance(temp_first, std::distance(global_first_, first_)); std::advance(temp_first, std::distance(global_first_, first));
*temp_first = *first_; *temp_first = *first;
} }
return; return;
} }
// Recurse further. Use binary split, ignoring chunk size as this // Recurse further. Use binary split, ignoring chunk size as this
// recursion is serial: // recursion is serial and has leaf size 1:
ChunkPartitioner<RAI> partitioner(first_, last_, 2); ChunkPartitioner<RAI> partitioner(first, last, 2);
ChunkDescriptor<RAI> chunk_l = partitioner[0]; ChunkDescriptor<RAI> chunk_l = partitioner[0];
ChunkDescriptor<RAI> chunk_r = partitioner[1]; ChunkDescriptor<RAI> chunk_r = partitioner[1];
MergeSortChunkFunctor functor_l( MergeSortChunk(
chunk_l.GetFirst(), chunk_l.GetFirst(),
chunk_l.GetLast(), chunk_l.GetLast(),
temp_first_, global_first_, depth_ + 1); depth + 1);
MergeSortChunkFunctor functor_r( MergeSortChunk(
chunk_r.GetFirst(), chunk_r.GetFirst(),
chunk_r.GetLast(), chunk_r.GetLast(),
temp_first_, global_first_, depth_ + 1); depth + 1);
functor_l.Action(); if (CloneBackToInput(depth)) {
functor_r.Action(); // Merge from temp into input:
difference_type d_first = std::distance(global_first_, chunk_l.GetFirst());
difference_type d_mid = std::distance(global_first_, chunk_r.GetFirst());
difference_type d_last = std::distance(global_first_, chunk_r.GetLast());
SerialMerge(
temp_first_ + d_first, temp_first_ + d_mid, temp_first_ + d_last,
chunk_l.GetFirst(),
comparison_);
}
else {
// Merge from input into temp:
SerialMerge(
chunk_l.GetFirst(), chunk_r.GetFirst(), chunk_r.GetLast(),
temp_first_ + std::distance(global_first_, chunk_l.GetFirst()),
comparison_);
}
} }
private:
/** /**
* Determines the input and output arrays for one level in merge sort. * Determines the input and output arrays for one level in merge sort.
* *
* \return \c true if the temporary data range is input and the array to be * \return \c true if the temporary data range is input and the array to be
* sorted is output. \c false, if the other way around. * sorted is output. \c false, if the other way around.
*/ */
bool CloneBackToInput() { bool CloneBackToInput(int depth) {
return depth_ % 2 == 0 ? true : false; return depth % 2 == 0 ? true : false;
} }
RAI first_; private:
RAI last_; typedef MergeSortFunctor<RAI, RAITemp, ComparisonFunction> self_t;
RAITemp temp_first_; typedef typename std::iterator_traits<RAI>::difference_type
RAI global_first_; difference_type;
int depth_;
};
private: private:
size_t chunk_first_; size_t chunk_first_;
...@@ -243,24 +226,27 @@ void MergeSort( ...@@ -243,24 +226,27 @@ void MergeSort(
size_t block_size size_t block_size
) { ) {
typedef typename std::iterator_traits<RAI>::difference_type difference_type; typedef typename std::iterator_traits<RAI>::difference_type difference_type;
typedef internal::MergeSortFunctor<RAI, RAITemp, ComparisonFunction> functor_t;
difference_type distance = std::distance(first, last);
if (distance == 0) {
EMBB_THROW(embb::base::ErrorException, "Distance for ForEach is 0");
}
embb::mtapi::Node &node = embb::mtapi::Node::GetInstance(); embb::mtapi::Node &node = embb::mtapi::Node::GetInstance();
difference_type distance = last - first; // Determine actually used block size
assert(distance >= 0);
if (block_size == 0) { if (block_size == 0) {
block_size = (static_cast<size_t>(distance) / node.GetCoreCount()); block_size = (static_cast<size_t>(distance) / node.GetCoreCount());
if (block_size == 0) if (block_size == 0)
block_size = 1; block_size = 1;
} }
if (((distance/block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { // Check task number sufficiency
if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) {
EMBB_THROW(embb::base::ErrorException, EMBB_THROW(embb::base::ErrorException,
"Not enough MTAPI tasks available to perform the merge sort"); "Not enough MTAPI tasks available to perform merge sort");
} }
internal::BlockSizePartitioner<RAI> partitioner(first, last, block_size); internal::BlockSizePartitioner<RAI> partitioner(first, last, block_size);
functor_t functor(0,
internal::MergeSortFunctor<RAI, RAITemp, ComparisonFunction> functor( partitioner.Size() - 1,
0, partitioner.Size() - 1,
temporary_first, temporary_first,
comparison, comparison,
policy, policy,
...@@ -268,12 +254,14 @@ void MergeSort( ...@@ -268,12 +254,14 @@ void MergeSort(
first, first,
0); 0);
mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction(functor, mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction(functor,
&internal::MergeSortFunctor<RAI, RAITemp, ComparisonFunction>::Action), &functor_t::Action),
policy)); policy));
task.Wait(MTAPI_INFINITE); task.Wait(MTAPI_INFINITE);
} }
// @NOTE: Why is there no type guard for RAI?
} // namespace algorithms } // namespace algorithms
} // namespace embb } // namespace embb
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment