From 59d56923f5678e9bb9881f4a91265d99347c202f Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Sun, 9 Apr 2023 18:48:26 -0400 Subject: [PATCH] Add example mt_word_counter --- CMakeLists.txt | 1 + examples/mt_word_counter.cc | 83 +++++++++++++++++++++++++++++++ parallel_hashmap/phmap_fwd_decl.h | 32 ++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 examples/mt_word_counter.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index b40eb4a..7b0a6ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,7 @@ if (PHMAP_BUILD_EXAMPLES) add_executable(ex_dump_load examples/dump_load.cc phmap.natvis) add_executable(ex_btree examples/btree.cc phmap.natvis) add_executable(ex_matt examples/matt.cc phmap.natvis) + add_executable(ex_mt_word_counter examples/mt_word_counter.cc phmap.natvis) target_link_libraries(ex_knucleotide Threads::Threads) target_link_libraries(ex_bench Threads::Threads) diff --git a/examples/mt_word_counter.cc b/examples/mt_word_counter.cc new file mode 100644 index 0000000..aad7124 --- /dev/null +++ b/examples/mt_word_counter.cc @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * count the number of occurrences of each word in a large text file using multiple threads + */ + +int main() { + // download Jane Austin "Pride and Prejudice" + // ------------------------------------------ + if (system("wget https://www.gutenberg.org/files/1342/1342-0.txt") != 0) { + std::cout << "Error: could not retrieve test file https://www.gutenberg.org/files/1342/1342-0.txt\n"; + return 1; + } + + const std::string filename = "1342-0.txt"; + + constexpr int num_threads = 4; + std::vector threads; + std::array, num_threads> lines_array; + + { + // populate 4 vectors with lines from the book + std::ifstream file(filename); + if (!file.is_open()) { + std::cout << "Error: could not open file " << filename << std::endl; + return 1; + } + int line_idx = 0; + std::string line; + while (std::getline(file, line)) { + lines_array[line_idx % num_threads].push_back(std::move(line)); + ++line_idx; + } + } + + using Map = phmap::parallel_flat_hash_map_m; // parallel_flat_hash_map_m has default internal mutex + Map word_counts; + + // run 4 threads, each thread processing lines from one of the vectors + // ------------------------------------------------------------------- + for (int i = 0; i < num_threads; ++i) { + threads.emplace_back( + [&word_counts](std::vector&& lines) { + for (auto& line : lines) { + std::replace_if(line.begin(), line.end(), [](char c) -> bool { return !std::isalnum(c); }, ' '); + std::istringstream iss(line); + std::string word; + while (iss >> word) { + // use lazy_emplace to modify the map while the mutex is locked + word_counts.lazy_emplace_l(word, + [&](Map::value_type& p) { ++p.second; }, // called only when key was already present + [&](const Map::constructor& ctor) // construct value_type in place when key not present + { ctor(std::move(word), 1); } ); + } + } + }, + std::move(lines_array[i])); + } + + for (auto& thread : threads) + thread.join(); + + // print one word used at each frequency + // ------------------------------------- + phmap::btree_map result; + for (const auto& pair : word_counts) + result[pair.second] = pair.first; + + for (const auto& p : result) + std::cout << p.first << ": " << p.second << std::endl; + + return 0; +} diff --git a/parallel_hashmap/phmap_fwd_decl.h b/parallel_hashmap/phmap_fwd_decl.h index a7719c4..c625be1 100644 --- a/parallel_hashmap/phmap_fwd_decl.h +++ b/parallel_hashmap/phmap_fwd_decl.h @@ -20,6 +20,7 @@ #include #include +#include #if defined(PHMAP_USE_ABSL_HASH) && !defined(ABSL_HASH_HASH_H_) namespace absl { template struct Hash; }; @@ -127,6 +128,37 @@ namespace phmap { class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks class parallel_node_hash_map; + // ----------------------------------------------------------------------------- + // phmap::parallel_*_hash_* using std::mutex by default + // ----------------------------------------------------------------------------- + template , + class Eq = phmap::priv::hash_default_eq, + class Alloc = phmap::priv::Allocator, + size_t N = 4> + using parallel_flat_hash_set_m = parallel_flat_hash_set; + + template , + class Eq = phmap::priv::hash_default_eq, + class Alloc = phmap::priv::Allocator>, + size_t N = 4> + using parallel_flat_hash_map_m = parallel_flat_hash_map; + + template , + class Eq = phmap::priv::hash_default_eq, + class Alloc = phmap::priv::Allocator, + size_t N = 4> + using parallel_node_hash_set_m = parallel_node_hash_set; + + template , + class Eq = phmap::priv::hash_default_eq, + class Alloc = phmap::priv::Allocator>, + size_t N = 4> + using parallel_node_hash_map_m = parallel_node_hash_map; + // ------------- forward declarations for btree containers ---------------------------------- template , typename Alloc = phmap::Allocator>