<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic an eventcount algorithm... in Intel® Moderncode for Parallel Architectures</title>
    <link>https://community.intel.com/t5/Intel-Moderncode-for-Parallel/an-eventcount-algorithm/m-p/869918#M2840</link>
    <description>&lt;DIV&gt;&lt;SPAN style="font-family: Verdana;"&gt;I am presenting an eventcount algorithm I invented. I know that Dmitriy Vyukov has already submitted one to the TBB, but his algorithm is based on TLS/TSD. I just wanted to show an alternative technique that does not require TLS/TSD. Here is code which is based on Dmitriy excellent Relacy Race Detector package:&lt;/SPAN&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;BR /&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;
&lt;PRE&gt;[cpp]#define RL_GC
#include &lt;RELACY&gt;
#include &lt;CSTDIO&gt;



class eventcount {
public:
  typedef unsigned long key_type;


private:
  mutable rl::atomic&lt;KEY_TYPE&gt; m_count;
  rl::mutex m_mutex;
  rl::condition_variable m_cond;


  void prv_signal(key_type key) {
    if (key &amp;amp; 1) {
      m_mutex.lock($);
      while (! m_count($).compare_exchange_weak(key, (key + 2) &amp;amp; ~1, 
        rl::memory_order_seq_cst));
      m_mutex.unlock($);
      m_cond.notify_all($);
    }
  }


public:
  eventcount() {
    m_count($).store(0, rl::memory_order_relaxed);
  }


public:
  key_type get() const {
    return m_count($).fetch_or(1, rl::memory_order_acquire);
  }


  void signal() {
    prv_signal(m_count($).fetch_add(0, rl::memory_order_seq_cst));
  }


  void signal_relaxed() {
    prv_signal(m_count($).load(rl::memory_order_relaxed));
  }


  void wait(key_type cmp) {
    m_mutex.lock($);
    if ((m_count($).load(rl::memory_order_seq_cst) &amp;amp; ~1) == (cmp &amp;amp; ~1)) {
      m_cond.wait(m_mutex, $);
    }
    m_mutex.unlock($);
  }
};




template&lt;TYPENAME t=""&gt;
class mpmcq {
  struct node {
    rl::atomic&lt;NODE&gt; m_next;
    T volatile m_state;
  };


  rl::atomic&lt;NODE&gt; m_head;
  rl::atomic&lt;NODE&gt; m_tail;


public:
  mpmcq() {
    node* n = RL_NEW(node);
    n-&amp;gt;m_next($).store(NULL, rl::memory_order_relaxed);
    m_head($).store(n, rl::memory_order_relaxed);
    m_tail($).store(n, rl::memory_order_relaxed);
  }


  ~mpmcq() {
    RL_ASSERT(m_head($).load(rl::memory_order_relaxed) ==
              m_tail($).load(rl::memory_order_relaxed));
  }


public:
  void push(T&amp;amp; state) {
    node* n = RL_NEW(node);
    n-&amp;gt;m_next($).store(NULL, rl::memory_order_relaxed);
    n-&amp;gt;m_state = state;
    node* p = m_head($).exchange(n, rl::memory_order_seq_cst);
    p-&amp;gt;m_next($).store(n, rl::memory_order_seq_cst);
  }


  bool pop(T&amp;amp; state) {
    node* n;
    node* t = m_tail($).load(rl::memory_order_seq_cst);
    do {
      n = t-&amp;gt;m_next($).load(rl::memory_order_seq_cst);
      if (! n) return false;
      state = n-&amp;gt;m_state;
    } while (! m_tail($).compare_exchange_weak(t, n, rl::memory_order_seq_cst));
    return true;
  }
};





#define PRODUCERS 2
#define CONSUMERS 2
#define THREADS (PRODUCERS + CONSUMERS)
#define ITERS 6


struct mpmcq_test : rl::test_suite&lt;MPMCQ_TEST&gt; {
  eventcount m_ecount;
  mpmcq&lt;INT&gt; m_queue;
  std::atomic&lt;INT&gt; m_count;


  void before() {
    m_count($).store(PRODUCERS * ITERS, rl::memory_order_relaxed);
  }


  void invariant() {
    RL_ASSERT(m_count($).load(rl::memory_order_relaxed) &amp;gt; -1);
  }


  void after() {
    RL_ASSERT(! m_count($).load(rl::memory_order_relaxed));
  }


  void thread(unsigned tidx) {
    int i;
    if (tidx &amp;lt; PRODUCERS) {
      for (i = 0; i &amp;lt; ITERS; ++i) {
        m_queue.push(i);
        m_ecount.signal();
      }
    } else {
      do {
        while (! m_queue.pop(i)) {
          eventcount::key_type key = m_ecount.get();
          if (m_queue.pop(i)) break;
          m_ecount.wait(key);
        }
      } while (i != -666 &amp;amp;&amp;amp;
               m_count($).fetch_add(-1, rl::memory_order_relaxed) != 1);
      if (i != -666) {
        for (i = 1; i &amp;lt; CONSUMERS; ++i) {
          int x = -666;
          m_queue.push(x);
        }
        m_ecount.signal();
      }
    }
  }
};




