1 Split-Ordered Lists: Lock-Free Extensible Hash Tables Pierre LaBorde
Nir Shavit 2 Tel-Aviv University, Israel Ph.D. from Hebrew University Professor at School of Computer Science at Tel-Aviv University in 1992. 2004 Gödel Prize Winner The Art of Multiprocessor Programming
Outline 3 Hashing Concurrency Algorithm Implementation Performance
Hash Table 4 Map keys to values Map each possible key to a unique slot index Hash collisions are normal Constant average cost per operation Efficient
Hash Table 5 Name Phone Number
Collision Resolution 6 Bucket Chains sorted by the key field Disadvantages of linked lists Next pointer overhead Processor Cache
Chained Hashing 7
Extensible Hash Table 8 Treats a hash as a bit string Soft real-time Array of buckets Only increase in size
Outline 9 Hashing Concurrency Algorithm Implementation Performance
Concurrent Hash Table 10 Operations Insert Delete Find Ability to synchronize
Synchronization 11 Critical section Race condition Locking Mutex
Lock-Free 12 At least one thread will progress Wait-freedom General design problems Long delays Waiting for locks
Difficulty 13 Synchronization problems Deadlock Livelock Starvation Priority Inversion
Avoiding Locks 14 CAS LL/SC Single-word Hardware locks Fine-granularity
Resizing Problem 15 Requires moving items Atomic Other options Helping
Lock-Free Linked List 16 Validity Mark for Deletion Bit stealing Same CAS Straightforward implementation
Michael s Lock-Free LL 17 struct MarkPtrType { <mark, next>: <bool, NodeType *> }; struct NodeType { key_t key; MarkPtrType <mark, next>; }; /* thread-private variables */ MarkPtrType *prev; MarkPtrType <pmark, cur>; MarkPtrType <cmark, next>;
List: Find int list_find(nodetype **head, so_key_t key) { F1: try_again: F2: prev = head; F3: <pmark,cur> = *prev; F4: while(1) { F5: if (cur == NULL) return 0; F6: <cmark,next> = cur-><mark,next>; F7: ckey = cur->key; F8: if (*prev!= <0,cur>) F9: goto try_again; F10: if (!cmark) { F11: if (ckey >= key) F12: return ckey == key; F13: prev = &(cur-><mark,next>); } F14: else { F15: if (CAS(prev, <0,cur>, <0,next>)) F16: delete_node(cur); F17: else goto try_again; } F18: <pmark,cur> = <cmark,next>; } } 18
List: Insert 19 int list_insert(markptrtype *head, NodeType *node) { key = node->key; while (1) { if (list_find(head, key) return 0; node-><mark,next> = <0,cur>; if (CAS(prev, <0,cur>, <0,node>)) return 1; } }
List: Delete 20 int list_delete(markptrtype *head, so_key_t key) { while (1) { if (!list_find(head, key)) return 0; if (!CAS(&(cur-><mark,next>), <0,next>, <1,next>)) continue; if (CAS(prev, <0,cur>, <0,next>)) delete_node(cur); else list_find(head, key); return 1; } }
Outline 21 Hashing Concurrency Algorithm Implementation Performance
Algorithm 22 Split-ordering Avoid resizing problem "moving the buckets among the items
Resizing Revisited 23 Moving an item Ability to split sublists recursively Recursive split-ordering
Split-Ordered Hash Table 24
Split-Ordered Hash Table 25
Split-Ordered Hash Table 26
Split-Ordered Hash Table 27
Insertion 28
Insertion 29
Insertion 30
Insertion 31
Operations 32 Hash to bucket using split-ordering Follow pointer Traverse list
Split-Ordered Hash Table 33
Outline 34 Hashing Concurrency Algorithm Implementation Performance
Implementation 35 Modular design Michael s Lock-Free lists Memory management
Fetch and Increment 36 int fetch-and-inc(int *p) { do { old = *p; } while (!CAS(p, old, old+1); return old; } int fetch-and-dec(int *p) { do { old = *p; } while (!CAS(p, old, old-1); return old; }
Hash Table: Initialize Bucket 37 void initialize_bucket(uint bucket) { B1: parent = GET_PARENT(bucket); B2: if (T[parent] == UNINITIALIZED) B3: initialize_bucket(parent); B4: dummy = new node(so_dummykey(bucket)); B5: if (!list_insert(&(t[parent]), dummy)) { B6: delete dummy; B7: dummy = cur; } B8: T[bucket] = dummy; }
Hash Table: Insert 38 int insert(so_key_t key) { I1: node = new node(so_regularkey(key)); I2: bucket = key % size; I3: if (T[bucket] == UNINITIALIZED) I4: initialize_bucket(bucket); I5: if (!list_insert(&(t[bucket]), node)) { I6: delete_node(node); I7: return 0; } I8: csize = size; I9: if (fetch-and-inc(&count) / csize > MAX_LOAD) I10: CAS(&size, csize, 2 * csize); I11:return 1;}
Hash Table: Find 39 int find(so_key_t key) { S1: bucket = key % size; S2: if (T[bucket] == UNINITIALIZED) S3: initialize_bucket(bucket); S4: return list_find(&(t[bucket]), so_regularkey(key)); }
Hash Table: Delete 40 int delete(so_key_t key) { D1: bucket = key % size; D2: if (T[bucket] == UNINITIALIZED) D3: initialize_bucket(bucket); D4: if (!list_delete(&(t[bucket]), so_regularkey(key))) D5: return 0; D6: fetch-and-dec(&count); D7: return 1; }
Complexity 41 Distribution of keys Scheduling of threads
Outline 42 Hashing Concurrency Algorithm Implementation Performance
Throughput 43
Varying Preinsertions 44
Conclusion 45 Robustness with a non-uniform hash function Performance loss Low-load non-multiprogrammed Medium to high load
Bibliography 46 Split-Ordered Lists: Lock-Free Extensible Hash Tables The Art of Multiprocessor Programming Wikipedia
47 Split-Ordered Lists: Lock-Free Extensible Hash Tables Pierre LaBorde
48
Extendible Hashing for Concurrent Operations and Distributed Data 49
After Splitting 10 b bucket 50
Locks 51
52
53
54
55
56