Update analysis

This commit is contained in:
Patrick Pelissier committed 2018-12-11 23:19:33 +01:00
1 parent 2416c2e466
commit 61a1e15103
1 file changed
+53 -30
+53 -30
View File
@@ -106,46 +106,30 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account. Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be taken into account.
** Proposed internal API: ** First step: lock free node pool :
*** lock free node pool : *** API based on Lock-Free Queue List
-- node_t is { node_t *next; uint ticket; data_t } -- node_t is { node_t *next; uint ticket; data_t }
-- freelist_t : basic internal lockfree list used to construct more user friendly list (FIFO queue to avoid putting constraints on free & alloc at the same times + it is needed for reclaim part). -- freelist_t : basic internal lockfree list used to construct more user friendly list (FIFO queue to avoid putting constraints on free & alloc at the same times + it is needed for reclaim part).
do not ensure consistensy of data. do not ensure consistensy of data.
is { _Atomic(node*) head, tail; } is { _Atomic(node*) head, tail; }
+ freelist_push (&list, &node) is { Atomic Queue List : See algorithm by Michael & Scott on Lock Free Queue List (aka MSList)
node.next := NULL + freelist_push (&list, &node)
old_head := node + freelist_pop(&list) return node
atomic_xchg(&list.head, &old_head) + freelist_pop_if(&list, ticket) return node
if (old_head != NULL) Needs minor adaptation on _pop.
atomic_store(&old_head.next, node);
else
CAS(&old.tail, &old_head, node)
}
+ freelist_pop(&list) return node is {
tail := atomic_load(&list.tail);
if (tail == NULL) return NULL;
do {
node := tail;
} while (!CAS(&list.tail, &tail, node.next))
if (list.tail == NULL)
CAS(&list.head, &node, NULL);
return node
}
(Something seems off when correcting other entries - I feel some concurrency issues. Maybe a sentinell will be better ? Is it possible ?)
+ empty()
-- lf_memory_core_t : basic type responsible for the centralization of the list allocation. -- lf_memory_core_t : basic type responsible for the centralization of the list allocation.
+ init(lf_mc_t &, int max_thread, size_t alloc_size) + init(lf_mc_t &, int max_thread, size_t alloc_size)
+ clear(lf_mc_t &) + clear(lf_mc_t &)
+ lc_mc_t is { + lc_mc_t is {
atomic_uint ticket; atomic_uint ticket;
atomic_uint reg_thread; // genint instead ? atomic_uint reg_thread; // ==> genint instead
atomic_uint tab_ticket[MAX_THREAD]; atomic_uint tab_ticket[MAX_THREAD];
freelist_t free; freelist_t free[MAX_THREAD];
freelist_t to_be_reclaimed; freelist_t to_be_reclaimed[MAX_THREAD];
} }
-- lf_memory_thread_t: specialization for the current thread -- lf_memory_thread_t: specialization for the current thread
@@ -160,7 +144,46 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
pop the node from to_be_reclaimed and push it to the list free pop the node from to_be_reclaimed and push it to the list free
lf_memory_thread_t shall be in awake state to do anything on the list. lf_memory_thread_t shall be in awake state to do anything on the list.
*** lf_list_t is a stack based list based on RCU: *** Bench
Basic malloc / free: 9.6s
LF Node : 10.6s
Even if LF Node ensures a correct life cycle of the node for the read, the performance is passable.
Too much CAS:
51.40 6.13 6.13 51146587 0.00 0.00 lf_freelist_push
20.10 8.53 2.40 28143911 0.00 0.00 lf_freelist_pop
15.65 10.40 1.87 253579 0.01 0.02 lf_mempool_sleep
*** Mixed API
To overcome this limitation performs a mixed approache:
- having a simple (non atomic) list of nodes to alloc quickly the nodes.
- embedded within an atomic list of list of nodes for GC.
Thread data will be:
- FreeList is a non atomic singly linked list.
- ToBeReclaimedList is the same
- FreeListList is an atomic queue list of non atomic singly list.
- CanBeReclaimedListList is an atomic queue list of non atomic singly list
**** alloc() returns node:
- Pop from FreeList a node.
- If failure, atomic pop from FreeListList a list of list of free nodes. If success, update FreeList with the new list and pop a node from it.
- If failure, try an atomic from the FreeListList of other threads. If success, update FreeList with the new list and pop a node from it.
- If failure, malloc a new pool of FreeListList (TBD: all nodes in one pass? Needs a separate list to handle final free of nodes).
*** free(node)
- Push node in ToBeReclaimedList
*** GarbageCollector called on sleep:
- Compute min_ticket for all threads
- Atomic push of ToBeReclaimedList in CanBeReclaimedListList
- for each thread starting from mine:
-- Atomic pop from CanBeReclaimedListList[] if max ticket of pool of nodes < min_ticket and atomic push in FreeListList
max ticket of pool is the ticket of the top node of the list.
==> Should be much faster since there is much less CAS operations performed and GC is perfomed per block.
** Second step: lf_list_t is a stack based list based on RCU:
_read: classic non atomic operations. _read: classic non atomic operations.
@@ -171,7 +194,7 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
do do
node->next := head node->next := head
while !CAS(&list.head, &head, node) while !CAS(&list.head, &head, node)
# ABA problem shall not happen since node cannot be reused immeditalely # ABA problem shall not happen since node cannot be reused immediately
_pop(list, data, lf_mt_t) is { _pop(list, data, lf_mt_t) is {
head := atomic_load(&list.head) head := atomic_load(&list.head)
@@ -186,7 +209,7 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
_erase() TODO _erase() TODO
_update() TODO _update() TODO
*** Way to use: ** Example / Way to use:
- init global memory handler. - init global memory handler.
- for each thread, - for each thread,
@@ -198,7 +221,7 @@ http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Co
-- ... -- ...
-- XX.sleep() # Memory can be reclaimed -- XX.sleep() # Memory can be reclaimed
-- XX.awake() -- XX.awake()
* TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT: * TODO #15: Constructor / Destructor Prologue / Epilogue for Stack Exception Handling :ENHANCEMENT: