1. 程式人生 > >Hyperion: Building the Largest In memory Search Tree

Hyperion: Building the Largest In memory Search Tree

Introduction

  索引在資料管理中起到很重要的作用,很多索引結構都會採用訪問速度快而且記憶體消耗少的trie樹,但一般常見的trie樹索引結構都強調效率而忽視記憶體的效率,他們的效率雖然高,但記憶體的消耗比較大。這篇文章提出了一種新的樹形結構----Hyperion,在效率上做到對範圍查詢和點查詢都能夠有比較好支援的同時,也實現了記憶體效率使用的極大提升。

整篇論文大體上可以分層三個結構:

  • 對常見的trie樹做一個調整,優化trie樹的編碼方式,從而減小對一棵trie樹編碼的開銷。

  • 為了減小由於記憶體分配而導致的碎片,Hyperion提出了一個量身定製的記憶體管理器,主要採用匿名記憶體對映的方式來實現記憶體分配。

  • 對上述的結構從編碼方式、查詢效能、關鍵字預處理等方面做了一定的優化。

編碼方式

  我們通常用到的trie樹結構如下圖,但是這棵trie樹有點問題,就是樹的高度太高,不易於搜尋,同時,每一個節點就儲存一個關鍵字,同時節點內還會包含節點的孩子指標等元素據信息,導致一個節點儲存一個關鍵字的效率比較低。在這裡作者提出了容器的概念,容器可以儲存字串中連續的兩個關鍵字,也就是16bit,且容器中可以有多個這樣的16位的關鍵字序列,最多可以有65536個16bit的關鍵字序列。容器對應著圖中用實現標出的框C1、C2以及C3。

更進一步可以表示成下圖所示,上圖的容器全部表現成下面的形式,在容器內,連續的兩個字元組成容器內的一個節點,如果末端只有一個,則用一個表示:

  在圖中,會發現一個問題,將連續的兩個關鍵字的字首作為一部分,字尾作為一部份,當容器內的某些關鍵字具有相同的字首的時候,比如上圖的th和to兩個字串有相同的字首"t",使得t被重複的儲存,為了避免浪費這種重複儲存所消耗的空間,在這裡作者使用瞭如下的結構,將連續的兩個字元分為兩部分ki0ki1​,即對應著T-Node(​)和S-Node(​ ),其中T-Node是連續兩個字元的第一個字元,比如上圖的a,b,t;而S-Node則是以第一個字元為字首的字尾集合,還是以th和to為例,它們的字首是t(T-Node),則S-Node是h和o,示例如下:

下面開始對這個容器就行的編碼,首先對容器內的T-Node和S-Node進行編碼,兩種Node的編碼格式如下:

  第一個標誌位表示型別,佔兩個位元大小,10和11表示葉子節點(是否帶值,對於這個標記還有疑問,但對文章影響不大),00表示指向無效的記憶體位置比如標誌記憶體碎片等等(相當於網路傳輸包的EOF標誌),其他節點都是內部節點,用01表示。標記位k表示節點是T-Node(0)還是S-Node(1)型別,C表示容器與容器之間的關係,下一段介紹,例如下面這兩種情況對他們的編碼如圖所示。

  容器與容器的關係可以分為四種,佔兩個位元,用S-Node的c去表示,c=00時,表示容器已經到達最低端,下面再也沒有其它容器了;c=11時,表示會採用一種壓縮路徑的方法去處理下一個容器,當trie樹結構如下圖所示時,就會採用。

c等於01的時候,表示正常的引用子容器的地址,其中子容器的地址大小是5個位元組,為什麼是5個位元組具體在後面的記憶體管理器介紹。C=10 的時候,表示大容器裡面嵌入了一些小容器,這個小容器就直接連在了S-Node的後面,兩種情況的編碼方式具體如下圖。

container與container的嵌入關係還可以這樣表示更復雜的形式如下:

  最後再介紹一下container的整個結構,Container頭部包含Size(已經分配的記憶體大小),Free(剩餘的記憶體大小),J(容器跳錶標誌位,後面介紹),S(延遲位,作為一個容器分裂的引數),而payload則存放的是跳錶資訊,在payload之後就開始對結點進行編碼:

  當一個容器中嵌入的容器數目過多,導致容器的儲存容量過大的時候,就會進行分裂,分裂的條件如下:

  在作者的系統中a=16Kib,b=64Kib,其中延遲引數s在container中可以設定,因為s在container的編碼中只佔兩位,所以值只在0-3內。一旦開始分裂,分裂的過程以下圖為例,首先會為這個容器申請一塊新的記憶體,同時初始化容器的頭部引數,然後呼叫memory_copy函式,將容器的payload複製到新的容器中,將頭部引數的值更新,之前那部分空間被釋放後,就需要將後面的內容往前移,同時將容器尾部空間的那一塊記憶體初始化。

