USENIX 13th (OSDI '18)
Pengfei Zuo, Yu Hua, and Jie Wu,
Huazhong University of Science and Technology
DRAM technology는 density scaling과 power leakage라는 심각한 문제들에 직면하고 있다. 따라서 미래의 memory system 후보로 non-volatile momory (NVM) technology가 떠오르게 되었다. 그러나 NVM은 제한된 내구성과 낮은 write performance의 문제점에 직면하고 있으며, 더 심각한 점은 기존 방식으로 data를 indexing 하는 것의 비효율성이다.
이를 해결하기 위해 그동안의 연구들은 tree-based index structure들을 고안하였지만 그것은 평균적인 time complexity가 O(log(N))으로, data 구조의 크기인 N에 영향을 받는다. 그와 달리 hashing-based index structure은 flat data structure로, constant lookup time complexity, 즉 O(1)을 만족시킬 수 있다. 따라서 이 논문에서는 hashing 기반의 index structure에 대해 연구를 진행하였다.
Hashing index structures in persistent memory는 3가지 문제점에 직면하게 된다.
1. High Overhead for Consistency Guarantee
NVM이 memory bus에 directly accessed 하게 되어 발생한다.
2. Performance Degradation for Reducing Writes
NVM의 메모리 쓰기는 제한된 내구성을 소모하며 읽기보다 더 높은 latency와 energy를 유발하고, cache line flush나 memory fence, 혹은 logging 또는 CoW 작업도 늘어나 시스템 성능이 크게 저하되므로 wirte reduction이 필요하다.
3. Cost Inefficiency for Resizing Hash Table
Load factor 가 threshold 에 도달하거나 insertion failure 이 발생하면 resizing 필요한데, 이때 기존의 방식대로라면 O(N)만큼의 시간이 걸리고 N번의 추가적인 Insertion operation을 수행해야한다. 이때 N은 기존 hash table의 크기이다.
이러한 문제들의 해결하기 위해 이 논문에서는 a write-optimized and high-performance hashing index scheme with low-overhead consistency guarantee and cost-efficient resizing for persistent memory 인 level hashing을 제안한다.
Level hashing의 속성으로는 Low-overhead Consistency Guarantee, Write-optimized Hash Table Structure, Cost-efficient Resizing, 그리고 Real implementation and Evaluation이 있다.
Persistent systems도 일반적으로 CPU cache와 같은 volatile storage component들을 가지고 있기 때문에, data consistency에 문제가 발생할 수 있다.
NVM에서 data consistency를 보장하기 위해서는 memory write의 순서를 지켜야하는데, CPU와 메모리 컨트롤러가 이들을 reorder할 가능성이 있으므로 cache line flush 또는 memory fence가 필요하다. 그러나 이들은 system performance overhead를 발생시키므로 write를 줄이는 것이 중요하다.
더 나아가서 atomic memory write 기준보다 큰 data를 넣을 때는 logging이나 CoW가 필요하고 각 update마다 2번씩 write 하므로 이 또한 system의 성능을 해친다.
기존의 hashing schemes로는 chained hashing과 open addressing, 그 중에서도 bucketized cuckoo hashing (BCH) 가 있다.
그 외에도 NVM write를 줄이는데 중점을 둔 PCM-friendly Hash Table (PFHT)와 Path hashing이 있다.
Table 1 은 level hashing을 state-of-the-art hashing schemes (BCH, PFHT, path) 비교한 것이다.
Resizing에 관해 간략하게 이야기하자면, 기존의 resizing 기법은 out-of-place resizing으로 O(N)만큼의 시간을 소요하며 search나 deletion을 할 경우 old와 new hash table을 모두 봐야하는 문제점이 있었다. 따라서 이 논문에서는 이러한 문제점을 해결하기 위한 방법을 제시한다.
일반적으로 새로운 hash table은 기존의 2배인데, 그 이유는 initial size가 2의 지수승이며, 이를 통해 shift 연산으로 간단하게 modulo operation을 수행할 수 있기 때문이다.
먼저 Level Hashing Design은 write-optimized hash table structure로, 4개의 design decision들을 가지고 있다.
이것이 Level hashing design 구조이다. a는 logical 구조이고 b가 physical 구조이다.
이러한 디자인의 강점으로는 write-optimized, high-performance, memory-efficient가 있다.
해당 디자인은 cascading writes를 발생시키지 않고 insertions시 최대 한번의 movement만을 발생시키며, 그 movement 또한 적은 수준의 insertions을 발생시킨다.
Search/deletion/update는 최대 4개의 Bucket만을 탐색하면 되고, insertion의 경우 대부분 최대 4개의 bucket 탐색으로 가능하지만 아주 드물게 하나를 옮겨야하는 경우가 생긴다. 따라서 결론적으로는 constant-scale worst-case time complexity 를 달성할 수 있다.
각 key 마다 2개의 hash function이 있으므로 top level은 대체로 분산되어 있고, 하나의 bottom이 두개의 top에게 공유되므로 Bottom 또한 고르게 분산되어있다고 볼 수 있으며 이는 곧 memory를 효율적으로 쓸 수 있도록 한다.
Figure 2를 통해 해당 디자인으로 load factor가 최대 90%라는 것을 확인할 수 있다.
다음으로 소개할 기법은 cost-efficient in-place resizing scheme 이다.
요약하자면 새로운 Level을 기존의 hash table top level 위에 올리고 bottom level 의 값들을 새로운 level로 rehash 하는 기법이다. 따라서 resizing 과정 중에는 hash table은 3 level 구조가 된다.
먼저 전반적인 과정을 보면,
2N buckets를 new top level에 할당한다. 이때 3번째 level(=기존의 bottom level)을 interim level이라고 한다. 이 interim level의 값들을 top-two level들로 rehash 하게 되고, IL의 모든 Item이 rehash 되면 IL은 반환된다.
이러한 과정을 통해 새로운 hash table은 정확히 2배의 크기가 되며, bottom level 만 rehash 하므로 전체 중 1/3 을 rehash 하는 것만으로도 resizing을 수행할 수 있다. 또한 이 모든 과정이 하나의 hash table 안에서 이루어진다.
Resizing이 끝나고 난 후, bottom level의 Bucket들이 대부분 차 있으므로 insertion failure이 발생할 수 있다. 이를 해결하기 위해 a bottom-to-top movement를 사용한다.
Insertion 과정에서, 그동안은 bottom까지 full하면 resizing을 했지만, 이 movement를 통해 이제는 bottom level의 item을 대체 가능한 top-level로 옮길 수 있다.
또한, resizing이 끝나고 난 후, search performance가 감소할 가능성이 높다. 2/3의 item이 bottom에 있기 때문이다. 이를 해결하기 위해 dynamic search scheme 를 도입하여, bottom과 top 중 item이 더 많은 level을 먼저 search 하도록 한다.
Level hashing은 low overhead consistency guarantee를 위해 token을 도입한다. Token은 slot이 비어있는지 여부를 표현하는 것으로 1bit flag이다. Slot이 8개 이하일 경우, 총 token은 1byte로 atomic write으로 수정이 가능하다.
따라서 이 token을 이용하여 deletion/insertion/resizing할 때 log 없이도 consistency를 보장할 수 있도록 하고, update의 경우 확률적으로 log 없이도 가능하도록 한다.
좀 더 자세히 보자.
Level hash table은 pointers를 쓰지 않고 cascading writes를 하지 않으므로 fine-grained locking을 이용하여 쉽게 multi-reader/writer이 가능하다.
Linux server (kernel version 3.10.0)
Instead of NVM:
Hewlett Packard's Quartz
DRAM-based performance emulator for persistent memory
Compared with:
the state-of-the art NVM-friendly scghemes; PFHT and path hashing
the memory-efficient hashing scheme for DRAM; BCH
Else:
16-byte key | value <= 15bytes
1-bit token for each slot
two slots align a cache line (64B)
each hash table is sized for million key-value items - 3.2GB memory space
Use YCSB to evaluate concurrent performance of the concurrent level hashing.
With different-distribution integer keys in level hash table: approximate load factor
Conditions:
4 slots per buckets for BCH, PFHT and level hashing
DRAM read write latency = 136ns
Worst-case time complexity:
Compared with:
state-of-the-art concurrent hash table in DRAM (Libcuckoo)