Guest User

Distributed Multi-Producer/Consumer Bakery SpinLock Queue...

a guest
Mar 5th, 2017
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.38 KB | None | 0 0
  1. #include <cstdio>
  2. #include <memory>
  3. #include <atomic>
  4. #include <thread>
  5. #include <functional>
  6.  
  7.  
  8. #define N 10000000
  9.  
  10. #define mb_relaxed std::memory_order_relaxed
  11. #define mb_acquire std::memory_order_acquire
  12. #define mb_release std::memory_order_release
  13.  
  14. #define mb_fence(mb) std::atomic_thread_fence(mb)
  15.  
  16. /*
  17. struct cell { uint32_t ver; double state; };
  18.  
  19. uint32_t head = 0;
  20. uint32_t tail = 0;
  21. cell cells[N]; // N must be a power of 2
  22.  
  23. void init() {
  24. for (uint32_t i = 0; i < N; ++i) cells[i].ver = i;
  25. }
  26.  
  27. void producer(double state) {
  28. uint32_t ver = XADD(&head, 1);
  29. cell& c = cells[ver & (N - 1)];
  30. while (LOAD(&c.ver) != ver) backoff();
  31. c.state = state;
  32. STORE(&c.ver, ver + 1);
  33. }
  34.  
  35. double consumer() {
  36. uint32_t ver = XADD(&tail, 1);
  37. cell& c = cells[ver & (N - 1)];
  38. while (LOAD(&c.ver) != ver + 1) backoff();
  39. double state = c.state;
  40. STORE(&c.ver, ver + N);
  41. return state;
  42. }
  43. */
  44.  
  45.  
  46. template<typename T, unsigned long T_depth>
  47. struct single_producer_consumer_queue
  48. {
  49.     struct cell
  50.     {
  51.         std::atomic<unsigned long> m_ver;
  52.         T m_data;
  53.     };
  54.  
  55.     std::atomic<unsigned long> m_head;
  56.     std::atomic<unsigned long> m_tail;
  57.     cell m_cells[T_depth];
  58.  
  59.  
  60.     single_producer_consumer_queue()
  61.         : m_head(0),
  62.         m_tail(0)
  63.     {
  64.         for (unsigned long i = 0; i < T_depth; ++i)
  65.         {
  66.             m_cells[i].m_ver.store(i, mb_relaxed);
  67.         }
  68.     }
  69.  
  70.  
  71.     void produce(T const& in)
  72.     {
  73.         unsigned long ver = produce_begin();
  74.         while (!produce_commit(ver, in))
  75.         {
  76.             std::printf("produce wait ver %lu\n", ver);
  77.             std::this_thread::yield();
  78.             std::this_thread::yield();
  79.             std::this_thread::yield();
  80.             std::this_thread::yield();
  81.         }
  82.     }
  83.  
  84.  
  85.     void consume(T& out)
  86.     {
  87.         unsigned long ver = consume_begin();
  88.         while (!consume_commit(ver, out))
  89.         {
  90.             std::printf("consume wait ver %lu\n", ver);
  91.             std::this_thread::yield();
  92.             std::this_thread::yield();
  93.             std::this_thread::yield();
  94.             std::this_thread::yield();
  95.         }
  96.  
  97.     }
  98.  
  99.  
  100.     unsigned long produce_begin()
  101.     {
  102.         return m_head.fetch_add(1, mb_relaxed);
  103.     }
  104.  
  105.     bool produce_commit(unsigned long ver, T const& in)
  106.     {
  107.         cell& c = m_cells[ver & (T_depth - 1)];
  108.         if (c.m_ver.load(mb_relaxed) != ver) return false;
  109.         mb_fence(mb_acquire);
  110.         c.m_data = in;
  111.         mb_fence(mb_release);
  112.         c.m_ver.store(ver + 1, mb_relaxed);
  113.         return true;
  114.     }
  115.  
  116.     unsigned long consume_begin()
  117.     {
  118.         return m_tail.fetch_add(1, mb_relaxed);
  119.     }
  120.  
  121.     bool consume_commit(unsigned long ver, T& out)
  122.     {
  123.         cell& c = m_cells[ver & (T_depth - 1)];
  124.         if (c.m_ver.load(mb_relaxed) != ver + 1) return false;
  125.         mb_fence(mb_acquire);
  126.         out = c.m_data;
  127.         mb_fence(mb_release);
  128.         c.m_ver.store(ver + T_depth, mb_relaxed);
  129.         return true;
  130.     }
  131. };
  132.  
  133.  
  134. typedef single_producer_consumer_queue<unsigned long, 8192 * 2> spscq_t;
  135.  
  136. void producer(spscq_t* const q)
  137. {
  138.     std::printf("producer(%p)\n", (void*)q);
  139.  
  140.     for (unsigned long i = 0; i < N; ++i)
  141.     {
  142.         q->produce(i);
  143.         //std::printf("producer(%p)::produced(%lu)\n", (void*)&q, i);
  144.         std::this_thread::yield();
  145.         std::this_thread::yield();
  146.  
  147.         if (!(i % 100000))
  148.         {
  149.             std::printf("producer(%p)::producer(%lu)\n", (void*)&q, i);
  150.         }
  151.     }
  152. }
  153.  
  154. void consumer(spscq_t* const q)
  155. {
  156.     std::printf("consumer(%p)\n", (void*)q);
  157.  
  158.     unsigned long consumed = 0;
  159.  
  160.     for (unsigned long i = 0; i < N; ++i)
  161.     {
  162.  
  163.         q->consume(consumed);
  164.         std::this_thread::yield();
  165.         std::this_thread::yield();
  166.  
  167.  
  168.         if (!(i % 100000))
  169.         {
  170.             std::printf("consumer(%p)::consumed(%lu):%lu\n", (void*)&q, consumed, i);
  171.         }
  172.     }
  173.  
  174.     std::printf("\n\n final: consumer(%p)::consumed(%lu)\n", (void*)q, consumed);
  175. }
  176.  
  177.  
  178. int main(void)
  179. {
  180.     {
  181.         std::unique_ptr<spscq_t> q(new spscq_t);
  182.  
  183.         std::thread consumer_thread(consumer, q.get());
  184.         std::thread producer_thread(producer, q.get());
  185.  
  186.         producer_thread.join();
  187.         consumer_thread.join();
  188.     }
  189.  
  190.     std::printf("\nComplete, hit <ENTER> to exit...\n");
  191.     std::fflush(stdout);
  192.     std::getchar();
  193.  
  194.     return 0;
  195. }
Advertisement
Add Comment
Please, Sign In to add comment