From 363580cdf8c15c38c2cc6b4b1db504a2fff70612 Mon Sep 17 00:00:00 2001 From: karsten Date: Wed, 15 Dec 2021 18:07:21 +0100 Subject: [PATCH] code cleanup: moved parallel search into template class --- CMakeSettings.json | 5 ++- ParallelSearch.hpp | 56 +++++++++++++++++++++++++------ SearchJob.hpp | 2 +- main.cpp | 84 ++++++++++------------------------------------ 4 files changed, 66 insertions(+), 81 deletions(-) diff --git a/CMakeSettings.json b/CMakeSettings.json index 0c5fbf9..d2bcfce 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -14,14 +14,13 @@ { "name": "x64-Release", "generator": "Ninja", - "configurationType": "RelWithDebInfo", + "configurationType": "Release", "buildRoot": "${projectDir}\\out\\build\\${name}", "installRoot": "${projectDir}\\out\\install\\${name}", "cmakeCommandArgs": "", "buildCommandArgs": "", "ctestCommandArgs": "", - "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [] + "inheritEnvironments": [ "msvc_x64_x64" ] } ] } \ No newline at end of file diff --git a/ParallelSearch.hpp b/ParallelSearch.hpp index 8485485..0aeaf75 100644 --- a/ParallelSearch.hpp +++ b/ParallelSearch.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include "SearchJob.hpp" template @@ -11,32 +13,66 @@ class ParallelSearch public: // Type of the search result typedef std::vector ResultList; - // underlying search jobs - typedef std::vector> JobList; - // Create partial search job for provided range - SearchJob(unsigned workerCount, Iterator begin, Iterator end, const std::string& pattern) + // construct parallel search by splitting the search range across SearchJob objects + ParallelSearch(unsigned workerCount, Iterator begin, Iterator end, const std::string& pattern) { + std::size_t totalCount = end - begin; + std::size_t bucketSize = totalCount / workerCount; + std::size_t remains = totalCount % workerCount; + std::size_t offset = 0; + while (offset < totalCount) + { + // compute [first; last( iterators for sub-range + std::size_t size = bucketSize + remains; + Iterator first = begin + offset; + Iterator last = first + bucketSize + remains; + + // add appropriate search job + jobList_.push_back(SearchJob(first, last, pattern)); + + // increment offset + offset += size; + // clear remains (it has been added to first bucket) + remains = 0; + } } // Destructor - ~SearchJob() + ~ParallelSearch() { } - // run the search job and collect results internally + // run the search workers and collect results void run() { - + // start thread for each job + std::vector threads; + for (SearchJob& job : jobList_) + { + threads.push_back(std::thread(&SearchJob::run, &job)); + } + + // wait for all threads to join + for (std::thread& th : threads) + { + th.join(); + } + + // collec the results + for (SearchJob& job : jobList_) + { + result_.insert(result_.end(), job.get_result().begin(), job.get_result().end()); + } } // return the result - const ResultList& GetResult() const { return result_; }; + const ResultList& get_result() const { return result_; }; -private: +private: // jobs to execute - JobList jobList_; + std::vector> jobList_; // result of the search ResultList result_; }; diff --git a/SearchJob.hpp b/SearchJob.hpp index ac87795..6c6ee2b 100644 --- a/SearchJob.hpp +++ b/SearchJob.hpp @@ -39,7 +39,7 @@ public: } // return the result - const ResultList& GetResult() const { return result_; }; + const ResultList& get_result() const { return result_; }; private: // pattern to search for diff --git a/main.cpp b/main.cpp index 1e4064c..362a3bf 100644 --- a/main.cpp +++ b/main.cpp @@ -6,16 +6,16 @@ #include #include "SearchJob.hpp" +#include "ParallelSearch.hpp" #include "TestRunner.hpp" // Type of our hay stack: a list of strings typedef std::vector WordList; -typedef SearchJob WordSearchJob; - -typedef std::vector SearchJobList; +typedef SearchJob WordSearch; +typedef ParallelSearch ParallelWordSearch; void create_testdata(WordList& words) { @@ -37,88 +37,38 @@ void shuffle_testdata(WordList& words) std::shuffle(words.begin(), words.end(), rng); } -void run_wordsearch(WordSearchJob& job) +void run_wordsearch(WordSearch& search) { - job.run(); + search.run(); } -void run_parallel_wordsearch(std::vector& jobList) +void run_parallel_wordsearch(ParallelWordSearch& search) { - std::vector threads; - - for (WordSearchJob& job : jobList) - { - threads.push_back(std::thread(&WordSearchJob::run, &job)); - } - - for (std::thread& th : threads) - { - th.join(); - } + search.run(); } int main() { double time_span; std::string pattern = "ABC"; - + unsigned workerCount = std::thread::hardware_concurrency(); WordList test_data; - std::cout << "Hello World." << std::endl; - time_span = TestRunner(create_testdata, test_data); - std::cout << "It took me " << time_span << " seconds to create " << test_data.size() << " words." << std::endl; - + std::cout << "created test_data (" << test_data.size() << " words) " << time_span << " seconds" << std::endl; + time_span = TestRunner(shuffle_testdata, test_data); - std::cout << "It took me " << time_span << " seconds to shuffle " << test_data.size() << " words." << std::endl; + std::cout << "shuffled test_data " << time_span << " seconds" << std::endl; - SearchJob job(test_data.begin(), test_data.end(), pattern); + WordSearch wordSearch(test_data.begin(), test_data.end(), pattern); - time_span = TestRunner(run_wordsearch, job); - std::cout << "It took me " << time_span << " seconds to find " << job.GetResult().size() << " matches." << std::endl; + time_span = TestRunner(run_wordsearch, wordSearch); + std::cout << "linear search found " << wordSearch.get_result().size() << " matches: " << time_span << " seconds" << std::endl; - - std::vector jobList; - - unsigned coreCount = std::thread::hardware_concurrency(); - std::size_t bucketSize = test_data.size() / coreCount; - std::size_t remains = test_data.size() % coreCount; - - std::cout << "Creating " << coreCount << " search jobs, bucketSize = " << bucketSize << std::endl; - - std::size_t offset = 0; - while(offset < test_data.size()) - { - // compute [first; last( iterators for sub-range - std::size_t size = bucketSize + remains; - WordList::const_iterator first = test_data.begin() + offset; - WordList::const_iterator last = first + bucketSize + remains; - - std::cout << "offset = " << offset << " size = " << size << std::endl; - - // add appropriate search job - jobList.push_back(WordSearchJob(first, last, pattern)); - - // increment offset - offset += size; - // clear remains (it has been added to first bucket) - remains = 0; - } - + ParallelWordSearch parallelWordSearch(workerCount, test_data.begin(), test_data.end(), pattern); + time_span = TestRunner(run_parallel_wordsearch, parallelWordSearch); + std::cout << "linear search found " << parallelWordSearch.get_result().size() << " matches: " << time_span << " seconds" << std::endl; - - time_span = TestRunner(run_parallel_wordsearch, jobList); - - // collect search results ... - unsigned found = 0; - for (WordSearchJob& job : jobList) - { - found += job.GetResult().size(); - } - std::cout << "It took me " << time_span << " seconds to find " << found << " matches." << std::endl; - - - std::cout << std::endl; return 0;