int main() {
  rl::test_params params;
  params.iteration_count = 99999999;
  //params.search_type = rl::fair_full_search_scheduler_type;
  //params.search_type = rl::fair_context_bound_scheduler_type;
  rl::simulate&lt;MPMCQ_TEST&gt;(params);
  std::puts("nnn____________________________________n"
            "DONE!!!!npress &lt;ENTER&gt; to exit...");
  std::fflush(stdin);
  std::fflush(stdout);
  std::getchar();
  return 0;
}[/cpp]&lt;/ENTER&gt;&lt;/MPMCQ_TEST&gt;&lt;/INT&gt;&lt;/INT&gt;&lt;/MPMCQ_TEST&gt;&lt;/NODE&gt;&lt;/NODE&gt;&lt;/NODE&gt;&lt;/TYPENAME&gt;&lt;/KEY_TYPE&gt;&lt;/CSTDIO&gt;&lt;/RELACY&gt;&lt;/PRE&gt;
&lt;BR /&gt;&lt;BR /&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;Here is an implementation of the algorithm in C#:&lt;BR /&gt;&lt;BR /&gt;&lt;A href="http://cpt.pastebin.com/f72cc3cc1" target="_blank"&gt;http://cpt.pastebin.com/f72cc3cc1&lt;/A&gt;&lt;BR /&gt;&lt;BR /&gt;You can obtain Relacy here:&lt;BR /&gt;&lt;BR /&gt;&lt;A href="http://groups.google.com/group/relacy" target="_blank"&gt;http://groups.google.com/group/relacy&lt;/A&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;The code shows how to use an eventcount which is basically like a condition variable for non-blocking, or even blocking, algorithms. Its an extremely usefull algorithm wrt adding conditional waiting ability to basically any non-blocking algorithm. If you have any questions on how to use it, please feel free to ask!&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;:^)&lt;/DIV&gt;
&lt;/DIV&gt;</description>
    <pubDate>Sat, 27 Jun 2009 15:51:30 GMT</pubDate>
    <dc:creator>Chris_M__Thomasson</dc:creator>
    <dc:date>2009-06-27T15:51:30Z</dc:date>
    <item>
      <title>an eventcount algorithm...</title>
      <link>https://community.intel.com/t5/Intel-Moderncode-for-Parallel/an-eventcount-algorithm/m-p/869918#M2840</link>
      <description>&lt;DIV&gt;&lt;SPAN style="font-family: Verdana;"&gt;I am presenting an eventcount algorithm I invented. I know that Dmitriy Vyukov has already submitted one to the TBB, but his algorithm is based on TLS/TSD. I just wanted to show an alternative technique that does not require TLS/TSD. Here is code which is based on Dmitriy excellent Relacy Race Detector package:&lt;/SPAN&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;BR /&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;
&lt;PRE&gt;[cpp]#define RL_GC
#include &lt;RELACY&gt;
#include &lt;CSTDIO&gt;



class eventcount {
public:
  typedef unsigned long key_type;


private:
  mutable rl::atomic&lt;KEY_TYPE&gt; m_count;
  rl::mutex m_mutex;
  rl::condition_variable m_cond;


  void prv_signal(key_type key) {
    if (key &amp;amp; 1) {
      m_mutex.lock($);
      while (! m_count($).compare_exchange_weak(key, (key + 2) &amp;amp; ~1, 
        rl::memory_order_seq_cst));
      m_mutex.unlock($);
      m_cond.notify_all($);
    }
  }


public:
  eventcount() {
    m_count($).store(0, rl::memory_order_relaxed);
  }


public:
  key_type get() const {
    return m_count($).fetch_or(1, rl::memory_order_acquire);
  }


  void signal() {
    prv_signal(m_count($).fetch_add(0, rl::memory_order_seq_cst));
  }


  void signal_relaxed() {
    prv_signal(m_count($).load(rl::memory_order_relaxed));
  }


  void wait(key_type cmp) {
    m_mutex.lock($);
    if ((m_count($).load(rl::memory_order_seq_cst) &amp;amp; ~1) == (cmp &amp;amp; ~1)) {
      m_cond.wait(m_mutex, $);
    }
    m_mutex.unlock($);
  }
};




template&lt;TYPENAME t=""&gt;
class mpmcq {
  struct node {
    rl::atomic&lt;NODE&gt; m_next;
    T volatile m_state;
  };


  rl::atomic&lt;NODE&gt; m_head;
  rl::atomic&lt;NODE&gt; m_tail;


public:
  mpmcq() {
    node* n = RL_NEW(node);
    n-&amp;gt;m_next($).store(NULL, rl::memory_order_relaxed);
    m_head($).store(n, rl::memory_order_relaxed);
    m_tail($).store(n, rl::memory_order_relaxed);
  }


