Advertisement
giGii

marry_map

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