B+樹性能測試(C++實現)

B+樹

簡介

文章主要是對我本身實現的B+樹的各項指標測試結果展現。B+樹的CRUD具體算法文本未涉及,後續可能會補充。c++

項目地址

github.com/SirLYC/BPTr…git

B+樹簡介

引自維基百科github

B+ 樹是一種樹數據結構,一般用於數據庫和操做系統的文件系統中。B+ 樹的特色是可以保持數據穩定有序,其插入與修改擁有較穩定的對數時間複雜度。B+ 樹元素自底向上插入,這與二叉樹剛好相反。算法

B+樹結構

B+樹有一個重要的參數叫(m),決定了一顆B+樹每個節點存儲關鍵子的個數。數據庫

每個節點都會按順序存儲一組關鍵字,對於非根節點,其關鍵字樹s >= (m + 1) /2。對於葉子節點,其結構中存儲指向值的指針,與關鍵字對應,同時還有一個next指針,指向下一個兄弟葉子節點,所以找到最左葉子節點後能夠按關鍵字順序遍歷;對於非葉子節點,存有s個指向子節點的指針。數組

B+樹經過插入時分裂,刪除時向兄弟節點借關鍵字或合併兄弟節點實現平衡,全部的葉子節點都在同一層。查詢、插入、刪除效率都是Log(N)bash

實現的public API

template<typename K, typename V>
class BPTree {
private:
    ...

public:
    // constructor and destructors
    ...

    /** * deserialize from a file */
    static BPTree<K, V> deserialize(const std::string &path);

    static BPTree<K, V> deserialize(const std::string &path, comparator<K> comp);

    void put(const K &key, const V &value);

    void remove(K &key);

    /** * @return NULL if not exists else a pointer to the value */
    V *get(const K &key);

    bool containsKey(const K &key);

    int getOrder();

    int getSize();

    /** * iterate order by key * @param func call func(key, value) for each. func returns true means iteration ends */
    void foreach(biApply<K, V> func);

    void foreachReverse(biApply<K, V> func);

    void foreachIndex(biApplyIndex<K, V> func);

    void foreachIndexReverse(biApplyIndex<K, V> func);

    void serialize(std::string &path);

    /** * clear the tree * note that all values allocated will be freed */
    void clear();
};
複製代碼

Tips:爲了兼容自定義類別,須要傳入比較大小的函數指針,或實現相應的>、=、<等運算符;數據結構

重要數據結構

Node:B+樹的索引節點函數

主要數據結構以下:工具

struct Node {
    // parent
    // if root, parentPtr == NULL
    Node *parentPtr = NULL;
    // flag
    bool leaf;
    List<K> keys;
    /*-------leaf--------*/
    Node *previous = NULL;
    Node *next = NULL;
    List<V> values;
    /*-------index-------*/
    List<Node *> childNodePtrs;
    // for init
    int initCap;
    // constructor
    ...
};
複製代碼

List<T>:使用定長數組實現的List,比起std::vector<T>功能更簡單,效率更高;內存會在移除必定數量元素後減少。

序列化

  • 文件後綴爲bpt
  • 頭部格式:
偏移(byte) 大小(byte) 內容
0 4 LYC\0 頭部標識
4 4 order,int類型,B+樹的階
8 4 initCap,int類型,每一個節點預分配大小
12 4 size,int類型,元素個數
  • 若是size不爲0,頭部結束後就是根節點,節點有同一的格式,節點前面通用格式:
偏移(相對於節點起始,byte) 大小(byte) 內容
0 4 leaf,int類型,標識節點是否爲葉子節點
4 4 sizeofK,int類型,key類型佔字節數
8 4 kSize,int類型,該節點擁有的關鍵字數量
12 kSize * sizeofK 按順序存儲關鍵字
  • 對於葉子節點
偏移(相對於節點起始,byte) 大小(byte) 內容
12 + kSize * sizeofK 4 sizeofV,int類型,value類型佔字節數
16 + kSize * sizeofK kSize*sizeofK 按順序存儲值
  • 對於非葉子節點
