Add prototype for fast non locking queue MPMC
This commit is contained in:
1 parent
1cfb0f4d1e
commit
ec41f87f97
1 file changed
+134
+134
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "m-core.h"
|
#include "m-core.h"
|
||||||
#include "m-mutex.h"
|
#include "m-mutex.h"
|
||||||
|
#include "m-atomic.h"
|
||||||
|
|
||||||
/* Define the different kind of policy a buffer can have. */
|
/* Define the different kind of policy a buffer can have. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -49,9 +50,28 @@ typedef enum {
|
|||||||
(BUFFERI_DEF2(name, type, m_size,__VA_ARGS__, (), M_C(name,_t)), \
|
(BUFFERI_DEF2(name, type, m_size,__VA_ARGS__, (), M_C(name,_t)), \
|
||||||
BUFFERI_DEF2(name, type, m_size,__VA_ARGS__, M_C(name,_t)))
|
BUFFERI_DEF2(name, type, m_size,__VA_ARGS__, M_C(name,_t)))
|
||||||
|
|
||||||
|
/* Define a queue for Many Producer Many Consummer
|
||||||
|
Much faster than queue of BUFFER_DEF in heavy communication scenario
|
||||||
|
Size of created queue can only a power of 2.
|
||||||
|
*/
|
||||||
|
#define QUEUE_MPMC_DEF(name, type, ...) \
|
||||||
|
M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||||
|
(QUEUEI_MPMC_DEF2(name, type, __VA_ARGS__, (), M_C(name,_t)), \
|
||||||
|
QUEUEI_MPMC_DEF2(name, type, __VA_ARGS__, M_C(name,_t)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********************************** INTERNAL ************************************/
|
/********************************** INTERNAL ************************************/
|
||||||
|
|
||||||
|
/* Define the exclusion size so that 2 atomic variables can be in
|
||||||
|
separate cache line. This prevent false sharing to occur within the
|
||||||
|
CPU cache. */
|
||||||
|
#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
|
||||||
|
# define BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION 128
|
||||||
|
#else
|
||||||
|
# define BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION 64
|
||||||
|
#endif
|
||||||
|
|
||||||
#define BUFFERI_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size))
|
#define BUFFERI_IF_CTE_SIZE(m_size) M_IF(M_BOOL(m_size))
|
||||||
#define BUFFERI_SIZE(m_size) BUFFERI_IF_CTE_SIZE(m_size) (m_size, v->size)
|
#define BUFFERI_SIZE(m_size) BUFFERI_IF_CTE_SIZE(m_size) (m_size, v->size)
|
||||||
|
|
||||||
@@ -289,6 +309,120 @@ M_C(name, _init)(buffer_t v, size_t size) \
|
|||||||
} \
|
} \
|
||||||
|
|
||||||
|
|
||||||
|
/* Definition of a a QUEUE for Many Produccer / Many Consummer
|
||||||
|
with high bandwidth. There is no blocking calls. */
|
||||||
|
|
||||||
|
#define QUEUEI_MPMC_DEF2(name, type, policy, oplist, buffer_t) \
|
||||||
|
\
|
||||||
|
/* The sequence number of an element will be equal to either \
|
||||||
|
- 2* the index of the production which creates it, \
|
||||||
|
- 1 + 2* the index of the consumption which consummes it \
|
||||||
|
*/ \
|
||||||
|
typedef struct M_C(name, _el_s) { \
|
||||||
|
atomic_ullong seq; /* Can only increase. */ \
|
||||||
|
type x; \
|
||||||
|
char align[BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION > sizeof(atomic_ullong)+sizeof(type) ? BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION - sizeof(atomic_ullong)-sizeof(type) : 1]; \
|
||||||
|
} M_C(name, _el_t); \
|
||||||
|
\
|
||||||
|
typedef struct M_C(name, _s) { \
|
||||||
|
atomic_ullong ProdIdx; /* Can only increase */ \
|
||||||
|
char align1[BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||||
|
atomic_ullong ConsoIdx; /* can only increase */ \
|
||||||
|
char align2[BUFFERI_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||||
|
element_t *Tab; \
|
||||||
|
unsigned int size; \
|
||||||
|
} buffer_t[1]; \
|
||||||
|
\
|
||||||
|
static inline bool \
|
||||||
|
M_C(name, _push)(buffer_t table, type x) \
|
||||||
|
{ \
|
||||||
|
const unsigned long long idx = atomic_load(&table->ProdIdx); \
|
||||||
|
const unsigned int i = idx & (table->size -1); \
|
||||||
|
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
||||||
|
if (M_UNLIKELY (2*(idx - table->size) + 1 != seq)) { \
|
||||||
|
/* Buffer full. Can not push */ \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
if (!atomic_compare_exchange_strong(&table->ProdIdx, &idx, idx+1)) { \
|
||||||
|
/* Thread has been preemptted by another one. */ \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
M_GET_SET oplist (table->Tab[i].x, x); \
|
||||||
|
atomic_store(&table->Tab[i].seq, 2*idx); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline bool \
|
||||||
|
M_C(name, _pop)(type *ptr, buffer_t table) \
|
||||||
|
{ \
|
||||||
|
const unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
||||||
|
const unsigned int i = (iC & (table->size -1)); \
|
||||||
|
const unsigned long long seq = atomic_load(&table->Tab[i].seq); \
|
||||||
|
if (seq != 2 * iC) { \
|
||||||
|
/* Nothing to consumme */ \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
if (!atomic_compare_exchange_strong(&table->ConsoIdx, &iC, iC+1)) { \
|
||||||
|
/* We have been preempted */ \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
M_GET_SET oplist (*ptr , table->Tab[i].x); \
|
||||||
|
atomic_store(&table->Tab[i].seq, 2*iC + 1); \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void \
|
||||||
|
M_C(name, _init)(buffer_t buffer, unsigned int size) \
|
||||||
|
{ \
|
||||||
|
assert (buffer != NULL); \
|
||||||
|
assert( M_POWEROF2_P(size)); \
|
||||||
|
atomic_init(&buffer->ProdIdx, size); \
|
||||||
|
atomic_init(&buffer->ConsoIdx, size); \
|
||||||
|
buffer->size = size; \
|
||||||
|
buffer->Tab = M_GET_REALLOC oplist (M_C(name, _el_t), NULL, size); \
|
||||||
|
if (buffer->data == NULL) { \
|
||||||
|
M_MEMORY_FULL (size*sizeof(M_C(name, _el_t) )); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
for(int j = 0; j < size; j++) { \
|
||||||
|
atomic_init(&buffer->Tab[j].seq, 2*j+1); \
|
||||||
|
M_GET_INIT oplist (buffer->Tab[j].x); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline void \
|
||||||
|
M_C(name, _clear)(buffer_t buffer) \
|
||||||
|
{ \
|
||||||
|
for(int j = 0; j < size; j++) { \
|
||||||
|
M_GET_CLEAR oplist (buffer->Tab[j].x); \
|
||||||
|
} \
|
||||||
|
M_GET_FREE oplist (buffer->Tab); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline size_t \
|
||||||
|
M_C(name, _size)(const buffer_t v) \
|
||||||
|
{ \
|
||||||
|
const unsigned long long iC = atomic_load(&table->ConsoIdx); \
|
||||||
|
const unsigned long long iP = atomic_load(&table->ProdIdx); \
|
||||||
|
/* We return an approximation as we can't read both iC & iP atomically*/ \
|
||||||
|
return iC >= iP ? 0 : iP-iC; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline bool \
|
||||||
|
M_C(name, _empty_p)(const buffer_t v) \
|
||||||
|
{ \
|
||||||
|
return M_C(name, _size) (v) == 0; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static inline bool \
|
||||||
|
M_C(name, _full_p)(const buffer_t v) \
|
||||||
|
{ \
|
||||||
|
return M_C(name, _size) == v->size; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
// TODO: INIT_MOVE policy to support
|
||||||
|
|
||||||
|
|
||||||
// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way.
|
// NOTE: SET & INIT_SET are deliberatly missing. TBC if it the right way.
|
||||||
// NOTE: There is no oplist defined for this container.
|
// NOTE: There is no oplist defined for this container.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user