/* Copyright (c) 2019 Sogou, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Author: Xie Han (xiehan@sogou-inc.com) */ #include #include #include #include #include #include #include "Workflow.h" #include "WFGlobal.h" /********** Classes without CMP **********/ template class __WFSortTask : public WFSortTask { protected: virtual void execute() { std::sort(this->input.first, this->input.last); this->output.first = this->input.first; this->output.last = this->input.last; } public: __WFSortTask(ExecQueue *queue, Executor *executor, T *first, T *last, sort_callback_t&& cb) : WFSortTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.last = last; this->output.first = NULL; this->output.last = NULL; } }; template class __WFMergeTask : public WFMergeTask { protected: virtual void execute(); public: __WFMergeTask(ExecQueue *queue, Executor *executor, T *first1, T *last1, T *first2, T *last2, T *d_first, merge_callback_t&& cb) : WFMergeTask(queue, executor, std::move(cb)) { this->input.first1 = first1; this->input.last1 = last1; this->input.first2 = first2; this->input.last2 = last2; this->input.d_first = d_first; this->output.first = NULL; this->output.last = NULL; } }; template void __WFMergeTask::execute() { auto *input = &this->input; auto *output = &this->output; if (input->first1 == input->d_first && input->last1 == input->first2) { std::inplace_merge(input->first1, input->first2, input->last2); output->last = input->last2; } else if (input->first2 == input->d_first && input->last2 == input->first1) { std::inplace_merge(input->first2, input->first1, input->last1); output->last = input->last1; } else { output->last = std::merge(input->first1, input->last1, input->first2, input->last2, input->d_first); } output->first = input->d_first; } template class __WFParSortTask : public __WFSortTask { public: virtual void dispatch(); protected: virtual SubTask *done() { if (this->flag) return series_of(this)->pop(); return this->WFSortTask::done(); } virtual void execute(); protected: int depth; int flag; public: __WFParSortTask(ExecQueue *queue, Executor *executor, T *first, T *last, int depth, sort_callback_t&& cb) : __WFSortTask(queue, executor, first, last, std::move(cb)) { this->depth = depth; this->flag = 0; } }; template void __WFParSortTask::dispatch() { size_t n = this->input.last - this->input.first; if (!this->flag && this->depth < 7 && n >= 32) { SeriesWork *series = series_of(this); T *middle = this->input.first + n / 2; auto *task1 = new __WFParSortTask(this->queue, this->executor, this->input.first, middle, this->depth + 1, nullptr); auto *task2 = new __WFParSortTask(this->queue, this->executor, middle, this->input.last, this->depth + 1, nullptr); SeriesWork *sub_series[2] = { Workflow::create_series_work(task1, nullptr), Workflow::create_series_work(task2, nullptr) }; ParallelWork *parallel = Workflow::create_parallel_work(sub_series, 2, nullptr); series->push_front(this); series->push_front(parallel); this->flag = 1; this->subtask_done(); } else this->__WFSortTask::dispatch(); } template void __WFParSortTask::execute() { if (this->flag) { size_t n = this->input.last - this->input.first; T *middle = this->input.first + n / 2; std::inplace_merge(this->input.first, middle, this->input.last); this->output.first = this->input.first; this->output.last = this->input.last; this->flag = 0; } else this->__WFSortTask::execute(); } /********** Classes with CMP **********/ template class __WFSortTaskCmp : public __WFSortTask { protected: virtual void execute() { std::sort(this->input.first, this->input.last, std::move(this->compare)); this->output.first = this->input.first; this->output.last = this->input.last; } protected: CMP compare; public: __WFSortTaskCmp(ExecQueue *queue, Executor *executor, T *first, T *last, CMP&& cmp, sort_callback_t&& cb) : __WFSortTask(queue, executor, first, last, std::move(cb)), compare(std::move(cmp)) { } }; template class __WFMergeTaskCmp : public __WFMergeTask { protected: virtual void execute(); protected: CMP compare; public: __WFMergeTaskCmp(ExecQueue *queue, Executor *executor, T *first1, T *last1, T *first2, T *last2, T *d_first, CMP&& cmp, merge_callback_t&& cb) : __WFMergeTask(queue, executor, first1, last1, first2, last2, d_first, std::move(cb)), compare(std::move(cmp)) { } }; template void __WFMergeTaskCmp::execute() { auto *input = &this->input; auto *output = &this->output; if (input->first1 == input->d_first && input->last1 == input->first2) { std::inplace_merge(input->first1, input->first2, input->last2, std::move(this->compare)); output->last = input->last2; } else if (input->first2 == input->d_first && input->last2 == input->first1) { std::inplace_merge(input->first2, input->first1, input->last1, std::move(this->compare)); output->last = input->last1; } else { output->last = std::merge(input->first1, input->last1, input->first2, input->last2, input->d_first, std::move(this->compare)); } output->first = input->d_first; } template class __WFParSortTaskCmp : public __WFSortTaskCmp { public: virtual void dispatch(); protected: virtual SubTask *done() { if (this->flag) return series_of(this)->pop(); return this->WFSortTask::done(); } virtual void execute(); protected: int depth; int flag; public: __WFParSortTaskCmp(ExecQueue *queue, Executor *executor, T *first, T *last, CMP cmp, int depth, sort_callback_t&& cb) : __WFSortTaskCmp(queue, executor, first, last, std::move(cmp), std::move(cb)) { this->depth = depth; this->flag = 0; } }; template void __WFParSortTaskCmp::dispatch() { size_t n = this->input.last - this->input.first; if (!this->flag && this->depth < 7 && n >= 32) { SeriesWork *series = series_of(this); T *middle = this->input.first + n / 2; auto *task1 = new __WFParSortTaskCmp(this->queue, this->executor, this->input.first, middle, this->compare, this->depth + 1, nullptr); auto *task2 = new __WFParSortTaskCmp(this->queue, this->executor, middle, this->input.last, this->compare, this->depth + 1, nullptr); SeriesWork *sub_series[2] = { Workflow::create_series_work(task1, nullptr), Workflow::create_series_work(task2, nullptr) }; ParallelWork *parallel = Workflow::create_parallel_work(sub_series, 2, nullptr); series->push_front(this); series->push_front(parallel); this->flag = 1; this->subtask_done(); } else this->__WFSortTaskCmp::dispatch(); } template void __WFParSortTaskCmp::execute() { if (this->flag) { size_t n = this->input.last - this->input.first; T *middle = this->input.first + n / 2; std::inplace_merge(this->input.first, middle, this->input.last, std::move(this->compare)); this->output.first = this->input.first; this->output.last = this->input.last; this->flag = 0; } else this->__WFSortTaskCmp::execute(); } /********** Factory functions without CMP **********/ template WFSortTask *WFAlgoTaskFactory::create_sort_task(const std::string& name, T *first, T *last, CB callback) { return new __WFSortTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(callback)); } template WFMergeTask *WFAlgoTaskFactory::create_merge_task(const std::string& name, T *first1, T *last1, T *first2, T *last2, T *d_first, CB callback) { return new __WFMergeTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first1, last1, first2, last2, d_first, std::move(callback)); } template WFSortTask *WFAlgoTaskFactory::create_psort_task(const std::string& name, T *first, T *last, CB callback) { return new __WFParSortTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, 0, std::move(callback)); } /********** Factory functions with CMP **********/ template WFSortTask *WFAlgoTaskFactory::create_sort_task(const std::string& name, T *first, T *last, CMP compare, CB callback) { return new __WFSortTaskCmp(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(compare), std::move(callback)); } template WFMergeTask *WFAlgoTaskFactory::create_merge_task(const std::string& name, T *first1, T *last1, T *first2, T *last2, T *d_first, CMP compare, CB callback) { return new __WFMergeTaskCmp(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first1, last1, first2, last2, d_first, std::move(compare), std::move(callback)); } template WFSortTask *WFAlgoTaskFactory::create_psort_task(const std::string& name, T *first, T *last, CMP compare, CB callback) { return new __WFParSortTaskCmp(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(compare), 0, std::move(callback)); } /****************** Shuffle ******************/ template class __WFShuffleTask : public WFShuffleTask { protected: virtual void execute() { std::shuffle(this->input.first, this->input.last, std::mt19937_64(random())); this->output.first = this->input.first; this->output.last = this->input.last; } public: __WFShuffleTask(ExecQueue *queue, Executor *executor, T *first, T *last, shuffle_callback_t&& cb) : WFShuffleTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.last = last; this->output.first = NULL; this->output.last = NULL; } }; template class __WFShuffleTaskGen : public __WFShuffleTask { protected: virtual void execute() { std::shuffle(this->input.first, this->input.last, std::move(this->generator)); this->output.first = this->input.first; this->output.last = this->input.last; } protected: URBG generator; public: __WFShuffleTaskGen(ExecQueue *queue, Executor *executor, T *first, T *last, URBG&& gen, shuffle_callback_t&& cb) : __WFShuffleTask(queue, executor, std::move(cb)), generator(std::move(gen)) { } }; template WFShuffleTask *WFAlgoTaskFactory::create_shuffle_task(const std::string& name, T *first, T *last, CB callback) { return new __WFShuffleTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(callback)); } template WFShuffleTask *WFAlgoTaskFactory::create_shuffle_task(const std::string& name, T *first, T *last, URBG generator, CB callback) { return new __WFShuffleTaskGen(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(generator), std::move(callback)); } /****************** Remove ******************/ template class __WFRemoveTask : public WFRemoveTask { protected: virtual void execute() { this->output.last = std::remove(this->input.first, this->input.last, this->input.value); this->output.first = this->input.first; } public: __WFRemoveTask(ExecQueue *queue, Executor *executor, T *first, T *last, T&& value, remove_callback_t&& cb) : WFRemoveTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.last = last; this->input.value = std::move(value); this->output.first = NULL; this->output.last = NULL; } }; template WFRemoveTask *WFAlgoTaskFactory::create_remove_task(const std::string& name, T *first, T *last, T value, CB callback) { return new __WFRemoveTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(value), std::move(callback)); } /****************** Unique ******************/ template class __WFUniqueTask : public WFUniqueTask { protected: virtual void execute() { this->output.last = std::unique(this->input.first, this->input.last); this->output.first = this->input.first; } public: __WFUniqueTask(ExecQueue *queue, Executor *executor, T *first, T *last, unique_callback_t&& cb) : WFUniqueTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.last = last; this->output.first = NULL; this->output.last = NULL; } }; template WFUniqueTask *WFAlgoTaskFactory::create_unique_task(const std::string& name, T *first, T *last, CB callback) { return new __WFUniqueTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(callback)); } /****************** Reverse ******************/ template class __WFReverseTask : public WFReverseTask { protected: virtual void execute() { std::reverse(this->input.first, this->input.last); this->output.first = this->input.first; this->output.last = this->input.last; } public: __WFReverseTask(ExecQueue *queue, Executor *executor, T *first, T *last, reverse_callback_t&& cb) : WFReverseTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.last = last; this->output.first = NULL; this->output.last = NULL; } }; template WFReverseTask *WFAlgoTaskFactory::create_reverse_task(const std::string& name, T *first, T *last, CB callback) { return new __WFReverseTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, last, std::move(callback)); } /****************** Rotate ******************/ template class __WFRotateTask : public WFRotateTask { protected: virtual void execute() { std::rotate(this->input.first, this->input.middle, this->input.last); this->output.first = this->input.first; this->output.last = this->input.last; } public: __WFRotateTask(ExecQueue *queue, Executor *executor, T *first, T* middle, T *last, rotate_callback_t&& cb) : WFRotateTask(queue, executor, std::move(cb)) { this->input.first = first; this->input.middle = middle; this->input.last = last; this->output.first = NULL; this->output.last = NULL; } }; template WFRotateTask *WFAlgoTaskFactory::create_rotate_task(const std::string& name, T *first, T *middle, T *last, CB callback) { return new __WFRotateTask(WFGlobal::get_exec_queue(name), WFGlobal::get_compute_executor(), first, middle, last, std::move(callback)); }