記憶體管理

  介紹了容器與節點這種緊湊的編碼方式之後,文章為Hyperion設計了一個記憶體管理器,因為不斷的使用堆去分配記憶體會造成外部碎片。在Hyperion的記憶體管理器中,為了避免這些記憶體碎片,於是主要使用匿名記憶體對映的方式去申請記憶體,採用這種方式申請記憶體的時候會返回一個與頁對其的記憶體塊。在申請記憶體大小小於2016個位元組的時候,採用剛剛說過匿名記憶體對映的方法,大於2016個位元組的時候,還是採用堆分配的方式去申請記憶體,因為通過堆申請記憶體比較快,記憶體管理器採用分層結構去定位已事先分配好的記憶體塊,如下圖:

  記憶體管理器的最上層有64個superbin,每一個superbin下面的chunk大小不一樣,從superbin1到superbin63依次按32個位元組的大小遞增,其中superbin1的每一個chunk大小是32byte。而SB0的chunk是大於2016byte的(即剛剛說的採用對分配的方式去分配,因為對分配的方式比較快,而且對於大塊記憶體不易產生外部碎片)。每一個superbin中有2的14次方個metabin,而每個metabin有256個bins,每一個bins則有2的12次方4096個chunk。通過這個記憶體管理器,定位一塊記憶體空間只需要5個位元組的大小。

使用這個記憶體分配器後,不僅可以減少記憶體中外部碎片的數量,還可以減少一些記憶體開銷,比如通常在一臺64位的機器上,指向某一塊記憶體的指標大小通常是8位元組,在這裡只需要5個位元組。第二個就是使用堆分配記憶體的時候,還要額外的花費8位元組去儲存這個堆的元素據信息。最後就是在這個記憶體管理器的每一層,都會維護一個數組去標記還有空chunk的superbin/metabin/bins,這樣可以快速的去定位空的記憶體塊,從而去申請記憶體。

這裡要注意的是SB0,它處理的是申請的記憶體大於2016byte的請求,它下面的bin被稱為Extended Bin,與普通的bin不同,它是包含了一個普通指標(8位元組)和其他欄位的bin(共16位元組),它分配的chunk大小也是隨著請求的不同,記憶體塊大小的遞增速度申請也不同。Chained Extended Bins (CEB)是SB0中連續的8個記憶體塊,通過一個chained point去引用,並且這八個記憶體塊統一由它管理,這樣就避免了在容器分裂的時候不用在trie樹中再把這些分裂的指標新增進節點裡去,避免一些繁雜的記憶體移動操作和一些更新開銷。

效能和記憶體優化

  為了提升效能和減少記憶體空間,作者又幾種方法去優化。

Delta Encoding(d)

  同一層的節點之間按升序排列,可以對他們進行增量編碼,就比如這兩個圖,超過了3bits的表示範圍,無法做Delta Encoding。此時將標值d全部置為0,將增量寫道原先存值的位置。

2.Jump Successor (js)

  第二個優化是在T-Node中維護了一個Jump Successor,用來從一個T-Node直接跳到下一個T-Node,而不需要遍歷該T-Node下的S-Node,從而才能找到下一個T-Node的位置。若T-Node的js標記1說明使用了Jump Successor,此時該T-Node後面有一個16bits的reference,儲存下一個T-Node距離該T-Node的距離。

3. Jump Tables

  JumpTable有兩類,第一類是在T-Node中的T-Node jump table,第二類則是在容器內的Container Jump Table。

T-Node jump table

  如果T-Node的jt位設定為1,表明T-Node啟用了jump table這個表。T-Node jump table是一個長度是15的陣列,(陣列的每個元素佔用2B。)它將S-Node所有可能的範圍【0-255】等分成16份,記錄每一位置相對T-Node的偏移量,從而可以快速的從T-node到S-Node的訪問。

Container Jump Table

  這裡的Container Jump Table是為了在容器中快速定位T-Node,J佔了3bits,最大表示7,每一個表示7個Entries,因此最多表示49個Entries。每個Entry佔用4B,1B儲存T-Node的Key,3B的Offset用來定位T-Node的位置,可以直接利用這個表定位到最接近目標T-Node的位置.

如圖,這裡的J設定為2,則表示有2*7=14個,就會將容器中所有的T-Node等分14分,然後在陣列中記錄每一份的關鍵字以及它的偏移量。

