CPP Multi-Threading Codes

2024-04-29
2 min read

CPP Multi-Threading

#include <atomic>
#include <ctime>
#include <iostream>
#include <memory>
#include <mutex>
#include <numeric>
#include <thread>
#include <vector>

constexpr int THD_NUM = 20;
constexpr int CNT_PER_THD = 50000000;

using namespace std;

class Counter {
public:
  virtual void operator++() {}
  virtual int64_t get() const { return 0; }
  virtual ~Counter() {}
};

class DistributedCounter : public Counter {
private:
  int64_t count{};
  std::mutex mutable mtx;

public:
  void operator++() override {
    std::lock_guard<std::mutex> lock(mtx);
    ++count;
  }
  int64_t get() const override {
    std::lock_guard<std::mutex> lock(mtx);
    return count;
  }
};

class DistributedCounterMultMtx : public Counter {
  struct bucket {
    mutable std::mutex sm;
    int64_t count{};
  };
  static size_t const buckets{THD_NUM};
  std::vector<bucket> counts{buckets};

public:
  void operator++() override {
    size_t index =
        std::hash<thread::id>()(std::this_thread::get_id()) % buckets;
    std::lock_guard<std::mutex> ul(counts[index].sm);
    ++(counts[index].count);
  }
  int64_t get() const override {
    return std::accumulate(counts.begin(), counts.end(), (int64_t)0,
                           [](auto acc, auto &x) {
                             std::lock_guard<std::mutex> sl(x.sm);
                             return acc + x.count;
                           });
  }
};

class DistributedCounterMultMtxPadding : public Counter {
  struct bucket {
    mutable std::mutex sm;
    int64_t count{};
    char padding[256];
  };
  static size_t const buckets{THD_NUM};
  std::vector<bucket> counts{buckets};

public:
  void operator++() override {
    size_t index =
        std::hash<thread::id>()(std::this_thread::get_id()) % buckets;
    std::lock_guard<std::mutex> ul(counts[index].sm);
    counts[index].count++;
  }
  int64_t get() const override {
    return std::accumulate(counts.begin(), counts.end(), (int64_t)0,
                           [](auto acc, auto &x) {
                             std::lock_guard<std::mutex> sl(x.sm);
                             return acc + x.count;
                           });
  }
};

class DistributedCounterOneAtomic : public Counter {
private:
  std::atomic<int64_t> count{};

public:
  void operator++() { ++count; }
  int64_t get() const override { return count; }
};

class DistributedCounterMultiAtomic : public Counter {
  struct bucket {
    std::atomic<int64_t> count;
  };
  static size_t const buckets{THD_NUM};
  std::vector<bucket> counts{buckets};

public:
  void operator++() override {
    size_t index =
        std::hash<thread::id>()(std::this_thread::get_id()) % buckets;
    ++(counts[index].count);
  }
  int64_t get() const override {
    return std::accumulate(
        counts.begin(), counts.end(), (int64_t)0,
        [](auto acc, auto &x) { return acc + x.count.load(); });
  }
};

class DistributedCounterMultiAtomicPadding : public Counter {
  struct bucket {
    std::atomic<int64_t> count;
    char padding[256];
  };
  static size_t const buckets{THD_NUM};
  std::vector<bucket> counts{buckets};

public:
  void operator++() {
    size_t index =
        std::hash<thread::id>()(std::this_thread::get_id()) % buckets;
    ++(counts[index].count);
  }
  int64_t get() const override {
    return std::accumulate(
        counts.begin(), counts.end(), (int64_t)0,
        [](auto acc, auto &x) { return acc + x.count.load(); });
  }
};

int main() {

  std::vector<std::pair<std::string, std::unique_ptr<Counter>>> counters;

  counters.emplace_back("DistributedCounterMtx", new DistributedCounter);
  counters.emplace_back("DistributedCounterMultMtx",
                        new DistributedCounterMultMtx);
  counters.emplace_back("DistributedCounterMultMtxPadding",
                        new DistributedCounterMultMtxPadding);
  counters.emplace_back("DistributedCounterOneAtomic",
                        new DistributedCounterOneAtomic);
  counters.emplace_back("DistributedCounterMultiAtomic",
                        new DistributedCounterMultiAtomic);
  counters.emplace_back("DistributedCounterMultiAtomicPadding",
                        new DistributedCounterMultiAtomicPadding);

  for (auto &p : counters) {
    time_t t;
    const auto start = std::time(&t);
    std::vector<std::thread> thds;
    auto t_num = THD_NUM;
    while (--t_num) {
      thds.emplace_back([&]() {
        auto cnt = CNT_PER_THD;
        while (--cnt)
          ++(*p.second);
      });
    }

    for (auto &t : thds) {
      t.join();
    }
    const auto end = std::time(&t);

    std::cout << p.first << ": " << end - start
              << ", get(): " << p.second->get() << std::endl;
  }
}
Next Flex&Bison