使用pthread和线程池实现B+树的并行块加载bulkload过程
數(shù)據(jù)庫(kù)課程設(shè)計(jì)
實(shí)驗(yàn)環(huán)境
- 架構(gòu):Intel x86_64 (虛擬機(jī))
- 操作系統(tǒng):Ubuntu 20.04
- 匯編器:gas (GNU Assembler) in AT&T mode
- 編譯器:gcc
項(xiàng)目地址
項(xiàng)目地址
課程設(shè)計(jì)要求
- 理解B+樹bulkloading操作過程的代碼。
- 掌握多核并行程序的設(shè)計(jì)。
- 并行化實(shí)現(xiàn)
- 實(shí)現(xiàn)底層葉子節(jié)點(diǎn)的并行構(gòu)建。
- 實(shí)現(xiàn)索引節(jié)點(diǎn)(非葉子節(jié)點(diǎn))的并行構(gòu)建。
- 實(shí)驗(yàn)結(jié)果
- 比較不同數(shù)據(jù)量的并行加速效果,將加速比繪制成折線圖。
項(xiàng)目運(yùn)行
進(jìn)入Tree_project文件夾,在終端輸入make,然后:
- 輸入./run為串行加載程序;
- 輸入./run parallel為并行加載程序;
- 輸入./run print為串行加載程序,并打印B+樹;
- 輸入./run parallel print為并行加載程序,并打印B+樹。
課程設(shè)計(jì)報(bào)告
B+樹bulkloading過程的理解
1. B+樹bulkloading過程的準(zhǔn)備階段:
在main函數(shù)中首先執(zhí)行程序,所以先看main函數(shù),首先聲明節(jié)點(diǎn)大小B_為512,存儲(chǔ)指向節(jié)點(diǎn)的指針個(gè)數(shù)n_pts_為1000000,然后將讀取數(shù)據(jù)文件和B+樹存儲(chǔ)數(shù)據(jù)文件的文件名復(fù)制到data_file和tree_file字符串?dāng)?shù)組中,并將文件名打印出來(lái),申請(qǐng)一個(gè)名為table的Result類型數(shù)組,數(shù)組大小為n_pts_,讀入數(shù)據(jù)文件data_file的文件流到文件指針fp中,然后讀入每行數(shù)據(jù)文件data_file中的數(shù)據(jù)到string類型變量line中,每一行i的兩個(gè)數(shù)據(jù)以逗號(hào)分隔,第一個(gè)數(shù)據(jù)讀到table數(shù)組相應(yīng)下標(biāo)i的key_里,代表關(guān)鍵字,第二個(gè)數(shù)據(jù)讀到table數(shù)組相應(yīng)下標(biāo)的id_里,代表指向節(jié)點(diǎn)的指針,關(guān)閉文件流,開始計(jì)時(shí),首先創(chuàng)建一個(gè)B+樹trees_,并對(duì)其進(jìn)行初始化,然后對(duì)其進(jìn)行bulkloading過程,打印這個(gè)過程的時(shí)間,程序結(jié)束。
2. B+樹bulkloading過程使用的函數(shù):
int BTree::bulkload(int n, const Result *table)B+樹bolkloading會(huì)調(diào)用一個(gè)名為int bulkload(int n, const Result *table)的函數(shù)進(jìn)行加載,這個(gè)函數(shù)在b_tree.cc中定義,參數(shù)n是要加載的條目entries數(shù),參數(shù)table是一個(gè)哈希表,存儲(chǔ)了這n個(gè)條目的相關(guān)信息,大小也為n,每個(gè)條目與每個(gè)哈希表的下標(biāo)一一對(duì)應(yīng),哈希表的關(guān)鍵字是key_,值是一個(gè)指向子結(jié)點(diǎn)的指針id_,通過這個(gè)哈希表,可以快速找到要加載的條目對(duì)應(yīng)的指針。
2.1 首先聲明變量:
BIndexNode *index_child = NULL; BIndexNode *index_prev_nd = NULL; BIndexNode *index_act_nd = NULL; BLeafNode *leaf_child = NULL; BLeafNode *leaf_prev_nd = NULL; BLeafNode *leaf_act_nd = NULL;int id = -1; int block = -1; float key = MINREAL;bool first_node = true; int start_block = 0; int end_block = 0;函數(shù)一開始時(shí),首先聲明了幾個(gè)變量:
- 三個(gè)BIndexNode *類的索引結(jié)點(diǎn)指針:
- index_child指向索引結(jié)點(diǎn)的子結(jié)點(diǎn),這個(gè)子結(jié)點(diǎn)也是索引結(jié)點(diǎn)
- index_prev_nd指向前一個(gè)條目插入滿的索引結(jié)點(diǎn)
- index_act_nd指向當(dāng)前正在進(jìn)行插入條目的索引結(jié)點(diǎn)
- 聲明了三個(gè)BLeafNode *葉子結(jié)點(diǎn)指針
- leaf_child指向第1層索引結(jié)點(diǎn)(葉子結(jié)點(diǎn)層數(shù)為0)的子結(jié)點(diǎn),這個(gè)子結(jié)點(diǎn)是葉子結(jié)點(diǎn)
- leaf_prev_nd指向上一個(gè)插入滿的葉子結(jié)點(diǎn)
- leaf_act_nd指向當(dāng)前正在進(jìn)行插入的葉子結(jié)點(diǎn)。
- 聲明整型變量id和浮點(diǎn)型變量key存儲(chǔ)遍歷哈希表table時(shí)每個(gè)id_和key_的值
- 聲明布爾類型變量first_node判斷當(dāng)前結(jié)點(diǎn)是否是當(dāng)前層的第一個(gè)結(jié)點(diǎn)
- 聲明整型變量start_block和end_block存儲(chǔ)B+樹每一層的第一個(gè)結(jié)點(diǎn)和最后一個(gè)結(jié)點(diǎn)的位置,葉子結(jié)點(diǎn)的block從1開始,依次加一往后
- 聲明整型變量block存儲(chǔ)從每一層的第一個(gè)結(jié)點(diǎn)位置start_block到最后一個(gè)結(jié)點(diǎn)的位置end_block進(jìn)行遍歷時(shí)每一個(gè)結(jié)點(diǎn)的位置
2.2 接著先從底層葉子節(jié)點(diǎn)構(gòu)建,從左往右按順序構(gòu)建一個(gè)雙向鏈表:
for (int i = 0; i < n; ++i) {id = table[i].id_;key = table[i].key_;if (!leaf_act_nd) {leaf_act_nd = new BLeafNode();leaf_act_nd->init(0, this);if (first_node) {first_node = false; // init <start_block>start_block = leaf_act_nd->get_block();}else { // label siblingleaf_act_nd->set_left_sibling(leaf_prev_nd->get_block());leaf_prev_nd->set_right_sibling(leaf_act_nd->get_block());delete leaf_prev_nd; leaf_prev_nd = NULL;}end_block = leaf_act_nd->get_block();} leaf_act_nd->add_new_child(id, key); // add new entryif (leaf_act_nd->isFull()) {// change next node to store entriesleaf_prev_nd = leaf_act_nd;leaf_act_nd = NULL;} } if (leaf_prev_nd != NULL) {delete leaf_prev_nd; leaf_prev_nd = NULL; } if (leaf_act_nd != NULL) {delete leaf_act_nd; leaf_act_nd = NULL; }- 先從底層葉子節(jié)點(diǎn)構(gòu)建,從左往右按順序構(gòu)建一個(gè)雙向鏈表,遍歷哈希表table:
- 在每次遍歷中,將哈希表中每一項(xiàng)的id_和key_值記錄在整型變量id和key中,代表要加載每一項(xiàng)的指向子節(jié)點(diǎn)的指針和關(guān)鍵字。判斷此時(shí)指向當(dāng)前正在進(jìn)行插入的葉子結(jié)點(diǎn)leaf_act_nd是否為空:
- 如果為空,那么要?jiǎng)?chuàng)建一個(gè)新的葉子結(jié)點(diǎn)leaf_act_nd,并使用它的初始化方法init(0, this)初始化這個(gè)葉子結(jié)點(diǎn),設(shè)置這個(gè)結(jié)點(diǎn)所處層數(shù)為0,所在B+樹為當(dāng)前調(diào)用這個(gè)操作的B+樹:
- 在葉子結(jié)點(diǎn)的初始化方法void init(int level, BTree *btree)中,首先根據(jù)傳入?yún)?shù)設(shè)置所在層數(shù)和B+樹,設(shè)置結(jié)點(diǎn)中保存的條目數(shù)num_entries_和關(guān)鍵字?jǐn)?shù)num_keys_為0,左兄弟和右兄弟所處在的block位置為-1,設(shè)置臟塊dirty_為true,表示要寫回文件。
- 然后調(diào)用btree_->file_->get_blocklength()獲取block塊的長(zhǎng)度到b_length,調(diào)用語(yǔ)句get_key_size(b_length)計(jì)算block塊的長(zhǎng)度b_length除以葉子結(jié)點(diǎn)大小LEAF_NODE_SIZE并向下取整賦給最大可以存儲(chǔ)的關(guān)鍵字?jǐn)?shù)capacity_keys_,經(jīng)過計(jì)算后得到關(guān)鍵字的大小到key_size中。
- 接著為結(jié)點(diǎn)的關(guān)鍵字浮點(diǎn)型指針key_申請(qǐng)一個(gè)大小為關(guān)鍵字最大容量capacity_keys_的浮點(diǎn)型數(shù)組并將其中元素都初始化為MINREAL,獲取結(jié)點(diǎn)的頭部大小header_size和條目大小entry_size,根據(jù)這兩個(gè)值以及block塊的長(zhǎng)度b_length和關(guān)鍵字的大小key_size計(jì)算出這個(gè)結(jié)點(diǎn)可以包含的條目的最大容量capacity_,小于100則報(bào)錯(cuò)退出,否則為結(jié)點(diǎn)的對(duì)象id整型指針id_申請(qǐng)一個(gè)大小為capacity_的整型數(shù)組并將其中元素都初始化為-1。
- 最后為字符型指針blk申請(qǐng)一個(gè)大小為block塊的長(zhǎng)度b_length的字符型數(shù)組,使用語(yǔ)句btree_->file_->append_block(blk)添加一個(gè)新塊block到文件末尾,并設(shè)置結(jié)點(diǎn)所處塊的序號(hào)。
- 在int append_block(Block block)方法中,首先使用語(yǔ)句fseek(fp_, 0, SEEK_END)使BlockFile的文件指針fp_指向存儲(chǔ)文件的末尾。
- 然后使用語(yǔ)句put_bytes(block, block_length_)將這個(gè)block中的內(nèi)容寫入文件指針fp_指向的存儲(chǔ)文件,并且文件中block的數(shù)目num_blocks_加一。
- 接著使用語(yǔ)句fseek(fp_, SIZEINT, SEEK_SET)使BlockFile的文件指針fp_指向存儲(chǔ)文件的開頭后4個(gè)字節(jié)文件頭部的位置,使用語(yǔ)句fwrite_number(num_blocks_)將更新后的block的數(shù)目num_blocks_寫入文件中。
- 最后使用語(yǔ)句fseek(fp_, -block_length_, SEEK_END)使文件指針fp_指向的存儲(chǔ)文件加入的新塊,設(shè)置文件指針指向的塊的序號(hào)act_block_為num_blocks_并返回act_block_ - 1。
- 接著判斷這個(gè)葉子結(jié)點(diǎn)是否是它所在這一層的第一個(gè)結(jié)點(diǎn)first_node:
- 如果是,則標(biāo)記起始點(diǎn)的位置start_block為這一個(gè)結(jié)點(diǎn)的位置,并把first_node置為false,使之后的葉子結(jié)點(diǎn)都不是第一個(gè)葉子結(jié)點(diǎn)
- 如果不是,那么標(biāo)記當(dāng)前插入的葉子結(jié)點(diǎn)的左兄弟的block位置為上一個(gè)插入滿的葉子結(jié)點(diǎn)的block位置,上一個(gè)插入滿的葉子結(jié)點(diǎn)的右兄弟的block位置為當(dāng)前插入的葉子結(jié)點(diǎn)的block位置,形成一個(gè)雙向鏈表。刪除指向上一個(gè)插入滿的葉子結(jié)點(diǎn)的指針leaf_prev_nd,此時(shí)調(diào)用析構(gòu)函數(shù)將結(jié)點(diǎn)的信息寫入文件相應(yīng)的block位置中。
- 標(biāo)記最后一個(gè)結(jié)點(diǎn)的位置end_block為當(dāng)前正在進(jìn)行插入的葉子結(jié)點(diǎn)的位置
- 如果為空,那么要?jiǎng)?chuàng)建一個(gè)新的葉子結(jié)點(diǎn)leaf_act_nd,并使用它的初始化方法init(0, this)初始化這個(gè)葉子結(jié)點(diǎn),設(shè)置這個(gè)結(jié)點(diǎn)所處層數(shù)為0,所在B+樹為當(dāng)前調(diào)用這個(gè)操作的B+樹:
- 當(dāng)前插入的葉子結(jié)點(diǎn)leaf_act_nd不為空后,使用語(yǔ)句leaf_act_nd->add_new_child(id, key)插入指針為id,關(guān)鍵字為key的條目到當(dāng)前正在執(zhí)行插入操作的結(jié)點(diǎn)leaf_act_nd中,插入完成后,如果當(dāng)前插入的葉子結(jié)點(diǎn)已經(jīng)滿了,那標(biāo)記當(dāng)前插入的葉子結(jié)點(diǎn)為上一個(gè)插入的結(jié)點(diǎn),當(dāng)前插入的葉子結(jié)點(diǎn)為空,以便之后可以創(chuàng)建新的結(jié)點(diǎn)插入新的條目。遍歷完成,哈希表table中所有的條目都被加到了葉子結(jié)點(diǎn)中。
- 刪除指向上一個(gè)插入滿的葉子結(jié)點(diǎn)的指針leaf_prev_nd和當(dāng)前正在進(jìn)行插入的葉子結(jié)點(diǎn)leaf_act_nd,此時(shí)調(diào)用析構(gòu)函數(shù)將結(jié)點(diǎn)的信息寫入文件相應(yīng)的block位置中。
- 在每次遍歷中,將哈希表中每一項(xiàng)的id_和key_值記錄在整型變量id和key中,代表要加載每一項(xiàng)的指向子節(jié)點(diǎn)的指針和關(guān)鍵字。判斷此時(shí)指向當(dāng)前正在進(jìn)行插入的葉子結(jié)點(diǎn)leaf_act_nd是否為空:
2.3 然后從下往上,一層層構(gòu)建索引節(jié)點(diǎn),每一層也是從左往右構(gòu)建索引節(jié)點(diǎn):
int current_level = 1; // current level (leaf level is 0) int last_start_block = start_block; // build b-tree level by level int last_end_block = end_block; // build b-tree level by levelwhile (last_end_block > last_start_block) {first_node = true;for (int i = last_start_block; i <= last_end_block; ++i) {block = i; // get <block>if (current_level == 1) {leaf_child = new BLeafNode();leaf_child->init_restore(this, block);key = leaf_child->get_key_of_node();delete leaf_child; leaf_child = NULL;}else {index_child = new BIndexNode();index_child->init_restore(this, block);key = index_child->get_key_of_node();delete index_child; index_child = NULL;}if (!index_act_nd) {index_act_nd = new BIndexNode();index_act_nd->init(current_level, this);if (first_node) {first_node = false;start_block = index_act_nd->get_block();}else {index_act_nd->set_left_sibling(index_prev_nd->get_block());index_prev_nd->set_right_sibling(index_act_nd->get_block());delete index_prev_nd; index_prev_nd = NULL;}end_block = index_act_nd->get_block();} index_act_nd->add_new_child(key, block); // add new entryif (index_act_nd->isFull()) {index_prev_nd = index_act_nd;index_act_nd = NULL;}}if (index_prev_nd != NULL) {// release the spacedelete index_prev_nd; index_prev_nd = NULL;}if (index_act_nd != NULL) {delete index_act_nd; index_act_nd = NULL;}last_start_block = start_block;// update infolast_end_block = end_block; // build b-tree of higher level++current_level; } root_ = last_start_block; // update the <root>首先進(jìn)行聲明:
- 以葉子所在層為第0層,從下往上,聲明當(dāng)前結(jié)點(diǎn)所處樹的層current_level為1
- 之前層開始的位置last_start_block為上面第一個(gè)葉子結(jié)點(diǎn)的位置start_block
- 之前層結(jié)束的位置last_end_block為上面最后一個(gè)葉子結(jié)點(diǎn)的位置end_block
然后按照每一層從左往右、從下往上構(gòu)建索引結(jié)點(diǎn),直到根結(jié)點(diǎn):
- 然后當(dāng)之前層結(jié)束的位置last_end_block大于之前層開始的位置last_start_block時(shí),說明還沒有插入到根結(jié)點(diǎn)(last_start_block == last_end_block),進(jìn)入一個(gè)while循環(huán),每次循環(huán),從下往上構(gòu)建每一層的索引結(jié)點(diǎn):
- 首先使此層第一個(gè)點(diǎn)first_node為true,從上一個(gè)開始的位置last_start_block開始,到上一個(gè)結(jié)束的位置last_end_block結(jié)束,進(jìn)入一個(gè)for循環(huán),使block為當(dāng)前位置
- 如果當(dāng)前所處層current_level為1,說明正在構(gòu)建第1層的索引結(jié)點(diǎn),其每個(gè)兒子結(jié)點(diǎn)都是葉子結(jié)點(diǎn),那么創(chuàng)建一個(gè)新的葉子結(jié)點(diǎn)leaf_child,這個(gè)葉子結(jié)點(diǎn)是構(gòu)建的索引結(jié)點(diǎn)的兒子結(jié)點(diǎn),調(diào)用這個(gè)葉子結(jié)點(diǎn)的init_restore(this, block)方法加載處于block位置的之前存儲(chǔ)的葉子結(jié)點(diǎn)的信息,并獲取這個(gè)結(jié)點(diǎn)的關(guān)鍵字并賦值給key。刪除葉子結(jié)點(diǎn)leaf_child,此時(shí)調(diào)用析構(gòu)函數(shù)將結(jié)點(diǎn)的信息寫入文件相應(yīng)的block位置中。
- 如果當(dāng)前所處層current_level大于1,說明正在構(gòu)建的索引結(jié)點(diǎn),其每個(gè)兒子結(jié)點(diǎn)也全都是索引結(jié)點(diǎn),那么創(chuàng)建一個(gè)新的索引結(jié)點(diǎn)index_child,這個(gè)索引結(jié)點(diǎn)是構(gòu)建的索引結(jié)點(diǎn)的兒子結(jié)點(diǎn),調(diào)用這個(gè)結(jié)點(diǎn)的init_restore(this, block)方法加載這個(gè)處于block位置的之前存儲(chǔ)的結(jié)點(diǎn)的信息,并獲取這個(gè)結(jié)點(diǎn)的關(guān)鍵字并賦值給key。刪除索引結(jié)點(diǎn)index_child,此時(shí)調(diào)用析構(gòu)函數(shù)將結(jié)點(diǎn)的信息寫入文件相應(yīng)的block位置中。
- 判斷此時(shí)正在進(jìn)行插入操作的索引結(jié)點(diǎn)index_act_nd是否為空:
- 如果為空,那么要?jiǎng)?chuàng)建一個(gè)新的索引結(jié)點(diǎn)index_act_nd,并使用它的初始化方法init(current_level, this)初始化這個(gè)索引結(jié)點(diǎn),設(shè)置這個(gè)結(jié)點(diǎn)所處的層數(shù)和所在B+樹,接著判斷這個(gè)索引結(jié)點(diǎn)是否是它所在這一層的第一個(gè)結(jié)點(diǎn)first_node:
- 如果是,則標(biāo)記此層起始點(diǎn)的位置start_block為這一個(gè)結(jié)點(diǎn)的位置,并把first_node置為false,使之后的索引結(jié)點(diǎn)都不是第一個(gè)索引結(jié)點(diǎn)
- 如果不是,那么標(biāo)記當(dāng)前正在插入的索引結(jié)點(diǎn)index_act_nd的左兄弟為上一個(gè)插入滿的索引結(jié)點(diǎn)index_prev_nd,上一個(gè)插入滿的索引結(jié)點(diǎn)index_prev_nd的右兄弟為當(dāng)前正在插入的索引結(jié)點(diǎn)index_act_nd,形成一個(gè)雙向鏈表。標(biāo)記最后一個(gè)結(jié)點(diǎn)的位置end_block為正在進(jìn)行插入的索引結(jié)點(diǎn)的位置
- 如果為空,那么要?jiǎng)?chuàng)建一個(gè)新的索引結(jié)點(diǎn)index_act_nd,并使用它的初始化方法init(current_level, this)初始化這個(gè)索引結(jié)點(diǎn),設(shè)置這個(gè)結(jié)點(diǎn)所處的層數(shù)和所在B+樹,接著判斷這個(gè)索引結(jié)點(diǎn)是否是它所在這一層的第一個(gè)結(jié)點(diǎn)first_node:
- 當(dāng)前正在插入的索引結(jié)點(diǎn)index_act_nd不為空后,使用語(yǔ)句index_act_nd->add_new_child(key, block)把關(guān)鍵字為key,位置為block的兒子結(jié)點(diǎn)插入進(jìn)這個(gè)索引結(jié)點(diǎn)中,插入完成后,如果當(dāng)前插入的索引結(jié)點(diǎn)已經(jīng)滿了,那標(biāo)記當(dāng)前插入的索引結(jié)點(diǎn)為上一個(gè)插入的結(jié)點(diǎn),當(dāng)前插入的索引結(jié)點(diǎn)為空,以便之后可以創(chuàng)建新的結(jié)點(diǎn)插入新的條目。遍歷完成,這一層的索引結(jié)點(diǎn)就構(gòu)建完成。
- 當(dāng)前層插入完成之后,更新之前層開始的位置last_start_block為插入完成后的當(dāng)前層的開始位置start_block,之前層結(jié)束的位置last_end_block為當(dāng)前層的位置end_block,層數(shù)加一,代表往上一層。
- 首先使此層第一個(gè)點(diǎn)first_node為true,從上一個(gè)開始的位置last_start_block開始,到上一個(gè)結(jié)束的位置last_end_block結(jié)束,進(jìn)入一個(gè)for循環(huán),使block為當(dāng)前位置
- 全部插入完成之后,更新根結(jié)點(diǎn)root_為上一個(gè)開始的位置last_start_block,刪除所用到的結(jié)點(diǎn)指針,此時(shí)調(diào)用析構(gòu)函數(shù)將結(jié)點(diǎn)的信息寫入文件相應(yīng)的block位置中,bulkloading加載過程完成。
算法并行的設(shè)計(jì)思路
從上面串行bulkloading過程可以知道,首先從最底層葉子結(jié)點(diǎn)開始,從左往右初始化葉子結(jié)點(diǎn)并構(gòu)建雙向鏈表,然后從下往上,逐層從左往右初始化索引結(jié)點(diǎn)并構(gòu)建雙向鏈表,直到根結(jié)點(diǎn),程序結(jié)束。
那么并行設(shè)計(jì)算法,就可以通過對(duì)每一層結(jié)點(diǎn)的位置和相應(yīng)存儲(chǔ)信息進(jìn)行計(jì)算,得到每一個(gè)結(jié)點(diǎn)需要添加的條目或兒子結(jié)點(diǎn)的信息,從而每一層的每個(gè)結(jié)點(diǎn)都可以并行地添加數(shù)據(jù),從而提高程序運(yùn)行效率。
葉子結(jié)點(diǎn)所添加的是條目,索引結(jié)點(diǎn)添加的是關(guān)鍵字和兒子結(jié)點(diǎn)的位置,分別需要使用不同的添加函數(shù)和初始化函數(shù),由于葉子結(jié)點(diǎn)在最底層,所以可以將程序分為兩個(gè)部分,先開始向葉子結(jié)點(diǎn)并行添加數(shù)據(jù),葉子結(jié)點(diǎn)全部構(gòu)建完成之后,再每層向上并行向索引結(jié)點(diǎn)添加數(shù)據(jù)。
對(duì)每一層進(jìn)行并行添加數(shù)據(jù),從下往上,下面一層構(gòu)建完成后,再向上一層的結(jié)點(diǎn)并行添加數(shù)據(jù),直到根結(jié)點(diǎn),程序結(jié)束。
算法流程圖
關(guān)鍵代碼描述
1. 初始化結(jié)點(diǎn)
每個(gè)結(jié)點(diǎn)都需要根據(jù)所處層調(diào)用init方法進(jìn)行初始化,在初始化方法中,需要完成確定結(jié)點(diǎn)的block位置(使用init方法)、設(shè)置結(jié)點(diǎn)的信息數(shù)據(jù)和添加條目或兒子結(jié)點(diǎn)的任務(wù),對(duì)于葉子結(jié)點(diǎn)和索引結(jié)點(diǎn),初始化方法有所不同,可以分成兩個(gè)函數(shù)進(jìn)行實(shí)現(xiàn)。
初始化葉子結(jié)點(diǎn):
// 初始化葉子結(jié)點(diǎn)所使用的參數(shù)結(jié)構(gòu)體 struct Init_Leaf_Node_Params {BLeafNode *first_leaf_node; // 葉子結(jié)點(diǎn)層的第一個(gè)結(jié)點(diǎn),如果這個(gè)初始化的葉子結(jié)點(diǎn)不是第一個(gè)結(jié)點(diǎn),這個(gè)參數(shù)為NULLBTree *btree; // 葉子結(jié)點(diǎn)所在B+樹int n; // 總的條目個(gè)數(shù)const Result *table; // 存儲(chǔ)條目信息的哈希表int leaf_node_capacity_entries; // 葉子結(jié)點(diǎn)可以存儲(chǔ)的最大條目數(shù)int leaf_node_capacity_keys; // 葉子結(jié)點(diǎn)可以存儲(chǔ)的最大關(guān)鍵字?jǐn)?shù)int leaf_node_num; // 葉子結(jié)點(diǎn)的總數(shù) };// 初始化葉子結(jié)點(diǎn) static void init_the_leaf_node(void *arg) {// 獲取參數(shù)Init_Leaf_Node_Params *init_params = (Init_Leaf_Node_Params *)arg;BLeafNode *first_leaf_node = init_params->first_leaf_node;BTree *btree = init_params->btree;int n = init_params->n;const Result *table = init_params->table;int leaf_node_capacity_entries = init_params->leaf_node_capacity_entries;int leaf_node_capacity_keys = init_params->leaf_node_capacity_keys;int leaf_node_num = init_params->leaf_node_num;int i, ret;// 聲明初始化的葉子結(jié)點(diǎn)BLeafNode *leaf_node;// 如果作為參數(shù)傳遞進(jìn)來(lái)的first_leaf_node為空if (first_leaf_node == NULL) {// 說明初始化的這個(gè)結(jié)點(diǎn)不是該層的第一個(gè)結(jié)點(diǎn),調(diào)用init方法進(jìn)行初始化leaf_node = new BLeafNode();// 調(diào)用init方法時(shí),會(huì)改變block的位置序號(hào),每次調(diào)用位置block加一,所以要加鎖leaf_node->init(0, btree);}// first_leaf_node不為空說明是第一個(gè)結(jié)點(diǎn),直接獲取結(jié)點(diǎn)else {leaf_node = first_leaf_node;}// 獲取結(jié)點(diǎn)位置int block = leaf_node->get_block();// 向葉子結(jié)點(diǎn)添加條目在哈希表中的起始位置int start_pos = (block - 1) * leaf_node_capacity_entries;// 向葉子結(jié)點(diǎn)添加條目在哈希表中的終點(diǎn)位置int end_pos = start_pos + leaf_node_capacity_entries;// 如果終點(diǎn)位置大于條目個(gè)數(shù),那么終點(diǎn)位置直接為條目的末尾nif (end_pos > n) {end_pos = n;}// 向這個(gè)葉子結(jié)點(diǎn)添加的總條目數(shù)int num_entries = end_pos - start_pos;// 要完成的任務(wù)數(shù)加上要添加的條目數(shù)task_num += num_entries;// 向這個(gè)葉子結(jié)點(diǎn)添加的關(guān)鍵字?jǐn)?shù)int num_keys;if ((num_entries * SIZEINT) % LEAF_NODE_SIZE == 0) {num_keys = (num_entries * SIZEINT) / LEAF_NODE_SIZE;} else {num_keys = (num_entries * SIZEINT) / LEAF_NODE_SIZE + 1;}assert(num_keys <= leaf_node_capacity_keys);// 并行添加條目的參數(shù)數(shù)組Add_Leaf_Node_Child_Params params[num_entries];i = 0;while (i < num_entries) {// 在數(shù)組中賦值并傳遞參數(shù),可保證參數(shù)地址固定,避免并行時(shí)參數(shù)出現(xiàn)錯(cuò)誤params[i].leaf_node = leaf_node;params[i].begin_index = i + start_pos;// 確定添加進(jìn)結(jié)點(diǎn)的末尾條目的位置if (i + THREAD_ADD_CHILD_NUM - 1 >= num_entries) {params[i].end_index = start_pos + num_entries - 1;}else {params[i].end_index = i + start_pos + THREAD_ADD_CHILD_NUM - 1;}params[i].table = table;params[i].start_pos = start_pos;// 將添加條目的線程加入任務(wù)隊(duì)列等待執(zhí)行addTask(&pools, add_leaf_node_child, (void *)¶ms[i]);i += THREAD_ADD_CHILD_NUM;}// 當(dāng)完成的任務(wù)數(shù)小于要完成的任務(wù)數(shù),等待完成while (task_num_finish < task_num) {sleep(0);}// 設(shè)置葉子結(jié)點(diǎn)含有的條目數(shù)、關(guān)鍵字?jǐn)?shù)、是臟塊刪除指針要寫回文件leaf_node->set_num_entries(num_entries);leaf_node->set_num_keys(num_keys);leaf_node->set_dirty(true);// 如果結(jié)點(diǎn)不是這層第一個(gè)結(jié)點(diǎn),設(shè)置它的左兄弟是前一個(gè)結(jié)點(diǎn)if (block > 1) {leaf_node->set_left_sibling(block - 1);}// 如果結(jié)點(diǎn)不是此層最后一個(gè)結(jié)點(diǎn),設(shè)置它的右兄弟是后一個(gè)結(jié)點(diǎn)if (block < leaf_node_num) {leaf_node->set_right_sibling(block + 1);}// 刪除結(jié)點(diǎn)指針,使其信息寫回文件if (leaf_node != NULL) {delete leaf_node;leaf_node = NULL;} }在初始化葉子結(jié)點(diǎn)的方法中,需要確定這個(gè)葉子結(jié)點(diǎn)添加的條目在哈希表中的起始位置,這樣就不用一個(gè)一個(gè)使用add_new_child方法進(jìn)行添加條目,而是可以并行化,將條目添加進(jìn)這個(gè)葉子結(jié)點(diǎn)中,一個(gè)線程也可以一次添加多個(gè)條目,不一定一次只有添加一個(gè)條目,一次添加的最大條目數(shù)由宏定義THREAD_ADD_CHILD_NUM控制。
添加進(jìn)這個(gè)葉子結(jié)點(diǎn)的條目在哈希表中的起始位置為這個(gè)葉子結(jié)點(diǎn)的位置block減一(葉子結(jié)點(diǎn)層第一個(gè)葉子結(jié)點(diǎn)位置block為1),乘以每個(gè)葉子結(jié)點(diǎn)可以添加的最大條目數(shù)leaf_node_capacity_entries,終點(diǎn)位置為開始位置加上每個(gè)葉子結(jié)點(diǎn)可以添加的最大條目數(shù)leaf_node_capacity_entries,如果終點(diǎn)位置大于條目個(gè)數(shù),那么終點(diǎn)位置直接為條目的末尾n,代碼如下:
// 向葉子結(jié)點(diǎn)添加條目在哈希表中的起始位置 int start_pos = (block - 1) * leaf_node_capacity_entries; // 向葉子結(jié)點(diǎn)添加條目在哈希表中的終點(diǎn)位置 int end_pos = start_pos + leaf_node_capacity_entries; // 如果終點(diǎn)位置大于條目個(gè)數(shù),那么終點(diǎn)位置直接為條目的末尾n if (end_pos > n) {end_pos = n; }// 向這個(gè)葉子結(jié)點(diǎn)添加的總條目數(shù) int num_entries = end_pos - start_pos;i = 0; while (i < num_entries) {// 在數(shù)組中賦值并傳遞參數(shù),可保證參數(shù)地址固定,避免并行時(shí)參數(shù)出現(xiàn)錯(cuò)誤params[i].leaf_node = leaf_node;params[i].begin_index = i + start_pos;// 確定添加進(jìn)結(jié)點(diǎn)的末尾條目的位置if (i + THREAD_ADD_CHILD_NUM - 1 >= num_entries) {params[i].end_index = start_pos + num_entries - 1;}else {params[i].end_index = i + start_pos + THREAD_ADD_CHILD_NUM - 1;}params[i].table = table;params[i].start_pos = start_pos;// 將添加條目的線程加入任務(wù)隊(duì)列等待執(zhí)行addTask(&pools, add_leaf_node_child, (void *)¶ms[i]);i += THREAD_ADD_CHILD_NUM; }在主函數(shù)中,使用for循環(huán)調(diào)用init_the_leaf_node方法,就可以實(shí)現(xiàn)從左到右對(duì)葉子結(jié)點(diǎn)進(jìn)行初始化。
初始化索引結(jié)點(diǎn):
// 初始化索引結(jié)點(diǎn)所使用的參數(shù)結(jié)構(gòu)體 struct Init_Index_Node_Params {BIndexNode *first_index_node; // 這個(gè)索引結(jié)點(diǎn)所在層的第一個(gè)結(jié)點(diǎn),如果這個(gè)初始化的索引結(jié)點(diǎn)不是該層第一個(gè)結(jié)點(diǎn),這個(gè)參數(shù)為NULLBTree *btree; // 葉子結(jié)點(diǎn)所在B+樹int last_start_block; // 索引結(jié)點(diǎn)下一層結(jié)點(diǎn)的開頭結(jié)點(diǎn)的block位置int last_end_block; // 索引結(jié)點(diǎn)下一層結(jié)點(diǎn)的結(jié)尾結(jié)點(diǎn)的block位置int current_level; // 索引結(jié)點(diǎn)所在層數(shù)int index_node_capacity_entries; // 索引結(jié)點(diǎn)可以存儲(chǔ)的最大兒子結(jié)點(diǎn)數(shù)int index_node_num_cur_level; // 索引結(jié)點(diǎn)所處層的索引結(jié)點(diǎn)數(shù) };// 初始化索引結(jié)點(diǎn) static void init_the_index_node(void *arg) {// 獲取參數(shù)Init_Index_Node_Params *init_params = (Init_Index_Node_Params *)arg;BIndexNode *first_index_node = init_params->first_index_node;BTree *btree = init_params->btree;int last_start_block = init_params->last_start_block;int last_end_block = init_params->last_end_block;int current_level = init_params->current_level;int index_node_capacity_entries = init_params->index_node_capacity_entries;int index_node_num_cur_level = init_params->index_node_num_cur_level;int i, ret;// 聲明初始化的索引結(jié)點(diǎn)BIndexNode *index_node;// 如果作為參數(shù)傳遞進(jìn)來(lái)的first_index_node為空if (first_index_node == NULL) {// 說明初始化的這個(gè)結(jié)點(diǎn)不是該層的第一個(gè)結(jié)點(diǎn),調(diào)用init方法進(jìn)行初始化index_node = new BIndexNode();index_node->init(current_level, btree);}// first_index_node不為空說明是第一個(gè)結(jié)點(diǎn),直接獲取結(jié)點(diǎn)else {index_node = first_index_node;}// 獲取結(jié)點(diǎn)位置int block = index_node->get_block();// 索引結(jié)點(diǎn)相對(duì)這一層的第一個(gè)結(jié)點(diǎn)的位置int block_index_cur_level = block - last_end_block - 1;// 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的block的起始位置int start_pos = last_start_block + index_node_capacity_entries * block_index_cur_level;// 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的block的終點(diǎn)位置int end_pos = start_pos + index_node_capacity_entries - 1;// 如果終點(diǎn)位置大于下一層結(jié)點(diǎn)個(gè)數(shù),那么終點(diǎn)位置直接為下一層末尾結(jié)點(diǎn)位置last_end_blockif (end_pos > last_end_block) {end_pos = last_end_block;}// 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的總數(shù)int num_entries = end_pos - start_pos + 1;// 要完成的任務(wù)數(shù)加上要添加的兒子結(jié)點(diǎn)數(shù)task_num += num_entries;// 添加兒子結(jié)點(diǎn)的參數(shù)數(shù)組Add_Index_Node_Child_Params params[num_entries];i = 0;while (i < num_entries) {// 在數(shù)組中賦值并傳遞參數(shù),可保證參數(shù)地址固定,避免并行時(shí)參數(shù)出現(xiàn)錯(cuò)誤params[i].index_node = index_node;params[i].begin_child_block = i + start_pos;// 確定添加進(jìn)結(jié)點(diǎn)的末尾兒子結(jié)點(diǎn)的位置if (i + THREAD_ADD_CHILD_NUM - 1 >= num_entries) {params[i].end_child_block = start_pos + num_entries - 1;}else {params[i].end_child_block = i + start_pos + THREAD_ADD_CHILD_NUM - 1;}params[i].start_pos = start_pos;params[i].cur_level = current_level;params[i].btree = btree;// 將添加兒子結(jié)點(diǎn)的線程加入任務(wù)隊(duì)列等待執(zhí)行addTask(&pools, add_index_node_child, (void *)¶ms[i]);i += THREAD_ADD_CHILD_NUM;}// 當(dāng)完成的任務(wù)數(shù)小于要完成的任務(wù)數(shù),等待完成while (task_num_finish < task_num) {sleep(0);}// 設(shè)置所以結(jié)點(diǎn)含有的兒子結(jié)點(diǎn)數(shù)、是臟塊刪除指針要寫回文件index_node->set_num_entries(num_entries);index_node->set_dirty(true);// 如果結(jié)點(diǎn)不是這層第一個(gè)結(jié)點(diǎn),設(shè)置它的左兄弟是前一個(gè)結(jié)點(diǎn)if (block_index_cur_level > 0) {index_node->set_left_sibling(block - 1);}// 如果結(jié)點(diǎn)不是此層最后一個(gè)結(jié)點(diǎn),設(shè)置它的右兄弟是后一個(gè)結(jié)點(diǎn)if (block_index_cur_level < index_node_num_cur_level - 1) {index_node->set_right_sibling(block + 1);}// 刪除結(jié)點(diǎn)指針,使其信息寫回文件if (index_node != NULL) {delete index_node;index_node = NULL;} }在初始化索引結(jié)點(diǎn)的方法中,需要確定這個(gè)索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的block位置,這樣就不用一個(gè)一個(gè)使用add_new_child方法進(jìn)行添加兒子結(jié)點(diǎn),而是可以并行化,將關(guān)鍵字和兒子結(jié)點(diǎn)添加進(jìn)這個(gè)索引結(jié)點(diǎn)中,一個(gè)線程也可以一次添加多個(gè)關(guān)鍵字和兒子結(jié)點(diǎn),不一定一次只添加一個(gè)關(guān)鍵字和兒子結(jié)點(diǎn),一次添加的兒子結(jié)點(diǎn)數(shù)由宏定義THREAD_ADD_CHILD_NUM控制。
添加進(jìn)這個(gè)索引結(jié)點(diǎn)的全部?jī)鹤咏Y(jié)點(diǎn)的起始block位置為這個(gè)索引結(jié)點(diǎn)在其所處層的相對(duì)位置(所處層第一個(gè)索引結(jié)點(diǎn)下標(biāo)為0)block_index_cur_level,乘以每個(gè)索引結(jié)點(diǎn)可以添加的最大兒子結(jié)點(diǎn)數(shù)index_node_capacity_entries,再加上上一層結(jié)點(diǎn)的起始位置last_start_block,終點(diǎn)位置為開始位置加上每個(gè)索引子結(jié)點(diǎn)可以添加的最大兒子結(jié)點(diǎn)數(shù)index_node_capacity_entries減一,如果終點(diǎn)位置大于上一層結(jié)點(diǎn)的末尾位置last_end_block,那么終點(diǎn)位置直接為上一層的末尾位置last_end_block,代碼如下:
// 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的block的起始位置 int start_pos = last_start_block + index_node_capacity_entries * block_index_cur_level; // 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的block的終點(diǎn)位置 int end_pos = start_pos + index_node_capacity_entries - 1; // 如果終點(diǎn)位置大于下一層結(jié)點(diǎn)個(gè)數(shù),那么終點(diǎn)位置直接為下一層末尾結(jié)點(diǎn)位置last_end_block if (end_pos > last_end_block) {end_pos = last_end_block; }// 向索引結(jié)點(diǎn)添加的兒子結(jié)點(diǎn)的總數(shù) int num_entries = end_pos - start_pos + 1;i = 0; while (i < num_entries) {// 在數(shù)組中賦值并傳遞參數(shù),可保證參數(shù)地址固定,避免并行時(shí)參數(shù)出現(xiàn)錯(cuò)誤params[i].index_node = index_node;params[i].begin_child_block = i + start_pos;// 確定添加進(jìn)結(jié)點(diǎn)的末尾兒子結(jié)點(diǎn)的位置if (i + THREAD_ADD_CHILD_NUM - 1 >= num_entries) {params[i].end_child_block = start_pos + num_entries - 1;}else {params[i].end_child_block = i + start_pos + THREAD_ADD_CHILD_NUM - 1;}params[i].start_pos = start_pos;params[i].cur_level = current_level;params[i].btree = btree;// 將添加兒子結(jié)點(diǎn)的線程加入任務(wù)隊(duì)列等待執(zhí)行addTask(&pools, add_index_node_child, (void *)¶ms[i]);i += THREAD_ADD_CHILD_NUM; }在主函數(shù)中,使用for循環(huán)調(diào)用init_the_index_node方法,就可以實(shí)現(xiàn)從左到右對(duì)索引結(jié)點(diǎn)進(jìn)行初始化。
2. 添加條目或兒子結(jié)點(diǎn)
向葉子結(jié)點(diǎn)添加條目:
// 并行向葉子結(jié)點(diǎn)添加條目所使用的參數(shù)結(jié)構(gòu)體 struct Add_Leaf_Node_Child_Params {BLeafNode *leaf_node; // 所要添加條目的葉子結(jié)點(diǎn)int begin_index; // 每次添加進(jìn)葉子結(jié)點(diǎn)的id數(shù)組的起始條目在哈希表中的下標(biāo)位置int end_index; // 每次添加進(jìn)葉子結(jié)點(diǎn)的id數(shù)組的末尾條目在哈希表中的下標(biāo)位置const Result *table; // 存儲(chǔ)條目信息的哈希表int start_pos; // 添加進(jìn)葉子結(jié)點(diǎn)的id數(shù)組的第一個(gè)條目在哈希表中的下標(biāo)位置 };// 向葉子結(jié)點(diǎn)添加條目所使用的線程函數(shù) static void *add_leaf_node_child(void *arg) {// 獲取參數(shù)Add_Leaf_Node_Child_Params *params = (Add_Leaf_Node_Child_Params *)arg;BLeafNode *leaf_node = params->leaf_node;int begin_index = params->begin_index;int end_index = params->end_index;const Result *table = params->table;int start_pos = params->start_pos;// 一次添加end_index - begin_index + 1個(gè)條目for (int i = begin_index; i <= end_index; ++i) {// 向葉子結(jié)點(diǎn)的id_數(shù)組的相應(yīng)下標(biāo)位置添加條目leaf_node->set_id(table[i].id_, i - start_pos);// 如果滿足一定的條件,向葉子結(jié)點(diǎn)的key_數(shù)組的相應(yīng)下標(biāo)位置添加關(guān)鍵字if (((i - start_pos) * SIZEINT) % LEAF_NODE_SIZE == 0) {leaf_node->set_key(table[i].key_, ((i - start_pos) * SIZEINT) / LEAF_NODE_SIZE);}}sleep(0);// 添加完成的任務(wù)數(shù)pthread_mutex_lock(&task_num_finish_mutex);task_num_finish += end_index - begin_index + 1;pthread_mutex_unlock(&task_num_finish_mutex); }向索引結(jié)點(diǎn)添加條目:
// 并行向索引結(jié)點(diǎn)添加關(guān)鍵字和兒子結(jié)點(diǎn)的位置所使用的參數(shù)結(jié)構(gòu)體 struct Add_Index_Node_Child_Params {BIndexNode *index_node; // 所要添加兒子結(jié)點(diǎn)的索引結(jié)點(diǎn)int begin_child_block; // 每次添加的起始兒子結(jié)點(diǎn)的block位置int end_child_block; // 每次添加的末尾兒子結(jié)點(diǎn)的block位置int start_pos; // 添加的兒子結(jié)點(diǎn)的block的起始位置int cur_level; // 索引結(jié)點(diǎn)層數(shù)BTree *btree; // 索引結(jié)點(diǎn)所在的B+樹 };// 向索引結(jié)點(diǎn)添加關(guān)鍵字和兒子結(jié)點(diǎn)的位置所使用的線程函數(shù) static void *add_index_node_child(void *arg) {// 獲取參數(shù)Add_Index_Node_Child_Params *params = (Add_Index_Node_Child_Params *)arg;BIndexNode *index_node = params->index_node;int begin_child_block = params->begin_child_block;int end_child_block = params->end_child_block;int start_pos = params->start_pos;int cur_level = params->cur_level;BTree *btree = params->btree;float key;if (cur_level == 1) {// 一次添加end_child_block - begin_child_block + 1個(gè)兒子結(jié)點(diǎn)for (int i = begin_child_block; i <= end_child_block; ++i) {BLeafNode *leaf_child = new BLeafNode();pthread_mutex_lock(&init_restore_mutex);leaf_child->init_restore(btree, i);pthread_mutex_unlock(&init_restore_mutex);// 獲取這個(gè)結(jié)點(diǎn)的關(guān)鍵字key = leaf_child->get_key_of_node();if (leaf_child != NULL) {delete leaf_child;leaf_child = NULL;}// 向索引結(jié)點(diǎn)添加關(guān)鍵字和兒子結(jié)點(diǎn)index_node->set_key(key, i - start_pos);index_node->set_son(i, i - start_pos);}} else {// 一次添加end_child_block - begin_child_block + 1個(gè)兒子結(jié)點(diǎn)for (int i = begin_child_block; i <= end_child_block; ++i) {BIndexNode *index_child = new BIndexNode();pthread_mutex_lock(&init_restore_mutex);index_child->init_restore(btree, i);pthread_mutex_unlock(&init_restore_mutex);// 獲取這個(gè)結(jié)點(diǎn)的關(guān)鍵字key = index_child->get_key_of_node();if (index_child != NULL) {delete index_child;index_child = NULL;}// 向索引結(jié)點(diǎn)添加關(guān)鍵字和兒子結(jié)點(diǎn)index_node->set_key(key, i - start_pos);index_node->set_son(i, i - start_pos);}}sleep(0);// 添加完成的任務(wù)數(shù)pthread_mutex_lock(&task_num_finish_mutex);task_num_finish += end_child_block - begin_child_block + 1;;pthread_mutex_unlock(&task_num_finish_mutex); }3. 構(gòu)建葉子結(jié)點(diǎn)層
// ----------------------------------------------------------------------------- // 加載葉子結(jié)點(diǎn)// ------------------------------------------------------------------------- // build leaf node from <_hashtable> (level = 0) // ------------------------------------------------------------------------- int start_block = 0; // position of first node int end_block = 0; // position of last node// 創(chuàng)建第一個(gè)葉子結(jié)點(diǎn) BLeafNode *first_leaf_node = new BLeafNode(); // 進(jìn)行初始化 first_leaf_node->init(0, this); // 獲取第一個(gè)葉子結(jié)點(diǎn)的位置作為開頭位置 start_block = first_leaf_node->get_block();// 通過創(chuàng)建的葉子結(jié)點(diǎn),獲取每個(gè)葉子結(jié)點(diǎn)可以添加的最大條目數(shù)和最大關(guān)鍵字?jǐn)?shù) int leaf_node_capacity_entries = first_leaf_node->get_capacity(); int leaf_node_capacity_keys = first_leaf_node->get_capacity_keys();// 通過最大條目數(shù),計(jì)算要?jiǎng)?chuàng)建的葉子結(jié)點(diǎn)數(shù) int leaf_node_num; if (n % leaf_node_capacity_entries == 0) {leaf_node_num = n / leaf_node_capacity_entries; } else {leaf_node_num = n / leaf_node_capacity_entries + 1; }// 初始化葉子結(jié)點(diǎn)的參數(shù)數(shù)組 Init_Leaf_Node_Params init_leaf_node_params[leaf_node_num]; pthread_t leaf_node_ptid[leaf_node_num]; for (i = 0; i < leaf_node_num; i++) {// 第一個(gè)初始化的葉子結(jié)點(diǎn),將之前創(chuàng)建的第一個(gè)葉子結(jié)點(diǎn)直接作為參數(shù)傳遞進(jìn)去if (i == 0) {init_leaf_node_params[i].first_leaf_node = first_leaf_node;}// 其它結(jié)點(diǎn)傳遞NULLelse {init_leaf_node_params[i].first_leaf_node = NULL;}// 設(shè)置相應(yīng)參數(shù)init_leaf_node_params[i].btree = this;init_leaf_node_params[i].table = table;init_leaf_node_params[i].n = n;init_leaf_node_params[i].leaf_node_capacity_entries = leaf_node_capacity_entries;init_leaf_node_params[i].leaf_node_capacity_keys = leaf_node_capacity_keys;init_leaf_node_params[i].leaf_node_num = leaf_node_num;// 對(duì)每個(gè)結(jié)點(diǎn)進(jìn)行初始化init_the_leaf_node((void *)&init_leaf_node_params[i]); }// 葉子結(jié)點(diǎn)層的末尾位置為開始位置加上葉子結(jié)點(diǎn)個(gè)數(shù)減一 end_block = start_block + leaf_node_num - 1;- 首先創(chuàng)建第一個(gè)葉子結(jié)點(diǎn),并使用它的初始化方法init(0,this)進(jìn)行初始化,然后使用get_block()方法獲取第一個(gè)葉子結(jié)點(diǎn)的位置,并將它作為開頭位置start_block。
- 然后,通過第一個(gè)葉子結(jié)點(diǎn)獲取每個(gè)葉子結(jié)點(diǎn)可以添加的最大條目數(shù)leaf_node_capacity_entries和最大關(guān)鍵字?jǐn)?shù)leaf_node_capacity_keys。并且根據(jù)最大條目數(shù),計(jì)算要?jiǎng)?chuàng)建的葉子結(jié)點(diǎn)數(shù):
- 如果并行 bulkload 過程bulkload_parallel輸入的條目數(shù)n能夠被葉子結(jié)點(diǎn)最大條目數(shù)整除,說明leaf_node_num個(gè)葉子結(jié)點(diǎn)可以剛好存儲(chǔ)n個(gè)條目,所需要?jiǎng)?chuàng)建的葉子結(jié)點(diǎn)數(shù)即為leaf_node_num。
- 否則需要增加一個(gè)葉子結(jié)點(diǎn)存儲(chǔ)多余的條目,即葉子結(jié)點(diǎn)數(shù)leaf_node_num = n / leaf_node_capacity_entries + 1。
- 確定葉子結(jié)點(diǎn)數(shù)后,遍歷創(chuàng)建初始化葉子結(jié)點(diǎn)的參數(shù)數(shù)組init_leaf_node_params
- 首先判斷當(dāng)前結(jié)點(diǎn)是否為第一個(gè)初始化的結(jié)點(diǎn)
- 如果當(dāng)前葉子結(jié)點(diǎn)是第一個(gè)初始化的葉子結(jié)點(diǎn),將之前創(chuàng)建的第一個(gè)葉子結(jié)點(diǎn)first_leaf_node作為參數(shù)傳遞給init_leaf_node_params[0]
- 其他結(jié)點(diǎn)均為NULL
- 接著對(duì)參數(shù)數(shù)組中的成員變量btree、table、n、leaf_node_capacity_entries、leaf_node_capacity_keys、leaf_node_num進(jìn)行賦值
- 最后,將賦值后的參數(shù)數(shù)組元素init_leaf_node_params[i]傳遞給函數(shù)init_the_leaf_node,用于葉子結(jié)點(diǎn)的初始化。
- 首先判斷當(dāng)前結(jié)點(diǎn)是否為第一個(gè)初始化的結(jié)點(diǎn)
- 葉子結(jié)點(diǎn)層的末尾位置為開始位置加上葉子結(jié)點(diǎn)個(gè)數(shù)減一,即end_block = start_block + leaf_node_num - 1;
4. 構(gòu)建索引結(jié)點(diǎn)部分
// ----------------------------------------------------------------------------- // 加載索引結(jié)點(diǎn)// ------------------------------------------------------------------------- // stop condition: lastEndBlock == lastStartBlock (only one node, as root) // ------------------------------------------------------------------------- int current_level = 1; // current level (leaf level is 0) int last_start_block = start_block; // build b-tree level by level int last_end_block = end_block; // build b-tree level by level// 當(dāng)之前層末尾位置大于之前層開頭位置時(shí),說明還沒有插入到根結(jié)點(diǎn)(last_start_block == last_end_block) while (last_end_block > last_start_block) {// 開始構(gòu)建每一層的索引結(jié)點(diǎn),首先創(chuàng)建每一層的第一個(gè)索引結(jié)點(diǎn)BIndexNode *first_index_node = new BIndexNode();// 進(jìn)行初始化first_index_node->init(current_level, this);// 獲取每一層第一個(gè)索引結(jié)點(diǎn)的位置作為該層開頭位置int block = first_index_node->get_block();start_block = block;// 通過創(chuàng)建的索引結(jié)點(diǎn),獲取每個(gè)索引結(jié)點(diǎn)可以添加的最大兒子結(jié)點(diǎn)數(shù)和最大關(guān)鍵字?jǐn)?shù)int index_node_capacity_entries = first_index_node->get_capacity();// 計(jì)算下一層的結(jié)點(diǎn)數(shù)量int node_num_last_level = last_end_block - last_start_block + 1;// 根據(jù)下一層的結(jié)點(diǎn)數(shù)量和最大兒子結(jié)點(diǎn)數(shù)計(jì)算這一層的結(jié)點(diǎn)數(shù)量int index_node_num_cur_level;if (node_num_last_level % index_node_capacity_entries == 0) {index_node_num_cur_level = node_num_last_level / index_node_capacity_entries;}else {index_node_num_cur_level = node_num_last_level / index_node_capacity_entries + 1;} // 初始化索引結(jié)點(diǎn)的參數(shù)數(shù)組Init_Index_Node_Params init_index_node_params[index_node_num_cur_level];pthread_t index_node_ptid[index_node_num_cur_level];for (i = 0; i < index_node_num_cur_level; i++) {// 此層第一個(gè)初始化的索引結(jié)點(diǎn),將之前創(chuàng)建的此層第一個(gè)索引結(jié)點(diǎn)直接作為參數(shù)傳遞進(jìn)去if (i == 0) {init_index_node_params[i].first_index_node = first_index_node;}// 其它結(jié)點(diǎn)傳遞NULLelse {init_index_node_params[i].first_index_node = NULL;}// 設(shè)置相應(yīng)參數(shù)init_index_node_params[i].btree = this;init_index_node_params[i].last_start_block = last_start_block;init_index_node_params[i].last_end_block = last_end_block;init_index_node_params[i].current_level = current_level;init_index_node_params[i].index_node_capacity_entries = index_node_capacity_entries;init_index_node_params[i].index_node_num_cur_level = index_node_num_cur_level;// 對(duì)每個(gè)結(jié)點(diǎn)進(jìn)行初始化init_the_index_node((void *)&init_index_node_params[i]);}// 此層索引結(jié)點(diǎn)的末尾位置為開始位置加上此層索引結(jié)點(diǎn)個(gè)數(shù)減一end_block = start_block + index_node_num_cur_level - 1;last_start_block = start_block;// update infolast_end_block = end_block; // build b-tree of higher level++current_level; }// 根結(jié)點(diǎn)的位置即上一層的開頭位置 root_ = last_start_block; // update the <root>- 當(dāng)之前層結(jié)束的位置last_end_block大于之前層開始的位置last_start_block時(shí),說明還沒有插入到根結(jié)點(diǎn)(last_start_block == last_end_block),進(jìn)入一個(gè)while循環(huán),每次循環(huán),從下往上構(gòu)建每一層的索引結(jié)點(diǎn):
- 首先創(chuàng)建每一層的第一個(gè)索引結(jié)點(diǎn)first_index_node,并使用init(current_level,this)進(jìn)行初始化。使用first_index_node獲取位置,并將第一個(gè)索引結(jié)點(diǎn)的位置作為當(dāng)前層的開頭位置start_block。
- 另外,通過get_capacity()方法,從創(chuàng)建的索引結(jié)點(diǎn)中,獲取可以添加的最大兒子結(jié)點(diǎn)數(shù)和最大關(guān)鍵字?jǐn)?shù),由于索引結(jié)點(diǎn)的兒子結(jié)點(diǎn)的指針數(shù)和關(guān)鍵字?jǐn)?shù)相等,所以使用index_node_capacity_entries代替兩者
- 然后,計(jì)算下一層的節(jié)點(diǎn)數(shù)量node_num_last_level,并根據(jù)下一層節(jié)點(diǎn)數(shù)量和最大兒子結(jié)點(diǎn)數(shù)計(jì)算當(dāng)前層的結(jié)點(diǎn)數(shù)量index_node_num_cur_level
- 確定結(jié)點(diǎn)數(shù)后,遍歷創(chuàng)建初始化索引結(jié)點(diǎn)的參數(shù)數(shù)組init_index_node_params:
- 首先判斷當(dāng)前結(jié)點(diǎn)是否為第一個(gè)初始化的結(jié)點(diǎn)
- 如果當(dāng)前索引結(jié)點(diǎn)是并行時(shí)第一個(gè)初始化的結(jié)點(diǎn),將之前創(chuàng)建的第一個(gè)索引結(jié)點(diǎn)first_index_node作為參數(shù)傳遞給init_index_node_params[0]
- 其他結(jié)點(diǎn)均為NULL
- 接著對(duì)參數(shù)數(shù)組中的成員變量btree、last_start_block、last_end_block、current_level、index_node_capacity_entries、index_node_num_cur_level進(jìn)行賦值
- 最后,將賦值后的參數(shù)數(shù)組元素init_index_node_params[i]傳遞給函數(shù)init_the_index_node,用于索引結(jié)點(diǎn)的初始化。
- 首先判斷當(dāng)前結(jié)點(diǎn)是否為第一個(gè)初始化的結(jié)點(diǎn)
- 此層索引結(jié)點(diǎn)的末尾位置為開始位置加上此索引結(jié)點(diǎn)個(gè)數(shù)減一,即end_block = start_block + index_node_num_cur_level - 1;
- 根結(jié)點(diǎn)的位置即上一層的開頭位置
5. 線程池
因?yàn)槊總€(gè)條目或兒子結(jié)點(diǎn)的添加都需要?jiǎng)?chuàng)建一個(gè)線程,如果每次都pthread_create要造成十分大的開銷,所以可以創(chuàng)建一個(gè)線程池,減少了在創(chuàng)建和退出線程時(shí)的消耗。
變量聲明:
#define THREADS_NUM 1000 // 線程池中的線程個(gè)數(shù) #define TASK_QUEUE_MAX_SIZE 50000 // 任務(wù)的等待隊(duì)列的最大長(zhǎng)度,等待隊(duì)列中的最大任務(wù)個(gè)數(shù)為長(zhǎng)度減一#define THREAD_ADD_CHILD_NUM 5 // 每個(gè)線程在添加條目或兒子結(jié)點(diǎn)時(shí)一次添加的最多個(gè)數(shù)// 任務(wù)完成數(shù)變量的鎖 pthread_mutex_t task_num_finish_mutex = PTHREAD_MUTEX_INITIALIZER; // 調(diào)用結(jié)點(diǎn)的init_restore方法時(shí)使用的鎖 pthread_mutex_t init_restore_mutex = PTHREAD_MUTEX_INITIALIZER;Threadpools pools; // 線程池 long long int task_num_finish = 0; // 完成的任務(wù)數(shù) long long int task_num = 0; // 要完成的任務(wù)數(shù)數(shù)據(jù)結(jié)構(gòu):
// 線程池中每個(gè)線程執(zhí)行的任務(wù)的結(jié)構(gòu)體 typedef struct {void *(*function)(void *); // 執(zhí)行函數(shù)void *arg; // 參數(shù) } Task;// 任務(wù)循環(huán)隊(duì)列的數(shù)據(jù)結(jié)構(gòu) typedef struct {Task tasks[TASK_QUEUE_MAX_SIZE]; // 任務(wù)隊(duì)列數(shù)組int front; // 隊(duì)首下標(biāo)int rear; // 隊(duì)尾下標(biāo) } TaskQueue;// 線程池?cái)?shù)據(jù)結(jié)構(gòu) typedef struct {pthread_t threads[THREADS_NUM]; // 線程數(shù)組TaskQueue taskQueue; // 任務(wù)隊(duì)列bool isEnd; // 程序是否結(jié)束,要銷毀線程池sem_t sem_mutex; // 互斥信號(hào)量 } Threadpools;任務(wù)執(zhí)行函數(shù):
// 線程池中每個(gè)線程執(zhí)行的任務(wù) static void *executeTask(void *arg) {// 向每個(gè)線程傳入的參數(shù)是線程池Threadpools *pools = (Threadpools *)arg;while (1) {// 等待互斥信號(hào)量大于0,防止臨界沖突,然后將互斥鎖信號(hào)量減一,繼續(xù)執(zhí)行sem_wait(&pools->sem_mutex);// 當(dāng)任務(wù)隊(duì)列為空時(shí)while (pools->taskQueue.front == pools->taskQueue.rear) {// 如果已經(jīng)沒有剩余任務(wù)要處理,那么退出線程if (pools->isEnd) {sem_post(&pools->sem_mutex);pthread_exit(NULL);}// 否則等待任務(wù)隊(duì)列中有任務(wù)后再取任務(wù)進(jìn)行執(zhí)行sleep(0); }// 獲取任務(wù)隊(duì)列隊(duì)首的任務(wù)Task task;int front = pools->taskQueue.front;task.function = pools->taskQueue.tasks[front].function;task.arg = pools->taskQueue.tasks[front].arg;// 循環(huán)隊(duì)列隊(duì)首下標(biāo)加一pools->taskQueue.front = (front + 1) % TASK_QUEUE_MAX_SIZE;// 將互斥鎖信號(hào)量加一,允許其它線程執(zhí)行sem_post(&pools->sem_mutex);// 執(zhí)行任務(wù)(*(task.function))(task.arg);} }- 由類型轉(zhuǎn)換獲得線程池參數(shù)pools,循環(huán)從任務(wù)隊(duì)列中取出任務(wù)執(zhí)行
- 首先使用sem_wait(&pools->sem_mutex)函數(shù)進(jìn)行互斥等待,防止臨界沖突
- 當(dāng)任務(wù)隊(duì)列為空,即pools->taskQueue.front == pools->taskQueue.rear隊(duì)尾和隊(duì)首相等時(shí)
- 如果已經(jīng)沒有剩余任務(wù)處理,則使用sem_post(&pools->sem_mutex)釋放信號(hào)量,并退出當(dāng)前線程
- 否則等待任務(wù)隊(duì)列有任務(wù)后再取出任務(wù)進(jìn)行執(zhí)行
- 任務(wù)隊(duì)列中不為空時(shí),將直接獲取隊(duì)首的任務(wù),具體操作為將任務(wù)參數(shù)function、arg賦值給task。此時(shí)任務(wù)隊(duì)列減少一個(gè)任務(wù),循環(huán)隊(duì)列隊(duì)首下標(biāo)加一。
- 取完任務(wù)后使用sem_post通知其他線程,開始執(zhí)行任務(wù)(*(task.function))(task.arg);
初始化線程池:
// 初始化線程池 void initThreadpools(Threadpools *pools) {int ret;// 任務(wù)隊(duì)列的隊(duì)首和隊(duì)尾的坐標(biāo)都為0pools->taskQueue.front = 0;pools->taskQueue.rear = 0;pools->isEnd = false;// 初始化互斥信號(hào)量為0ret = sem_init(&pools->sem_mutex, 0, 1);if (ret == -1) {perror("sem_init-mutex");exit(1);}// 創(chuàng)建線程池中的線程for(int i = 0; i < THREADS_NUM; ++i) {ret = pthread_create(&pools->threads[i], NULL, executeTask, (void *)pools);if(ret != 0) {fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}} }- 初始化線程池pools,將線程池任務(wù)隊(duì)列taskQueue的隊(duì)首隊(duì)尾下標(biāo)置為 0,并將線程池銷毀標(biāo)志置為false,同時(shí),使用sem_init函數(shù)初始化互斥信號(hào)量sem_mutex。
- 通過循環(huán),使用pthread_create創(chuàng)建THREADS_NUM個(gè)線程在線程池中待命
向任務(wù)隊(duì)列中添加任務(wù):
// 向任務(wù)隊(duì)列中添加任務(wù) void addTask(Threadpools *pools, void *(*function)(void *arg), void *arg) {// 當(dāng)任務(wù)隊(duì)列為滿時(shí),等待有任務(wù)被取出任務(wù)隊(duì)列不為滿再加入隊(duì)列while ((pools->taskQueue.rear + TASK_QUEUE_MAX_SIZE + 1 - pools->taskQueue.front) % TASK_QUEUE_MAX_SIZE == 0) {sleep(0);}// 向任務(wù)隊(duì)列的隊(duì)尾加入任務(wù)Task task;task.function = function;task.arg = arg;int rear = pools->taskQueue.rear;pools->taskQueue.tasks[rear] = task;// 任務(wù)隊(duì)列隊(duì)尾下標(biāo)加一pools->taskQueue.rear = (rear + 1) % (TASK_QUEUE_MAX_SIZE); }- 首先根據(jù)任務(wù)隊(duì)列是否為滿進(jìn)行等待,有任務(wù)被取出任務(wù)隊(duì)列,任務(wù)隊(duì)列不為滿時(shí),再將新的任務(wù)加入任務(wù)隊(duì)列
- 任務(wù)隊(duì)列增加任務(wù)的方式是在任務(wù)隊(duì)列隊(duì)尾插入新任務(wù),增加新任務(wù)task后,任務(wù)隊(duì)列隊(duì)尾下標(biāo)加一pools->taskQueue.rear = (rear + 1) % (TASK_QUEUE_MAX_SIZE)
6. 主函數(shù)
在主函數(shù)中,設(shè)計(jì)了程序使輸入不同命令程序執(zhí)行不同的結(jié)果:
if (nargs == 1) {if (trees_->bulkload(n_pts_, table)) return 1; } else if (nargs > 1) {if (!strcmp(args[1], "print")) {if (trees_->bulkload(n_pts_, table)) return 1;print_tree(trees_);}if (!strcmp(args[1], "parallel")) {if (trees_->bulkload_parallel(n_pts_, table)) return 1;if (nargs > 2 && !strcmp(args[2], "print")) {print_tree(trees_);}} }輸入./run執(zhí)行串行加載;
輸入./run parallel執(zhí)行并行加載;
輸入./run print執(zhí)行串行加載并打印B+樹;
輸入./run parallel print執(zhí)行并行加載并打印B+樹。
打印B+樹使用的是遞歸算法:
void recursive_print(BTree *btree, int level, int block, string space) {if (level == 0) {BLeafNode *leaf_node = new BLeafNode();leaf_node->init_restore(btree, block);int key_index = 0;int num_entries = leaf_node->get_num_entries();printf("%sblock: %d (leaf node)\n", space.c_str(), block);for (int i = 0; i < num_entries; ++i) {if ((i * SIZEINT) % LEAF_NODE_SIZE == 0) {printf("%s ", space.c_str());int key = leaf_node->get_key(key_index);printf("key:%d\n", key);key_index++;}printf("%s ", space.c_str());printf("id:%d\n", leaf_node->get_entry_id(i));}delete leaf_node;leaf_node = NULL;}else {BIndexNode *index_node = new BIndexNode();index_node->init_restore(btree, block);int num_entries = index_node->get_num_entries();printf("%sblock: %d (index node)\n", space.c_str(), block);for (int i = 0; i < num_entries; ++i) {printf("%s ", space.c_str());int key = index_node->get_key(i);printf("key:%d son_block:%d\n", key, index_node->get_son(i));recursive_print(btree, level - 1, index_node->get_son(i), space + " ");}delete index_node;index_node = NULL;} }void print_tree(BTree *btree) {int root_block = btree->root_;BNode *root_ptr;if (root_block == 1) {root_ptr = new BLeafNode();}else {root_ptr = new BIndexNode();}root_ptr->init_restore(btree, root_block);int level = root_ptr->get_level();printf("root\n");recursive_print(btree, level, root_block, "");delete root_ptr;root_ptr = NULL; }實(shí)驗(yàn)結(jié)果與實(shí)驗(yàn)分析
驗(yàn)證輸出正確
設(shè)置點(diǎn)的個(gè)數(shù)都為1000000,在終端分別輸入./run print > test1.txt和./run parallel print > test2.txt,將串行加載和并行加載打印出的樹輸出到test1.txt文件和test2.txt文件中,然后使用vscode的文件比較功能進(jìn)行比較:
可以看到,除了開頭因?yàn)槭遣⑿谐绦蚨啻蛴〕龅木€程數(shù)量和每個(gè)線程處理的最大任務(wù)數(shù),和結(jié)尾運(yùn)行時(shí)間不同,中間打印出的樹的數(shù)據(jù)完全相同,測(cè)試多次仍然是同樣的結(jié)果,說明并行輸出正確。
串行加載與并行加載結(jié)果分析
改變點(diǎn)的個(gè)數(shù),對(duì)串行加載和并行加載的時(shí)間進(jìn)行比較,其中并行加載的線程池中的線程個(gè)數(shù)為1000,每個(gè)線程可以添加的條目或兒子結(jié)點(diǎn)的最大數(shù)目為
40,比較如下:
點(diǎn)的個(gè)數(shù)為100:
點(diǎn)的個(gè)數(shù)為1000:
點(diǎn)的個(gè)數(shù)為10000:
點(diǎn)的個(gè)數(shù)為100000:
點(diǎn)的個(gè)數(shù)為1000000:
| 100 | 0.00237 | 0.086792 |
| 1000 | 0.000623 | 0.113164 |
| 10000 | 0.003443 | 0.160166 |
| 100000 | 0.018719 | 0.893055 |
| 1000000 | 0.147339 | 7.018940 |
可以看到,雖然理論上講,并行的效率比串行要高,但是因?yàn)椴⑿袆?chuàng)建線程的消耗太大,所以在實(shí)驗(yàn)中,并行的效率并沒有特別的高,雖然代碼是按照并行設(shè)計(jì)的。
以下數(shù)據(jù)均在 one thread add child num = 40 時(shí)測(cè)得
性能調(diào)優(yōu)、創(chuàng)新優(yōu)化
性能調(diào)優(yōu)
理論上,可以通過增大線程池中的線程數(shù)目,使更多的任務(wù)可以被線程同時(shí)執(zhí)行,或者調(diào)整一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù),來(lái)調(diào)優(yōu)性能。
固定結(jié)點(diǎn)數(shù)為1000000、線程池中的線程數(shù)為1000,調(diào)整一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為10時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為20時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為30時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為40時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為50時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為60時(shí):
一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為80時(shí):
| 10 | 7.710704 | 7.691538 |
| 20 | 7.593454 | 7.326895 |
| 30 | 7.230909 | 7.036579 |
| 40 | 6.861761 | 6.745689 |
| 50 | 6.917748 | 6.871345 |
| 60 | 7.124508 | 7.094536 |
| 80 | 7.316152 | 7.213579 |
固定結(jié)點(diǎn)數(shù)為1000000、一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為40,調(diào)整線程池中的線程數(shù):
線程池中的線程數(shù)為10時(shí):
線程池中的線程數(shù)為50時(shí):
線程池中的線程數(shù)為100時(shí):
線程池中的線程數(shù)為200時(shí):
線程池中的線程數(shù)為500時(shí):
線程池中的線程數(shù)為1000時(shí):
| 10 | 7.387442 | 7.231549 |
| 50 | 6.737798 | 6.845321 |
| 100 | 5.607697 | 5.745186 |
| 200 | 6.725368 | 6.712356 |
| 500 | 6.880218 | 6.865419 |
| 1000 | 7.010874 | 7.010768 |
可以看到,對(duì)于點(diǎn)的個(gè)數(shù)為1000000時(shí),性能最優(yōu)時(shí)的搭配為線程池中的線程數(shù)為100,一個(gè)線程可以執(zhí)行的任務(wù)的最大個(gè)數(shù)為40,這樣性能最優(yōu)。
創(chuàng)新優(yōu)化
對(duì)于B+數(shù)的并行加載,由于需要對(duì)條目或兒子結(jié)點(diǎn)的添加創(chuàng)建線程,當(dāng)條目數(shù)為初始給定的1000000時(shí),光是向葉子結(jié)點(diǎn)添加條目就要?jiǎng)?chuàng)建1000000個(gè)線程,對(duì)程序來(lái)說是極大的開銷,所以想到,將單純的創(chuàng)建線程改為使用線程池,如果使用線程池,就可以避免很大部分的創(chuàng)建線程的花費(fèi),從而提升效率。線程池的代碼已在前面提到。
另外如果一個(gè)線程一次只添加一個(gè)條目或葉子結(jié)點(diǎn),就會(huì)顯得有些浪費(fèi),所以在優(yōu)化時(shí),設(shè)計(jì)了一個(gè)線程一次可以添加多個(gè)任務(wù),由宏定義THREAD_ADD_CHILD_NUM進(jìn)行控制,這樣一來(lái),測(cè)試發(fā)現(xiàn)在一定程度上也提高了程序的效率。
總結(jié)
以上是生活随笔為你收集整理的使用pthread和线程池实现B+树的并行块加载bulkload过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Lex工具进行tiny+语言的词法分
- 下一篇: webbench源码解析