偏移(相對於節點起始,byte) 大小(byte) 內容
12 + kSize * sizeofK kSize * 8 long類型,按順序存儲字節點在文件中的偏移

實現要點

  • 最初的實現是使用vector,測下來性能不是特別理想;
  • 基於節點內關鍵字有序的特色,查找時使用二分查找;
  • 存儲子節點應該存儲指向節點的指針。由於涉及分裂、合併操做,須要複製列表,若是存儲的是結構,複製會形成遞歸複製,效率低,且不易控制內存;
  • 由於root節點沒有最少關鍵字限制,在刪除節點操做完成後,須要檢查一下root子節點數量,若是爲1,直接將root的字節點設置爲root,不然刪除子節點後可能會形成root的關鍵字、子節點丟失。
  • 每一次插入、刪除節點最後一個關鍵字後須要向上更新parent。
  • 分裂、合併操做時對於葉子節點next和previous指針要更新。

測試

測試環境:

測試環境

文件main.cpp有以下宏,1表示開啓測試:

// 測試List性能(和vector對比)
#define TEST_LIST 0
// 測試B+樹功能正確性
#define TEST_FUNC 0
// 測試B+樹的速度(增刪查改)
#define TEST_SPEED 0
// 測試B+樹的堆使用及內存泄漏(build後使用工具測試)
#define TEST_MEM 0
// 測試B+樹的序列化與反序列化
#define TEST_SERIAL 0
複製代碼

List測試

  • 數據量:10^5
  • 增、刪數據,斷言對應位置是否如預期(功能測試)
  • 尾部插入測試(預分配和不預分配)
  • 頭部插入測試
  • 頭刪除測試
  • 尾刪除測試
  • rangeRemove測試

測試運行結果:

List測試

表格:

List(ms) vector(ms)
尾部插入 1.506 4.724
尾部插入(預分配空間) 1.201 2.765
頭部插入 834.804 906.981
移除一半元素(頭部) 619.493 805.379
移除一半元素(尾部) 1.444 7.523
rangeRemove(一半) 0.065 0.558

柱狀圖:

List測試柱狀圖

B+樹功能測試

  • 數據量:10^5
  • 數據插入後斷言插入數據存在
  • 去除一半數據,斷言去除數據不存在,未去除數據存在
  • 從新插入全部數據,斷言全部數據都存在(測試刪除是否破壞結構)
  • clear()後斷言以前數據都不存在
  • 插入所有數據後,測試遍歷方法(key順序測試)

速度測試

  • 數據量:10^八、10^七、10^6
  • B+樹階爲 log(TEST_SIZE)^2
  • 循環插入數據
  • 循環訪問全部數據
  • 循環移除數據

測試運行結果:

B+樹功能測試

表格:

bp tree(ms) stl map(ms)
插入(10^8) 192808.064 325621.333
訪問(10^8) 163102.022 280150.403
移除(10^8) 213982.406 366576.836
插入(10^7) 11825.821 22213.139
訪問(10^7) 10190.870 18137.073
移除(10^7) 15130.015 22133.154
插入(10^6) 1057.291 1624.615
訪問(10^6) 888.186 1155.504
移除(10^6) 1099.584 1495.433

柱狀圖:

B+樹速度測試柱狀圖

內存測試

  • 數據量:10^6
  • 添加數據後移除數據
  • 循環5次,添加後調用clear()
  • 工具:Xcode Instruments

測試運行結果:

B+樹內存測試

序列化測試

  • 數據量:10^6~0

測試運行結果:

B+樹序列化測試結果

表格:

數據量 序列化(ms) 反序列化(ms) 文件大小(bytes)
0 13.794 0.082 16
1 0.15 0.03 40
10 0.111 0.033 112
10^2 0.207 0.064 1052
10^3 0.877 0.490 10168
10^4 7.870 5.567 101312
10^5 61.318 59.418 1012108
10^6 694.879 477.630 10127572
相關文章
相關標籤/搜索