Key Pre-processing

  這是一個可選項,關鍵字預處理函式的作用就是為了將關鍵字通過一定的函式轉換後得到的值更加符合我們所期望的分佈,比如一些原來很分散的資料,通過一些函式處理後,可以讓這些資料的分佈變得緊密。通常,關鍵字的預處理函式是一個單射函式。對於範圍查詢來說,還要求經過單射函式得到的值也要滿足原先關鍵字所遵守的順序。在Hyperion中,作者使用的方法是在第二,第三,第四個byte中插入兩個0,這樣就會使得容器的數量由2(16)2(16)=2(32)個容器減少到2(14)2(12)=2(26)個。可以使得一個容器放入更多的關鍵字資訊。

相關推薦

Hyperion: Building the Largest In memory Search Tree

Introduction   索引在資料管理中起到很重要的作用,很多索引結構都會採用訪問速度快而且記憶體消耗少的trie樹,但一般常見的trie樹索引結構都強調效率而忽視記憶體的效率,他們的效率雖然高,但記憶體的消耗比較大。這篇文章提出了一種新的樹形結構----Hyperion,在效率上做到對範圍查詢和點

LeetCode 501. Find Mode in Binary Search Tree (找到二叉搜索樹的眾數)

btn https 標簽 one con pac 發現 log 個數字 Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred

[Leetcode] Binary tree -- 501. Find Mode in Binary Search Tree

upd bsp have values greate pop mes strong print Given a binary search tree (BST) with duplicates, find all the mode(s) (the most freque

501. Find Mode in Binary Search Tree

element arraylist ati temp 哪些 arc org href search Given a binary search tree (BST) with duplicates, find all the mode(s) (the most freque

find second maximum element in Binary search tree

roo arc 一個 gpo sharp div find serial val 一個BST, 問怎麽找到第二大的節點。首先說了個naive的方法,serialize, 他問有沒有更有效的方法。最後用的iterative的方法向右遍歷子節點 log(n) 或者inorder

[LeetCode]501. Find Mode in Binary Search Tree二叉搜索樹尋找眾數

pri fin lis 需要 efi lee value spa find 這次是二叉搜索樹的遍歷 感覺只要和二叉搜索樹的題目,都要用到一個重要性質: 中序遍歷二叉搜索樹的結果是一個遞增序列; 而且要註意,在遞歸遍歷樹的時候,有些參數如果是要隨遞歸不斷更新(也就是如果遞歸返

Find Mode in Binary Search Tree 二叉搜索樹中查找眾數

class div 問題 xtra 空間分析 deb spa pos out [抄題]: Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently

LF.51.Insert In Binary Search Tree

all body gpo recommend become amp ear time AC Insert a key in a binary search tree if the binary search tree does not already contain th

LF.53.Delete In Binary Search Tree

root there leaf set ins keys ive tar earch Delete the target key K in the given binary search tree if the binary search tree contains

LF.52.Search In Binary Search Tree

return contain fin AI int ear bin post otherwise Find the target key K in the given binary search tree, return the node that contains

501. Find Mode in Binary Search Tree查找BST中的眾數

pan 為什麽 AR dup 不用 lan 結果 with bug [抄題]: Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occur

132.Find Mode in Binary Search Tree(二分搜索樹的眾數)

ati 之前 復雜度 i++ cit 二分搜索 無法 treenode 重復 題目: Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently oc

Leetcode501.Find Mode in Binary Search Tree二叉搜尋樹中的眾數

給定一個有相同值的二叉搜尋樹(BST),找出 BST 中的所有眾數(出現頻率最高的元素)。 假定 BST 有如下定義: 結點左子樹中所含結點的值小於等於當前結點的值 結點右子樹中所含結點的值大於等於當前結點的值 左子樹和右子樹都是二叉搜尋樹 例如: 給定 B

255. Verify Preorder Sequence in Binary Search Tree

Given an array of numbers, verify whether it is the correct preorder traversal sequence of a binary search tree. You may assume each number in the seque

error LOADING Redis is loading the dataset in memory問題解決

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

[LeetCode] 501. Find Mode in Binary Search Tree

Find Mode in Binary Search Tree Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in

error LOADING Redis is loading the dataset in memory問題解決

                由於swap的頻繁切換導致上述錯誤解決:[[email protected] RedisTest]# redis-cli flushall(error) LOADING Redis is loading the dataset in memory[[email

[LeetCode] Verify Preorder Sequence in Binary Search Tree 驗證二叉搜尋樹的先序序列

Given an array of numbers, verify whether it is the correct preorder traversal sequence of a binary search tree. You may assume each number in the seque

[LeetCode] Find Mode in Binary Search Tree 找二分搜尋數的眾數

Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. Assume a BST is defined

501. Find Mode in Binary Search Tree - Easy

Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. Assume a BST is