|
Packit Service |
c5cf8c |
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
|
|
Packit Service |
c5cf8c |
/*
|
|
Packit Service |
c5cf8c |
* See COPYRIGHT in top-level directory.
|
|
Packit Service |
c5cf8c |
*/
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
/*
|
|
Packit Service |
c5cf8c |
* The original version of this code was contributed by Milind Chabbi
|
|
Packit Service |
c5cf8c |
* based on the work when he was at Rice University. It relies on the
|
|
Packit Service |
c5cf8c |
* HMCS lock description in [1] and the fast path described in [2].
|
|
Packit Service |
c5cf8c |
*
|
|
Packit Service |
c5cf8c |
* [1] Chabbi, Milind, Michael Fagan, and John Mellor-Crummey. "High
|
|
Packit Service |
c5cf8c |
* performance locks for multi-level NUMA systems." In Proceedings of
|
|
Packit Service |
c5cf8c |
* the ACM SIGPLAN Symposium on Principles and Practice of Parallel
|
|
Packit Service |
c5cf8c |
* Programming (PPoPP'15), ACM, 2015.
|
|
Packit Service |
c5cf8c |
*
|
|
Packit Service |
c5cf8c |
* [2] Chabbi, Milind, and John Mellor-Crummey. "Contention-conscious,
|
|
Packit Service |
c5cf8c |
* locality-preserving locks." In Proceedings of the 21st ACM SIGPLAN
|
|
Packit Service |
c5cf8c |
* Symposium on Principles and Practice of Parallel Programming (PPoPP'16,
|
|
Packit Service |
c5cf8c |
* p. 22. ACM, 2016.
|
|
Packit Service |
c5cf8c |
*/
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#include "lock/zm_lock_types.h"
|
|
Packit Service |
c5cf8c |
#include <hwloc.h>
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#ifndef DEFAULT_THRESHOLD
|
|
Packit Service |
c5cf8c |
#define DEFAULT_THRESHOLD 256
|
|
Packit Service |
c5cf8c |
#endif
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#ifndef HMCS_DEFAULT_MAX_LEVELS
|
|
Packit Service |
c5cf8c |
#define HMCS_DEFAULT_MAX_LEVELS 3
|
|
Packit Service |
c5cf8c |
#endif
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#define WAIT (0xffffffff)
|
|
Packit Service |
c5cf8c |
#define COHORT_START (0x1)
|
|
Packit Service |
c5cf8c |
#define ACQUIRE_PARENT (0xcffffffc)
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#ifndef TRUE
|
|
Packit Service |
c5cf8c |
#define TRUE 1
|
|
Packit Service |
c5cf8c |
#else
|
|
Packit Service |
c5cf8c |
#error "TRUE already defined"
|
|
Packit Service |
c5cf8c |
#endif
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#ifndef FALSE
|
|
Packit Service |
c5cf8c |
#define FALSE 0
|
|
Packit Service |
c5cf8c |
#else
|
|
Packit Service |
c5cf8c |
#error "TRUE already defined"
|
|
Packit Service |
c5cf8c |
#endif
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
/* Atomic operation shorthands. The memory ordering defaults to:
|
|
Packit Service |
c5cf8c |
* 1- Acquire ordering for loads
|
|
Packit Service |
c5cf8c |
* 2- Release ordering for stores
|
|
Packit Service |
c5cf8c |
* 3- Acquire+Release ordering for read-modify-write operations
|
|
Packit Service |
c5cf8c |
* */
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#define LOAD(addr) zm_atomic_load(addr, zm_memord_acquire)
|
|
Packit Service |
c5cf8c |
#define STORE(addr, val) zm_atomic_store(addr, val, zm_memord_release)
|
|
Packit Service |
c5cf8c |
#define SWAP(addr, desire) zm_atomic_exchange_ptr(addr, desire, zm_memord_acq_rel)
|
|
Packit Service |
c5cf8c |
#define CAS(addr, expect, desire) zm_atomic_compare_exchange_strong(addr,\
|
|
Packit Service |
c5cf8c |
expect,\
|
|
Packit Service |
c5cf8c |
desire,\
|
|
Packit Service |
c5cf8c |
zm_memord_acq_rel,\
|
|
Packit Service |
c5cf8c |
zm_memord_acquire)
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
struct hnode{
|
|
Packit Service |
c5cf8c |
unsigned threshold __attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
struct hnode * parent __attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
zm_atomic_ptr_t lock __attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t node __attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
}__attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
struct leaf{
|
|
Packit Service |
c5cf8c |
struct hnode * cur_node;
|
|
Packit Service |
c5cf8c |
struct hnode * root_node;
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t I;
|
|
Packit Service |
c5cf8c |
int curDepth;
|
|
Packit Service |
c5cf8c |
int took_fast_path;
|
|
Packit Service |
c5cf8c |
};
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
struct lock{
|
|
Packit Service |
c5cf8c |
// Assumes tids range from [0.. max_threads)
|
|
Packit Service |
c5cf8c |
// Assumes that tid 0 is close to tid and so on.
|
|
Packit Service |
c5cf8c |
struct leaf ** leaf_nodes __attribute__((aligned(ZM_CACHELINE_SIZE)));
|
|
Packit Service |
c5cf8c |
hwloc_topology_t topo;
|
|
Packit Service |
c5cf8c |
int levels;
|
|
Packit Service |
c5cf8c |
};
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static zm_thread_local int tid = -1;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
/* TODO: automate hardware topology detection
|
|
Packit Service |
c5cf8c |
* instead of the below hard-coded method */
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
/* Check the actual affinity mask assigned to the thread */
|
|
Packit Service |
c5cf8c |
static void check_affinity(hwloc_topology_t topo) {
|
|
Packit Service |
c5cf8c |
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
|
|
Packit Service |
c5cf8c |
int set_length;
|
|
Packit Service |
c5cf8c |
hwloc_get_cpubind(topo, cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
set_length = hwloc_get_nbobjs_inside_cpuset_by_type(topo, cpuset, HWLOC_OBJ_PU);
|
|
Packit Service |
c5cf8c |
hwloc_bitmap_free(cpuset);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if(set_length != 1) {
|
|
Packit Service |
c5cf8c |
printf("IZEM:HMCS:ERROR: thread bound to more than one HW thread!\n");
|
|
Packit Service |
c5cf8c |
exit(EXIT_FAILURE);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void reuse_qnode(zm_mcs_qnode_t *I){
|
|
Packit Service |
c5cf8c |
STORE(&I->status, WAIT);
|
|
Packit Service |
c5cf8c |
STORE(&I->next, ZM_NULL);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void* new_hnode() {
|
|
Packit Service |
c5cf8c |
int err;
|
|
Packit Service |
c5cf8c |
void *storage;
|
|
Packit Service |
c5cf8c |
err = posix_memalign(&storage, ZM_CACHELINE_SIZE, sizeof(struct hnode));
|
|
Packit Service |
c5cf8c |
if (err != 0) {
|
|
Packit Service |
c5cf8c |
printf("posix_memalign failed in HMCS : new_hnode \n");
|
|
Packit Service |
c5cf8c |
exit(EXIT_FAILURE);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
return storage;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
/* TODO: Macro or Template this for fast comprison */
|
|
Packit Service |
c5cf8c |
static inline unsigned get_threshold(struct hnode *L) {
|
|
Packit Service |
c5cf8c |
return L->threshold;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void normal_mcs_release_with_value(struct hnode * L, zm_mcs_qnode_t *I, unsigned val){
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t *succ = (zm_mcs_qnode_t *)LOAD(&I->next);
|
|
Packit Service |
c5cf8c |
if(succ) {
|
|
Packit Service |
c5cf8c |
STORE(&succ->status, val);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t *tmp = I;
|
|
Packit Service |
c5cf8c |
if (CAS(&(L->lock), (zm_ptr_t*)&tmp,ZM_NULL))
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
while(succ == NULL)
|
|
Packit Service |
c5cf8c |
succ = (zm_mcs_qnode_t *)LOAD(&I->next); /* SPIN */
|
|
Packit Service |
c5cf8c |
STORE(&succ->status, val);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void acquire_root(struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
// Prepare the node for use.
|
|
Packit Service |
c5cf8c |
reuse_qnode(I);
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t *pred = (zm_mcs_qnode_t*) SWAP(&(L->lock), (zm_ptr_t)I);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if(!pred) {
|
|
Packit Service |
c5cf8c |
// I am the first one at this level
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
STORE(&pred->next, I);
|
|
Packit Service |
c5cf8c |
while(LOAD(&I->status) == WAIT)
|
|
Packit Service |
c5cf8c |
; /* SPIN */
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void tryacq_root(struct hnode * L, zm_mcs_qnode_t *I, int *success) {
|
|
Packit Service |
c5cf8c |
zm_ptr_t expected = ZM_NULL;
|
|
Packit Service |
c5cf8c |
// Prepare the node for use.
|
|
Packit Service |
c5cf8c |
reuse_qnode(I);
|
|
Packit Service |
c5cf8c |
*success = CAS(&(L->lock), &expected, (zm_ptr_t)I);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void release_root(struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
// Top level release is usual MCS
|
|
Packit Service |
c5cf8c |
// At the top level MCS we always writr COHORT_START since
|
|
Packit Service |
c5cf8c |
// 1. It will release the lock
|
|
Packit Service |
c5cf8c |
// 2. Will never grow large
|
|
Packit Service |
c5cf8c |
// 3. Avoids a read from I->status
|
|
Packit Service |
c5cf8c |
normal_mcs_release_with_value(L, I, COHORT_START);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline int nowaiters_root(struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
return (LOAD(&I->next) == ZM_NULL);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void acquire_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
// Trivial case = root level
|
|
Packit Service |
c5cf8c |
if (level == 1)
|
|
Packit Service |
c5cf8c |
acquire_root(L, I);
|
|
Packit Service |
c5cf8c |
else {
|
|
Packit Service |
c5cf8c |
// Prepare the node for use.
|
|
Packit Service |
c5cf8c |
reuse_qnode(I);
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t* pred = (zm_mcs_qnode_t*)SWAP(&(L->lock), (zm_ptr_t)I);
|
|
Packit Service |
c5cf8c |
if(!pred) {
|
|
Packit Service |
c5cf8c |
// I am the first one at this level
|
|
Packit Service |
c5cf8c |
// begining of cohort
|
|
Packit Service |
c5cf8c |
STORE(&I->status, COHORT_START);
|
|
Packit Service |
c5cf8c |
// acquire at next level if not at the top level
|
|
Packit Service |
c5cf8c |
acquire_helper(level - 1, L->parent, &(L->node));
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
} else {
|
|
Packit Service |
c5cf8c |
STORE(&pred->next, I);
|
|
Packit Service |
c5cf8c |
for(;;){
|
|
Packit Service |
c5cf8c |
unsigned myStatus = LOAD(&I->status);
|
|
Packit Service |
c5cf8c |
if(myStatus < ACQUIRE_PARENT) {
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
if(myStatus == ACQUIRE_PARENT) {
|
|
Packit Service |
c5cf8c |
// beginning of cohort
|
|
Packit Service |
c5cf8c |
STORE(&I->status, COHORT_START);
|
|
Packit Service |
c5cf8c |
// This means this level is acquired and we can start the next level
|
|
Packit Service |
c5cf8c |
acquire_helper(level - 1, L->parent, &(L->node));
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
// spin back; (I->status == WAIT)
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void release_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
// Trivial case = root level
|
|
Packit Service |
c5cf8c |
if (level == 1) {
|
|
Packit Service |
c5cf8c |
release_root(L, I);
|
|
Packit Service |
c5cf8c |
} else {
|
|
Packit Service |
c5cf8c |
unsigned cur_count = LOAD(&(I->status)) ;
|
|
Packit Service |
c5cf8c |
zm_mcs_qnode_t * succ;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
// Lower level releases
|
|
Packit Service |
c5cf8c |
if(cur_count == get_threshold(L)) {
|
|
Packit Service |
c5cf8c |
// NO KNOWN SUCCESSORS / DESCENDENTS
|
|
Packit Service |
c5cf8c |
// reached threshold and have next level
|
|
Packit Service |
c5cf8c |
// release to next level
|
|
Packit Service |
c5cf8c |
release_helper(level - 1, L->parent, &(L->node));
|
|
Packit Service |
c5cf8c |
//COMMIT_ALL_WRITES();
|
|
Packit Service |
c5cf8c |
// Tap successor at this level and ask to spin acquire next level lock
|
|
Packit Service |
c5cf8c |
normal_mcs_release_with_value(L, I, ACQUIRE_PARENT);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
succ = (zm_mcs_qnode_t*)LOAD(&(I->next));
|
|
Packit Service |
c5cf8c |
// Not reached threshold
|
|
Packit Service |
c5cf8c |
if(succ) {
|
|
Packit Service |
c5cf8c |
STORE(&succ->status, cur_count + 1);
|
|
Packit Service |
c5cf8c |
return; // released
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
// No known successor, so release
|
|
Packit Service |
c5cf8c |
release_helper(level - 1, L->parent, &(L->node));
|
|
Packit Service |
c5cf8c |
// Tap successor at this level and ask to spin acquire next level lock
|
|
Packit Service |
c5cf8c |
normal_mcs_release_with_value(L, I, ACQUIRE_PARENT);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline int nowaiters_helper(int level, struct hnode * L, zm_mcs_qnode_t *I) {
|
|
Packit Service |
c5cf8c |
if (level == 1 ) {
|
|
Packit Service |
c5cf8c |
return nowaiters_root(L,I);
|
|
Packit Service |
c5cf8c |
} else {
|
|
Packit Service |
c5cf8c |
if(LOAD(&I->next) != ZM_NULL)
|
|
Packit Service |
c5cf8c |
return FALSE;
|
|
Packit Service |
c5cf8c |
else
|
|
Packit Service |
c5cf8c |
return nowaiters_helper(level - 1, L->parent, &(L->node));
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void* new_leaf(struct hnode *h, int depth) {
|
|
Packit Service |
c5cf8c |
int err;
|
|
Packit Service |
c5cf8c |
struct leaf *leaf;
|
|
Packit Service |
c5cf8c |
err = posix_memalign((void **) &leaf, ZM_CACHELINE_SIZE, sizeof(struct leaf));
|
|
Packit Service |
c5cf8c |
if (err != 0) {
|
|
Packit Service |
c5cf8c |
printf("posix_memalign failed in HMCS : new_leaf \n");
|
|
Packit Service |
c5cf8c |
exit(EXIT_FAILURE);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
leaf->cur_node = h;
|
|
Packit Service |
c5cf8c |
leaf->curDepth = depth;
|
|
Packit Service |
c5cf8c |
leaf->took_fast_path = FALSE;
|
|
Packit Service |
c5cf8c |
struct hnode *tmp, *root_node;
|
|
Packit Service |
c5cf8c |
for(tmp = leaf->cur_node; tmp->parent != NULL; tmp = tmp->parent);
|
|
Packit Service |
c5cf8c |
root_node = tmp;
|
|
Packit Service |
c5cf8c |
leaf->root_node = root_node;
|
|
Packit Service |
c5cf8c |
return leaf;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void acquire_from_leaf(int level, struct leaf *L){
|
|
Packit Service |
c5cf8c |
if((zm_ptr_t)L->cur_node->lock == ZM_NULL
|
|
Packit Service |
c5cf8c |
&& (zm_ptr_t)L->root_node->lock == ZM_NULL) {
|
|
Packit Service |
c5cf8c |
// go FP
|
|
Packit Service |
c5cf8c |
L->took_fast_path = TRUE;
|
|
Packit Service |
c5cf8c |
acquire_root(L->root_node, &L->I);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
acquire_helper(level, L->cur_node, &L->I);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void tryacq_from_leaf(int level, struct leaf *L, int *success){
|
|
Packit Service |
c5cf8c |
*success = 0;
|
|
Packit Service |
c5cf8c |
if((zm_ptr_t)L->cur_node->lock == ZM_NULL
|
|
Packit Service |
c5cf8c |
&& (zm_ptr_t)L->root_node->lock == ZM_NULL) {
|
|
Packit Service |
c5cf8c |
tryacq_root(L->root_node, &L->I, success);
|
|
Packit Service |
c5cf8c |
if (*success)
|
|
Packit Service |
c5cf8c |
L->took_fast_path = TRUE;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void release_from_leaf(int level, struct leaf *L){
|
|
Packit Service |
c5cf8c |
//myrelease(cur_node, I);
|
|
Packit Service |
c5cf8c |
if(L->took_fast_path) {
|
|
Packit Service |
c5cf8c |
release_root(L->root_node, &L->I);
|
|
Packit Service |
c5cf8c |
L->took_fast_path = FALSE;
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
release_helper(level, L->cur_node, &L->I);
|
|
Packit Service |
c5cf8c |
return;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline int nowaiters_from_leaf(int level, struct leaf *L){
|
|
Packit Service |
c5cf8c |
// Shouldnt this be nowaiters(root_node, I)?
|
|
Packit Service |
c5cf8c |
if(L->took_fast_path) {
|
|
Packit Service |
c5cf8c |
return nowaiters_root(L->cur_node, &L->I);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
return nowaiters_helper(level, L->cur_node, &L->I);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static int get_hwthread_id(hwloc_topology_t topo){
|
|
Packit Service |
c5cf8c |
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
|
|
Packit Service |
c5cf8c |
hwloc_obj_t obj;
|
|
Packit Service |
c5cf8c |
hwloc_get_cpubind(topo, cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
obj = hwloc_get_obj_inside_cpuset_by_type(topo, cpuset, HWLOC_OBJ_PU, 0);
|
|
Packit Service |
c5cf8c |
hwloc_bitmap_free(cpuset);
|
|
Packit Service |
c5cf8c |
return obj->logical_index;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void set_hierarchy(struct lock *L, int *max_threads, int** particip_per_level) {
|
|
Packit Service |
c5cf8c |
int max_depth, levels = 0, max_levels = HMCS_DEFAULT_MAX_LEVELS, explicit_levels = 0;
|
|
Packit Service |
c5cf8c |
char tmp[20];
|
|
Packit Service |
c5cf8c |
char *s = getenv("ZM_HMCS_MAX_LEVELS");
|
|
Packit Service |
c5cf8c |
if (s != NULL)
|
|
Packit Service |
c5cf8c |
max_levels = atoi(s);
|
|
Packit Service |
c5cf8c |
int depths[max_levels];
|
|
Packit Service |
c5cf8c |
int idx = 0;
|
|
Packit Service |
c5cf8c |
/* advice to users: run `hwloc-ls -s --no-io --no-icaches` and choose
|
|
Packit Service |
c5cf8c |
* depths of interest in ascending order. The first depth must be `0' */
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
s = getenv("ZM_HMCS_EXPLICIT_LEVELS");
|
|
Packit Service |
c5cf8c |
if (s != NULL) {
|
|
Packit Service |
c5cf8c |
strcpy(tmp, s);
|
|
Packit Service |
c5cf8c |
explicit_levels = 1;
|
|
Packit Service |
c5cf8c |
char* token;
|
|
Packit Service |
c5cf8c |
token = strtok(tmp,",");
|
|
Packit Service |
c5cf8c |
while(token != NULL) {
|
|
Packit Service |
c5cf8c |
depths[idx] = atoi(token);
|
|
Packit Service |
c5cf8c |
if (idx == 0)
|
|
Packit Service |
c5cf8c |
assert(depths[idx] == 0 && "the first depth must be machine level (i.e., depth 0), run `hwloc-ls -s --no-io --no-icaches` and choose appropriate depth values");
|
|
Packit Service |
c5cf8c |
idx++;
|
|
Packit Service |
c5cf8c |
token = strtok(NULL,",");
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
assert(idx == max_levels);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
hwloc_topology_init(&L->topo);
|
|
Packit Service |
c5cf8c |
hwloc_topology_load(L->topo);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
*max_threads = hwloc_get_nbobjs_by_type(L->topo, HWLOC_OBJ_PU);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
max_depth = hwloc_topology_get_depth(L->topo);
|
|
Packit Service |
c5cf8c |
assert(max_depth >= 2); /* At least Machine and Core levels exist */
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
*particip_per_level = (int*) malloc(max_levels * sizeof(int));
|
|
Packit Service |
c5cf8c |
int prev_nobjs = -1;
|
|
Packit Service |
c5cf8c |
if(!explicit_levels) {
|
|
Packit Service |
c5cf8c |
for (int d = max_depth - 2; d > 1; d--) {
|
|
Packit Service |
c5cf8c |
int cur_nobjs = hwloc_get_nbobjs_by_depth(L->topo, d);
|
|
Packit Service |
c5cf8c |
/* Check if this level has a hierarchical impact */
|
|
Packit Service |
c5cf8c |
if(cur_nobjs != prev_nobjs) {
|
|
Packit Service |
c5cf8c |
prev_nobjs = cur_nobjs;
|
|
Packit Service |
c5cf8c |
(*particip_per_level)[levels] = (*max_threads)/cur_nobjs;
|
|
Packit Service |
c5cf8c |
levels++;
|
|
Packit Service |
c5cf8c |
if(levels >= max_levels - 1)
|
|
Packit Service |
c5cf8c |
break;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
(*particip_per_level)[levels] = *max_threads;
|
|
Packit Service |
c5cf8c |
levels++;
|
|
Packit Service |
c5cf8c |
} else {
|
|
Packit Service |
c5cf8c |
for(int i = max_levels - 1; i >= 0; i--){
|
|
Packit Service |
c5cf8c |
int d = depths[i];
|
|
Packit Service |
c5cf8c |
int cur_nobjs = hwloc_get_nbobjs_by_depth(L->topo, d);
|
|
Packit Service |
c5cf8c |
/* Check if this level has a hierarchical impact */
|
|
Packit Service |
c5cf8c |
if(cur_nobjs != prev_nobjs) {
|
|
Packit Service |
c5cf8c |
prev_nobjs = cur_nobjs;
|
|
Packit Service |
c5cf8c |
(*particip_per_level)[levels] = (*max_threads)/cur_nobjs;
|
|
Packit Service |
c5cf8c |
levels++;
|
|
Packit Service |
c5cf8c |
} else {
|
|
Packit Service |
c5cf8c |
assert(0 && "plz choose levels that have a hierarchical impact");
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
L->levels = levels;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void free_hierarchy(int* particip_per_level){
|
|
Packit Service |
c5cf8c |
free(particip_per_level);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void* new_lock(){
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
struct lock *L;
|
|
Packit Service |
c5cf8c |
posix_memalign((void **) &L, ZM_CACHELINE_SIZE, sizeof(struct lock));
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int max_threads;
|
|
Packit Service |
c5cf8c |
int *participants_at_level;
|
|
Packit Service |
c5cf8c |
set_hierarchy(L, &max_threads, &participants_at_level);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
// Total locks needed = participantsPerLevel[1] + participantsPerLevel[2] + .. participantsPerLevel[levels-1] + 1
|
|
Packit Service |
c5cf8c |
// Save affinity
|
|
Packit Service |
c5cf8c |
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
|
|
Packit Service |
c5cf8c |
hwloc_get_cpubind(L->topo, cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int total_locks_needed = 0;
|
|
Packit Service |
c5cf8c |
int levels = L->levels;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
for (int i=0; i < levels; i++) {
|
|
Packit Service |
c5cf8c |
total_locks_needed += max_threads / participants_at_level[i] ;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
struct hnode ** lock_locations;
|
|
Packit Service |
c5cf8c |
posix_memalign((void **) &lock_locations, ZM_CACHELINE_SIZE, sizeof(struct hnode*) * total_locks_needed);
|
|
Packit Service |
c5cf8c |
struct leaf ** leaf_nodes;
|
|
Packit Service |
c5cf8c |
posix_memalign((void **) &leaf_nodes, ZM_CACHELINE_SIZE, sizeof(struct leaf*) * max_threads);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int threshold = DEFAULT_THRESHOLD;
|
|
Packit Service |
c5cf8c |
char *s = getenv("ZM_HMCS_THRESHOLD");
|
|
Packit Service |
c5cf8c |
if (s != NULL)
|
|
Packit Service |
c5cf8c |
threshold = atoi(s);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
hwloc_obj_t obj;
|
|
Packit Service |
c5cf8c |
for(int tid = 0 ; tid < max_threads; tid ++){
|
|
Packit Service |
c5cf8c |
obj = hwloc_get_obj_by_type (L->topo, HWLOC_OBJ_PU, tid);
|
|
Packit Service |
c5cf8c |
hwloc_set_cpubind(L->topo, obj->cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
// Pin me to hw-thread-id = tid
|
|
Packit Service |
c5cf8c |
int last_lock_location_end = 0;
|
|
Packit Service |
c5cf8c |
for(int cur_level = 0 ; cur_level < levels; cur_level++){
|
|
Packit Service |
c5cf8c |
if (tid%participants_at_level[cur_level] == 0) {
|
|
Packit Service |
c5cf8c |
// master, initialize the lock
|
|
Packit Service |
c5cf8c |
int lock_location = last_lock_location_end + tid/participants_at_level[cur_level];
|
|
Packit Service |
c5cf8c |
last_lock_location_end += max_threads/participants_at_level[cur_level];
|
|
Packit Service |
c5cf8c |
struct hnode * cur_hnode = new_hnode();
|
|
Packit Service |
c5cf8c |
cur_hnode->threshold = threshold;
|
|
Packit Service |
c5cf8c |
cur_hnode->parent = NULL;
|
|
Packit Service |
c5cf8c |
cur_hnode->lock = ZM_NULL;
|
|
Packit Service |
c5cf8c |
lock_locations[lock_location] = cur_hnode;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
// setup parents
|
|
Packit Service |
c5cf8c |
for(int tid = 0 ; tid < max_threads; tid ++){
|
|
Packit Service |
c5cf8c |
obj = hwloc_get_obj_by_type (L->topo, HWLOC_OBJ_PU, tid);
|
|
Packit Service |
c5cf8c |
hwloc_set_cpubind(L->topo, obj->cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
int last_lock_location_end = 0;
|
|
Packit Service |
c5cf8c |
for(int cur_level = 0 ; cur_level < levels - 1; cur_level++){
|
|
Packit Service |
c5cf8c |
if (tid%participants_at_level[cur_level] == 0) {
|
|
Packit Service |
c5cf8c |
int lock_location = last_lock_location_end + tid/participants_at_level[cur_level];
|
|
Packit Service |
c5cf8c |
last_lock_location_end += max_threads/participants_at_level[cur_level];
|
|
Packit Service |
c5cf8c |
int parentLockLocation = last_lock_location_end + tid/participants_at_level[cur_level+1];
|
|
Packit Service |
c5cf8c |
lock_locations[lock_location]->parent = lock_locations[parentLockLocation];
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
leaf_nodes[tid] = (struct leaf*)new_leaf(lock_locations[tid/participants_at_level[0]], levels);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
free(lock_locations);
|
|
Packit Service |
c5cf8c |
free_hierarchy(participants_at_level);
|
|
Packit Service |
c5cf8c |
// Restore affinity
|
|
Packit Service |
c5cf8c |
hwloc_set_cpubind(L->topo, cpuset, HWLOC_CPUBIND_THREAD);
|
|
Packit Service |
c5cf8c |
L->leaf_nodes = leaf_nodes;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
hwloc_bitmap_free(cpuset);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
return L;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void search_nodes_rec(struct hnode *node, struct hnode **nodes_to_free, int *num_ptrs, int max_threads) {
|
|
Packit Service |
c5cf8c |
int i;
|
|
Packit Service |
c5cf8c |
if(node != NULL) {
|
|
Packit Service |
c5cf8c |
for(i = 0; i < *num_ptrs; i++) {
|
|
Packit Service |
c5cf8c |
if(node == nodes_to_free[i])
|
|
Packit Service |
c5cf8c |
break; /* already marked to be free'd */
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
if(i == *num_ptrs) { /* newly encountered pointer */
|
|
Packit Service |
c5cf8c |
nodes_to_free[*num_ptrs] = node;
|
|
Packit Service |
c5cf8c |
(*num_ptrs)++;
|
|
Packit Service |
c5cf8c |
assert(*num_ptrs < 2*max_threads);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
search_nodes_rec(node->parent, nodes_to_free, num_ptrs, max_threads);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static void free_lock(struct lock* L) {
|
|
Packit Service |
c5cf8c |
int max_threads = hwloc_get_nbobjs_by_type(L->topo, HWLOC_OBJ_PU);
|
|
Packit Service |
c5cf8c |
int num_ptrs = 0;
|
|
Packit Service |
c5cf8c |
struct hnode **nodes_to_free = (struct hnode**) malloc(2*max_threads*sizeof(struct hnode*));
|
|
Packit Service |
c5cf8c |
for (int tid = 0; tid < max_threads; tid++) {
|
|
Packit Service |
c5cf8c |
search_nodes_rec(L->leaf_nodes[tid]->cur_node, nodes_to_free, &num_ptrs, max_threads);
|
|
Packit Service |
c5cf8c |
free(L->leaf_nodes[tid]);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
free(L->leaf_nodes);
|
|
Packit Service |
c5cf8c |
for(int i = 0; i < num_ptrs; i++)
|
|
Packit Service |
c5cf8c |
free(nodes_to_free[i]);
|
|
Packit Service |
c5cf8c |
free(nodes_to_free);
|
|
Packit Service |
c5cf8c |
hwloc_topology_destroy(L->topo);
|
|
Packit Service |
c5cf8c |
free(L);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void hmcs_acquire(struct lock *L){
|
|
Packit Service |
c5cf8c |
if (zm_unlikely(tid == -1)) {
|
|
Packit Service |
c5cf8c |
check_affinity(L->topo);
|
|
Packit Service |
c5cf8c |
tid = get_hwthread_id(L->topo);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
acquire_from_leaf(L->levels, L->leaf_nodes[tid]);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void hmcs_tryacq(struct lock *L, int *success){
|
|
Packit Service |
c5cf8c |
if (zm_unlikely(tid == -1)) {
|
|
Packit Service |
c5cf8c |
check_affinity(L->topo);
|
|
Packit Service |
c5cf8c |
tid = get_hwthread_id(L->topo);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
tryacq_from_leaf(L->levels, L->leaf_nodes[tid], success);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline void hmcs_release(struct lock *L){
|
|
Packit Service |
c5cf8c |
release_from_leaf(L->levels, L->leaf_nodes[tid]);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
static inline int hmcs_nowaiters(struct lock *L){
|
|
Packit Service |
c5cf8c |
return nowaiters_from_leaf(L->levels, L->leaf_nodes[tid]);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int zm_hmcs_init(zm_hmcs_t * handle) {
|
|
Packit Service |
c5cf8c |
void *p = new_lock();
|
|
Packit Service |
c5cf8c |
*handle = (zm_hmcs_t) p;
|
|
Packit Service |
c5cf8c |
return 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int zm_hmcs_destroy(zm_hmcs_t *L) {
|
|
Packit Service |
c5cf8c |
free_lock((struct lock*)(*L));
|
|
Packit Service |
c5cf8c |
return 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int zm_hmcs_acquire(zm_hmcs_t L){
|
|
Packit Service |
c5cf8c |
hmcs_acquire((struct lock*)L);
|
|
Packit Service |
c5cf8c |
return 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
int zm_hmcs_tryacq(zm_hmcs_t L, int *success){
|
|
Packit Service |
c5cf8c |
hmcs_tryacq((struct lock*)L, success);
|
|
Packit Service |
c5cf8c |
return 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
int zm_hmcs_release(zm_hmcs_t L){
|
|
Packit Service |
c5cf8c |
hmcs_release((struct lock*)L);
|
|
Packit Service |
c5cf8c |
return 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
int zm_hmcs_nowaiters(zm_hmcs_t L){
|
|
Packit Service |
c5cf8c |
return hmcs_nowaiters((struct lock*)L);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|