shushus

Спринт 9. Как поженить словарь и многопоточность.

Mar 6th, 2023
353
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 3.68 KB | None | 0 0
  1. #include <algorithm>
  2. #include <cstdlib>
  3. #include <future>
  4. #include <map>
  5. #include <numeric>
  6. #include <random>
  7. #include <string>
  8. #include <vector>
  9. #include <mutex>
  10.  
  11. #include "log_duration.h"
  12. #include "test_framework.h"
  13.  
  14. using namespace std::string_literals;
  15.  
  16. template <typename Key, typename Value>
  17. class ConcurrentMap {
  18. private:
  19.     struct Bucket {
  20.         std::mutex mutex_;
  21.         std::map<Key, Value> map_;
  22.     };
  23.    
  24.     std::vector<Bucket> bucket_;
  25.    
  26.  public:
  27.     static_assert(std::is_integral_v<Key>, "ConcurrentMap supports only integer keys"s);
  28.  
  29.     struct Access {
  30.         std::lock_guard<std::mutex> lock_guard_mutex;
  31.         Value& ref_to_value;
  32.  
  33.         Access(const Key& key, Bucket& bucket) : lock_guard_mutex(bucket.mutex_)
  34.                                                , ref_to_value(bucket.map_[key]) {}
  35.     };
  36.  
  37.     explicit ConcurrentMap(size_t bucket_count) : bucket_(bucket_count) {}
  38.  
  39.     Access operator[](const Key& key) {
  40.    
  41.         auto& bucket = bucket_[uint64_t(key) %bucket_.size()];
  42.         return {key,
  43.                 bucket};
  44.     }
  45.  
  46.     std::map<Key, Value> BuildOrdinaryMap() {
  47.    
  48.         std::map<Key, Value> result;
  49.        
  50.         for (auto& [mutex_, map_] : bucket_) {
  51.             std::lock_guard lock_guard_mutex(mutex_);
  52.             result.insert(map_.begin(), map_.end());
  53.         }
  54.        
  55.         return result;
  56.     }
  57. };
  58.  
  59.  
  60. using namespace std;
  61.  
  62. void RunConcurrentUpdates(ConcurrentMap<int, int>& cm, size_t thread_count, int key_count) {
  63.     auto kernel = [&cm, key_count](int seed) {
  64.         vector<int> updates(key_count);
  65.         iota(begin(updates), end(updates), -key_count / 2);
  66.         shuffle(begin(updates), end(updates), mt19937(seed));
  67.  
  68.         for (int i = 0; i < 2; ++i) {
  69.             for (auto key : updates) {
  70.                 ++cm[key].ref_to_value;
  71.             }
  72.         }
  73.     };
  74.  
  75.     vector<future<void>> futures;
  76.     for (size_t i = 0; i < thread_count; ++i) {
  77.         futures.push_back(async(kernel, i));
  78.     }
  79. }
  80.  
  81. void TestConcurrentUpdate() {
  82.     constexpr size_t THREAD_COUNT = 3;
  83.     constexpr size_t KEY_COUNT = 50000;
  84.  
  85.     ConcurrentMap<int, int> cm(THREAD_COUNT);
  86.     RunConcurrentUpdates(cm, THREAD_COUNT, KEY_COUNT);
  87.  
  88.     const auto result = cm.BuildOrdinaryMap();
  89.     ASSERT_EQUAL(result.size(), KEY_COUNT);
  90.     for (auto& [k, v] : result) {
  91.         AssertEqual(v, 6, "Key = " + to_string(k));
  92.     }
  93. }
  94.  
  95. void TestReadAndWrite() {
  96.     ConcurrentMap<size_t, string> cm(5);
  97.  
  98.     auto updater = [&cm] {
  99.         for (size_t i = 0; i < 50000; ++i) {
  100.             cm[i].ref_to_value.push_back('a');
  101.         }
  102.     };
  103.     auto reader = [&cm] {
  104.         vector<string> result(50000);
  105.         for (size_t i = 0; i < result.size(); ++i) {
  106.             result[i] = cm[i].ref_to_value;
  107.         }
  108.         return result;
  109.     };
  110.  
  111.     auto u1 = async(updater);
  112.     auto r1 = async(reader);
  113.     auto u2 = async(updater);
  114.     auto r2 = async(reader);
  115.  
  116.     u1.get();
  117.     u2.get();
  118.  
  119.     for (auto f : {&r1, &r2}) {
  120.         auto result = f->get();
  121.         ASSERT(all_of(result.begin(), result.end(), [](const string& s) {
  122.             return s.empty() || s == "a" || s == "aa";
  123.         }));
  124.     }
  125. }
  126.  
  127. void TestSpeedup() {
  128.     {
  129.         ConcurrentMap<int, int> single_lock(1);
  130.  
  131.         LOG_DURATION("Single lock");
  132.         RunConcurrentUpdates(single_lock, 4, 50000);
  133.     }
  134.     {
  135.         ConcurrentMap<int, int> many_locks(100);
  136.  
  137.         LOG_DURATION("100 locks");
  138.         RunConcurrentUpdates(many_locks, 4, 50000);
  139.     }
  140. }
  141.  
  142. int main() {
  143.     TestRunner tr;
  144.     RUN_TEST(tr, TestConcurrentUpdate);
  145.     RUN_TEST(tr, TestReadAndWrite);
  146.     RUN_TEST(tr, TestSpeedup);
  147. }
Advertisement
Add Comment
Please, Sign In to add comment