  ~mpmcq() {
    RL_ASSERT(m_head($).load(rl::memory_order_relaxed) ==
              m_tail($).load(rl::memory_order_relaxed));
  }


public:
  void push(T&amp;amp; state) {
    node* n = RL_NEW(node);
    n-&amp;gt;m_next($).store(NULL, rl::memory_order_relaxed);
    n-&amp;gt;m_state = state;
    node* p = m_head($).exchange(n, rl::memory_order_seq_cst);
    p-&amp;gt;m_next($).store(n, rl::memory_order_seq_cst);
  }


  bool pop(T&amp;amp; state) {
    node* n;
    node* t = m_tail($).load(rl::memory_order_seq_cst);
    do {
      n = t-&amp;gt;m_next($).load(rl::memory_order_seq_cst);
      if (! n) return false;
      state = n-&amp;gt;m_state;
    } while (! m_tail($).compare_exchange_weak(t, n, rl::memory_order_seq_cst));
    return true;
  }
};





#define PRODUCERS 2
#define CONSUMERS 2
#define THREADS (PRODUCERS + CONSUMERS)
#define ITERS 6


struct mpmcq_test : rl::test_suite&lt;MPMCQ_TEST&gt; {
  eventcount m_ecount;
  mpmcq&lt;INT&gt; m_queue;
  std::atomic&lt;INT&gt; m_count;


  void before() {
    m_count($).store(PRODUCERS * ITERS, rl::memory_order_relaxed);
  }


  void invariant() {
    RL_ASSERT(m_count($).load(rl::memory_order_relaxed) &amp;gt; -1);
  }


  void after() {
    RL_ASSERT(! m_count($).load(rl::memory_order_relaxed));
  }


  void thread(unsigned tidx) {
    int i;
    if (tidx &amp;lt; PRODUCERS) {
      for (i = 0; i &amp;lt; ITERS; ++i) {
        m_queue.push(i);
        m_ecount.signal();
      }
    } else {
      do {
        while (! m_queue.pop(i)) {
          eventcount::key_type key = m_ecount.get();
          if (m_queue.pop(i)) break;
          m_ecount.wait(key);
        }
      } while (i != -666 &amp;amp;&amp;amp;
               m_count($).fetch_add(-1, rl::memory_order_relaxed) != 1);
      if (i != -666) {
        for (i = 1; i &amp;lt; CONSUMERS; ++i) {
          int x = -666;
          m_queue.push(x);
        }
        m_ecount.signal();
      }
    }
  }
};




int main() {
  rl::test_params params;
  params.iteration_count = 99999999;
  //params.search_type = rl::fair_full_search_scheduler_type;
  //params.search_type = rl::fair_context_bound_scheduler_type;
  rl::simulate&lt;MPMCQ_TEST&gt;(params);
  std::puts("nnn____________________________________n"
            "DONE!!!!npress &lt;ENTER&gt; to exit...");
  std::fflush(stdin);
  std::fflush(stdout);
  std::getchar();
  return 0;
}[/cpp]&lt;/ENTER&gt;&lt;/MPMCQ_TEST&gt;&lt;/INT&gt;&lt;/INT&gt;&lt;/MPMCQ_TEST&gt;&lt;/NODE&gt;&lt;/NODE&gt;&lt;/NODE&gt;&lt;/TYPENAME&gt;&lt;/KEY_TYPE&gt;&lt;/CSTDIO&gt;&lt;/RELACY&gt;&lt;/PRE&gt;
&lt;BR /&gt;&lt;BR /&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;Here is an implementation of the algorithm in C#:&lt;BR /&gt;&lt;BR /&gt;&lt;A href="http://cpt.pastebin.com/f72cc3cc1" target="_blank"&gt;http://cpt.pastebin.com/f72cc3cc1&lt;/A&gt;&lt;BR /&gt;&lt;BR /&gt;You can obtain Relacy here:&lt;BR /&gt;&lt;BR /&gt;&lt;A href="http://groups.google.com/group/relacy" target="_blank"&gt;http://groups.google.com/group/relacy&lt;/A&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;The code shows how to use an eventcount which is basically like a condition variable for non-blocking, or even blocking, algorithms. Its an extremely usefull algorithm wrt adding conditional waiting ability to basically any non-blocking algorithm. If you have any questions on how to use it, please feel free to ask!&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;&lt;BR /&gt;&lt;/DIV&gt;
&lt;DIV&gt;:^)&lt;/DIV&gt;
&lt;/DIV&gt;</description>
      <pubDate>Sat, 27 Jun 2009 15:51:30 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-Moderncode-for-Parallel/an-eventcount-algorithm/m-p/869918#M2840</guid>
      <dc:creator>Chris_M__Thomasson</dc:creator>
      <dc:date>2009-06-27T15:51:30Z</dc:date>
    </item>
  </channel>
</rss>

