From 8ed4b9ac712564dbedac3a971d5499f85881297a Mon Sep 17 00:00:00 2001 From: Michael Mandl Date: Tue, 19 Mar 2024 17:10:48 +0100 Subject: [PATCH] feat: add naive implementation with timer and word-list generator --- CMakeLists.txt | 3 +- finder.h | 12 ++++++++ linear_finder.cpp | 17 +++++++++++ linear_finder.h | 12 ++++++++ main.cpp | 63 +++++++++++++++++++++++++++++++++++++++-- parallel_finder.cpp | 47 ++++++++++++++++++++++++++++++ parallel_finder.h | 13 +++++++++ timer.cpp | 17 +++++++++++ timer.h | 16 +++++++++++ word_list_generator.cpp | 34 ++++++++++++++++++++++ word_list_generator.h | 12 ++++++++ 11 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 finder.h create mode 100644 linear_finder.cpp create mode 100644 linear_finder.h create mode 100644 parallel_finder.cpp create mode 100644 parallel_finder.h create mode 100644 timer.cpp create mode 100644 timer.h create mode 100644 word_list_generator.cpp create mode 100644 word_list_generator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a96ffad..c7033c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,8 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(ExportCompileCommands) include(sccache) -add_executable(VectorSearch main.cpp) +add_executable(VectorSearch main.cpp word_list_generator.cpp timer.cpp + linear_finder.cpp parallel_finder.cpp) target_compile_features(VectorSearch PUBLIC cxx_std_20) diff --git a/finder.h b/finder.h new file mode 100644 index 0000000..9543adf --- /dev/null +++ b/finder.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +using std::string, std::string_view, std::vector; + +class Finder { +public: + virtual ~Finder() = default; + virtual vector find_prefix(string_view search_term) const = 0; +}; diff --git a/linear_finder.cpp b/linear_finder.cpp new file mode 100644 index 0000000..f6c76c1 --- /dev/null +++ b/linear_finder.cpp @@ -0,0 +1,17 @@ +#include "linear_finder.h" + +LinearFinder::LinearFinder(const vector &word_list) + : word_list_(word_list) {} + +vector +LinearFinder::find_prefix(string_view search_term) const { + vector matching_words; + + for (const auto ¤t_word : word_list_) { + if (current_word.starts_with(search_term)) { + matching_words.push_back(¤t_word); + } + } + + return matching_words; +} diff --git a/linear_finder.h b/linear_finder.h new file mode 100644 index 0000000..e9f3419 --- /dev/null +++ b/linear_finder.h @@ -0,0 +1,12 @@ +#pragma once + +#include "finder.h" + +class LinearFinder : public Finder { +private: + const vector &word_list_; + +public: + LinearFinder(const vector &word_list); + vector find_prefix(string_view search_term) const override; +}; diff --git a/main.cpp b/main.cpp index 33025f4..f9c356e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,66 @@ +#include "linear_finder.h" +#include "parallel_finder.h" +#include "timer.h" +#include "word_list_generator.h" + +#include #include +#include + +using std::string, std::string_view, std::vector, std::thread, std::cout, + std::endl; + +vector generate_word_list() { + cout << "\ngenerating word list" << endl; + + Timer generator_timer("word list generator"); + auto word_list = WordListGenerator().generate(); + generator_timer.stop(); + + cout << "word list is " << word_list.size() << " element(s) long" << endl; + + return word_list; +} + +void test_linear_finder(const vector &word_list) { + cout << "\nrunning linear finder" << endl; + + Timer constructor_timer("linear finder constructor"); + LinearFinder linear_finder(word_list); + constructor_timer.stop(); + + Timer find_timer("linear finder find"); + auto result = linear_finder.find_prefix("ABCD"); + find_timer.stop(); + + cout << "result list is " << result.size() << " element(s) long" << endl; +} + +void test_parallel_finder(const vector &word_list) { + cout << "\nrunning parallel finder" << endl; + + const size_t thread_count = thread::hardware_concurrency(); + + cout << "using " << thread_count << " threads" << endl; + + Timer constructor_timer("parallel finder constructor"); + ParallelFinder parallel_finder(word_list, thread_count); + constructor_timer.stop(); + + Timer find_timer("parallel finder find"); + auto result = parallel_finder.find_prefix("ABCD"); + find_timer.stop(); + + cout << "result list is " << result.size() << " element(s) long" << endl; +} int main(int argc, char *argv[]) { - std::cout << "VectorSearch" << std::endl; + cout << "\n== VectorSearch ==" << endl; - return 0; + auto word_list = generate_word_list(); + + test_linear_finder(word_list); + test_parallel_finder(word_list); + + return EXIT_SUCCESS; } diff --git a/parallel_finder.cpp b/parallel_finder.cpp new file mode 100644 index 0000000..d83b97a --- /dev/null +++ b/parallel_finder.cpp @@ -0,0 +1,47 @@ +#include "parallel_finder.h" + +#include +#include + +using std::mutex, std::thread, std::lock_guard; + +ParallelFinder::ParallelFinder(const vector &word_list, + size_t thread_count) + : word_list_(word_list), thread_count_(thread_count) {} + +vector +ParallelFinder::find_prefix(string_view search_term) const { + vector result; + mutex result_mutex; + + vector threads; + for (size_t thread_index = 0; thread_index < thread_count_; ++thread_index) { + const size_t first_word_index = + thread_index * (word_list_.size() / thread_count_); + const size_t last_word_index = + (thread_index == thread_count_ - 1) + ? word_list_.size() + : (thread_index + 1) * (word_list_.size() / thread_count_); + + threads.emplace_back( + [](const vector &word_list, const string_view &search_term, + vector &result, size_t start_index, size_t end_index, + mutex &result_mutex) { + for (size_t index = start_index; index < end_index; ++index) { + const auto ¤t_word = word_list[index]; + if (current_word.starts_with(search_term)) { + const lock_guard lock(result_mutex); + result.push_back(¤t_word); + } + } + }, + cref(word_list_), cref(search_term), ref(result), first_word_index, + last_word_index, ref(result_mutex)); + } + + for (auto &thread : threads) { + thread.join(); + } + + return result; +} diff --git a/parallel_finder.h b/parallel_finder.h new file mode 100644 index 0000000..556a167 --- /dev/null +++ b/parallel_finder.h @@ -0,0 +1,13 @@ +#pragma once + +#include "finder.h" + +class ParallelFinder : public Finder { +private: + const size_t thread_count_; + const vector &word_list_; + +public: + ParallelFinder(const vector &word_list, size_t thread_count); + vector find_prefix(string_view search_term) const override; +}; diff --git a/timer.cpp b/timer.cpp new file mode 100644 index 0000000..43987b5 --- /dev/null +++ b/timer.cpp @@ -0,0 +1,17 @@ +#include "timer.h" + +#include +#include + +Timer::Timer(std::string_view name) : name_(name) { start(); }; + +void Timer::start() { start_ = std::chrono::high_resolution_clock::now(); } + +void Timer::stop() { + auto end = std::chrono::high_resolution_clock::now(); + + auto duration = + std::chrono::duration_cast(end - start_); + + std::cout << name_ << " took " << duration << std::endl; +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..45226e4 --- /dev/null +++ b/timer.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class Timer { +private: + std::string name_; + std::chrono::time_point start_; + +public: + Timer(std::string_view name); + + void start(); + void stop(); +}; diff --git a/word_list_generator.cpp b/word_list_generator.cpp new file mode 100644 index 0000000..5863567 --- /dev/null +++ b/word_list_generator.cpp @@ -0,0 +1,34 @@ +#include "word_list_generator.h" + +#include +#include +#include + +const std::string WordListGenerator::charset_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +std::vector WordListGenerator::generate() { + const size_t multiplier = 10; + + std::vector result; + result.reserve(multiplier * std::pow(charset_.length(), 4)); + + for (auto char_1 : charset_) { + for (auto char_2 : charset_) { + for (auto char_3 : charset_) { + for (auto char_4 : charset_) { + for (auto i = 0; i < multiplier; ++i) { + result.emplace_back( + std::initializer_list({char_1, char_2, char_3, char_4})); + } + } + } + } + } + + std::random_device random_device; + std::mt19937 random_number_generator(random_device()); + + std::shuffle(result.begin(), result.end(), random_number_generator); + + return result; +} diff --git a/word_list_generator.h b/word_list_generator.h new file mode 100644 index 0000000..86c4b47 --- /dev/null +++ b/word_list_generator.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +class WordListGenerator { +private: + static const std::string charset_; + +public: + std::vector generate(); +};