---
# System prepended metadata

title: 研究計劃書
tags: [報告]

---


### Topic : Nonblocking open addressing hash table with rebuild operation
題目來自 Future work of [DHASH: Dynamic Hash Tables With Non-Blocking Regular Operations](https://ieeexplore.ieee.org/document/9714033)
## DHASH
* 討論 hash table 的 robust 議題 (i.e. change pre-defined hash functions when they can not evenly distribute incoming data to the hash table buckets).
* Most existing hash tables, including resizable ones, are unable to solve this problem effectively.
* 設計 rebuild operation that  is lightweight and does not noticeably degrade the performance of concurrent regular operations.
* regular operations are non-blocking, while the rebuild operation is blocking.




## Chaining vs. Open addressing
不同的 key 對應到相同 bucket 稱為 collision.
![](https://hackmd.io/_uploads/S1rXW3Xqn.png)
### Chaining
![](https://hackmd.io/_uploads/HJemG2m52.png)
### Open addressing
根據 probing 策略選擇下一個 bucket
#### insert 0, 8, 16, 24 (mod 8)
```graphviz
digraph ele_list {
    rankdir= LR
    node[shape=record]    
    ht1 [label="<0> 0 |<4> 8 |<8> 16 |<12> 24|- |- |- |-", style="bold", height=2]
    ht1:0 -> ht1:4 [headlabel="collision!           ", labeldistance=4]
    ht1:4 -> ht1:8 [headlabel="collision!            ", labeldistance=4]
    ht1:8 -> ht1:12 [headlabel="collision!           ", labeldistance=4]
}
```
* probe sequence : probe 所經的 bucket 稱為 probe sequence
* 若 probe sequence 很長 lookup time complexity : constant time -> O(n)


## 研究目標
* open addressing hash table with rebuild operation
* nonblocking regular operation
* prove linearizability, nonblocking
## Extensions
* nonblocking rebuild operation
* multi-word keys
    * key 長度超過 one word 無法使用一個 atomic load, atomic store, CAS 存取

## How to make rebuild operation nonblocking
#### DHASH's rebuild operation
```clike
21: void ht_rebuild(ht *htp, nbuckets, hash) {
22:     if ( trylock(rebuild_lock) != SUCCESS ) return -EBUSY;
23:     if ( ! rebuild_is_required() ) return -EPERM;
24:     htp_new := ht_alloc(htp, nbuckets, hash);
25:     htp->ht_new := htp_new;
        /* Wait for operations not aware of htp_new . */
26:     synchronize_rcu();
27:     for each bucket htbp in htp {
28:         for each node htnp in htbp {...
```
### DHASH's insert operation
```clike
90: int ht_insert(ht *htp, long key) {
91:     retry:
92:     htnp := allocate_node(key);
93:     htp_new := htp->ht_new;
94:     if htp_new = NULL{
            /* IO(k) */
            /* insert node to old hash table */
 99:    }
 100:   else{
            /* IN(k) */
            /* insert node to new hash table */
 105:   }...
       
```
### Solution 1
```clike
90: int ht_insert(ht *htp, long key) {
91:     retry:
92:     htnp := allocate_node(key);
93:     htp_new := htp->ht_new;
94:     if htp_new = NULL{
            /* IO(k) */
            /* insert node to old hash table */
            if(rebuild operation started before insert finish)
                goto retry;
 99:    }
 100:   else{
            /* IN(k) */
            /* insert node to new hash table */
 105:   }...
```
### Solution 2
```clike
21: void ht_rebuild(ht *htp, nbuckets, hash) {
22:     if ( trylock(rebuild_lock) != SUCCESS ) return -EBUSY;
23:     if ( ! rebuild_is_required() ) return -EPERM;
24:     htp_new := ht_alloc(htp, nbuckets, hash);
25:     htp->ht_new := htp_new;
        /* Wait for operations not aware of htp_new . */
26:     Assist();
27:     for each bucket htbp in htp {
28:         for each node htnp in htbp {...
```
---
### Bounding searches
![](https://hackmd.io/_uploads/B1qiPivqh.png)
#### insert 需確認是否增加 bound

```clike
void ConditionallyRaiseBound(int h, int index): // Ensure maximum ≥ index
    do
        <old bound,scanning> := bounds[h]
        new bound := max(old bound,index)
    while ¬CAS(&bounds[h],<old bound,scanning>,<new bound,false>)
```
#### delete 需確認是否減少 bound
```clike
void ConditionallyLowerBound(int h, int index): // Allow maximum < index
    <bound,scanning> := bounds[h]
    if scanning = true
        CAS(&bounds[h],<bound,true>,<bound,false>)
    if index > 0 // If maximum = index > 0, set maximum < index
        while CAS(&bounds[h],<index,false>,<index,true>)
            i := index-1 // Scanning phase: scan cells for new maximum
            while i > 0 ∧ ¬DoesBucketContainCollision(h, i)
                i--
            CAS(&bounds[h],<index,true>,<i,false>)
```
### Insertion of open addressing
* 檢查是否有相同的 key 在 hash table 中
* 將 key insert 進 hash table

困難的地方在於如何確保這兩個動作是 atomic.
#### obstruction-free insert
二個步驟:
* Find an empty slot in the probe sequence and insert the new key into that slot.
* Check if there is same key in the hash table.
    * 若遇到相同的 key 且狀態為 `inserting` : 將該 bucket 狀態改為 `busy`
    * 若遇到相同的 key 且狀態為 `member` : 放棄 insert 並 return false
```clike
bool Insert(Key k) // Insert k into the set if it is not a member
    int h = Hash(k);
    int i = 0; // Reserve a cell
    while (!CAS(Bucket(h, i), <-, empty>, <k, busy>))
        i++;
        if (i >= size)
            throw "Table full";
    
    // Attempt to insert a unique copy of k
    do
        *Bucket(h, i) = <k, inserting>;
        ConditionallyRaiseBound(h, i);
        
        int max = GetProbeBound(h); // Scan through the probe sequence
        for (int j = 0; j < max; j++)
            if (j != i)
                if (*Bucket(h, j) == <k, inserting>) // Stall concurrent inserts
                    CAS(Bucket(h, j), <k, inserting>, <k, busy>);
                if (*Bucket(h, j) == <k, member>) // Abort if k already a member
                    *Bucket(h, i) = <k, busy>;       
                    ConditionallyLowerBound(h, i);
                    *Bucket(h, i) = <k, empty>;
                    return false;
    while (!CAS(Bucket(h, i), <k, inserting>, <k, member>));   
    return true;
```
> 若 inserts 互相 stall 會造成 live-lock，因此上述 insert 為 obstruction-free.
obstruction-free 的定義為:
> * At any point, a single thread executed in isolation for a bounded number of steps will complete its operation.
> * 與 lock-free 的差別為是否會造成 live-lock

#### lock-free insert
若遇到 concurrent insert 相同的 key，則在 probe sequence 排序較後的 insert 放棄，排序較前的 insert 成功.
排序較後的 insert 須等待 (blocking) 或幫助 (nonblocking) 排序較前的 insert 完成才能 return 否則會違反 linearizability.
```clike
bool Insert(Key k): // Insert k into the set if it is not a member
    h := Hash(k)
    i := -1 // Reserve a cell
    do
        if ++i ≥ size
            throw "Table full"
        <version, state> := Bucket(h, i) → vs
    while ¬CAS(&Bucket(h, i) → vs, <version, empty>, <version, busy>)
    Bucket(h, i) → key := k
    while true // Attempt to insert a unique copy of k
        *Bucket(h, i) → vs := <version, visible>
        ConditionallyRaiseBound(h, i)
        *Bucket(h, i) → vs := <version, inserting>
        r := Assist(k, h, i, version)
        if Bucket(h, i) → vs != <version, collided>
            return true
        if ¬r
            ConditionallyLowerBound(h, i)
            Bucket(h, i) → vs := <version + 1, empty>
            return false
        version++
```
```clike
bool Assist(Key k, int h, int i, int veri): // Attempt to insert k at i
    max := GetProbeBound(h) // Scan through probe sequence
    for j := 0 .. max
        if i != j
            <ver_j, state_j> := Bucket(h, j) → vs
            if statej = inserting ∧ Bucket(h, j) → key = k
                if j < i // Assist any insert found earlier in the probe sequence
                    if Bucket(h, j) → vs = <ver_j, inserting>
                        CAS(&Bucket(h, i) → vs, <ver_i, inserting>, <ver_i, collided>)
                        return Assist(k, h, j, ver_j)
                else // Fail any insert found later in the probe sequence
                    if Bucket(h, i) → vs = <ver_i, inserting>
                        CAS(&Bucket(h, j) → vs, <ver_j, inserting>, <ver_j, collided>)
            <ver_j, state_j> := Bucket(h, j) → vs // Abort if k already a member
            if state_j = member ∧ Bucket(h, j) → key = k
                if Bucket(h, j) → vs = <ver_j, member>
                    CAS(&Bucket(h, i) → vs, <ver_i, inserting>, <ver_i, collided>)
                    return false
    CAS(&Bucket(h, i), <ver_i, inserting>, <ver_i, member>)
    return true
```
---
:::spoiler {state="open"} Reference
[DHash: Dynamic Hash Tables With Non-Blocking Regular Operations](https://ieeexplore.ieee.org/document/9714033)
[Lock-free Dynamic Hash Tables with Open Addressing](https://arxiv.org/abs/cs/0303011)
[Concurrent Hash Tables: Fast and General(?)!](https://arxiv.org/abs/1601.04017)
[Non-blocking hashtables with open addressing](https://timharris.uk/papers/2005-hashtable.pdf)
[Dynamic-Sized Nonblocking Hash Tables](https://dl.acm.org/doi/10.1145/2611462.2611495)
:::

