本發(fā)明屬于并行算法技術(shù)領(lǐng)域,具體涉及到一種多核環(huán)境下基于數(shù)組結(jié)構(gòu)的無(wú)等待棧操作方法。
背景技術(shù):
隨著多核處理器的迅猛普及,計(jì)算機(jī)編程模式由傳統(tǒng)串行編程模式向線(xiàn)程級(jí)并行編程模式轉(zhuǎn)變,以發(fā)揮出與核數(shù)量的增長(zhǎng)相一致的實(shí)際效果。共享數(shù)據(jù)結(jié)構(gòu)(例如:棧和隊(duì)列)作為線(xiàn)程間通信的常用手段,是多線(xiàn)程應(yīng)用的重要組成結(jié)構(gòu)。為了確保多線(xiàn)程應(yīng)用高效運(yùn)行于多核平臺(tái),需要設(shè)計(jì)高性能的并發(fā)數(shù)據(jù)結(jié)構(gòu)。此外,進(jìn)度保障是并發(fā)數(shù)據(jù)結(jié)構(gòu)需要提供的另一大特性。并發(fā)數(shù)據(jù)結(jié)構(gòu)通常可分為阻塞和非阻塞這兩大類(lèi)。如果線(xiàn)程對(duì)阻塞數(shù)據(jù)結(jié)構(gòu)進(jìn)行操作,它可能首先需要等待另一個(gè)線(xiàn)程操作的完成。因此,阻塞數(shù)據(jù)結(jié)構(gòu)會(huì)引發(fā)諸如死鎖和優(yōu)先級(jí)反轉(zhuǎn)等問(wèn)題。
非阻塞數(shù)據(jù)結(jié)構(gòu)可以確保被暫停的操作不會(huì)阻塞其它操作,從而避免上述阻塞數(shù)據(jù)結(jié)構(gòu)所帶來(lái)的問(wèn)題。非阻塞數(shù)據(jù)結(jié)構(gòu)提供以下三個(gè)層次的進(jìn)度保障:
無(wú)阻礙:保障系統(tǒng)中一個(gè)孤立運(yùn)行的線(xiàn)程能夠在有限步驟內(nèi)完成任意操作;
無(wú)鎖:保障系統(tǒng)中若干線(xiàn)程能夠在有限步驟內(nèi)完成任意操作;
無(wú)等待:保障系統(tǒng)中每個(gè)線(xiàn)程都能夠在有限步驟內(nèi)完成任意操作。
無(wú)等待是并發(fā)數(shù)據(jù)結(jié)構(gòu)可以提供的最強(qiáng)進(jìn)度保障,該特性對(duì)于多線(xiàn)程應(yīng)用和操作系統(tǒng)來(lái)說(shuō)彌足珍貴,尤其是當(dāng)它們運(yùn)行于高爭(zhēng)用環(huán)境下的時(shí)候(例如:云計(jì)算環(huán)境)。但是,無(wú)等待數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)往往比較復(fù)雜,且性能較低下。例如,當(dāng)前表現(xiàn)最好的無(wú)等待棧算法的執(zhí)行速度,甚至低于一種基于結(jié)合技術(shù)的阻塞棧算法。
技術(shù)實(shí)現(xiàn)要素:
本發(fā)明公開(kāi)了一種多核環(huán)境下基于數(shù)組結(jié)構(gòu)的無(wú)等待棧操作方法,其目的在于保持高效執(zhí)行的前提下,為線(xiàn)程的操作提供無(wú)等待的進(jìn)度保障。棧的基本操作包括入棧、出棧、取棧頂元素、判斷空等;其中,僅入棧和出棧操作會(huì)對(duì)棧進(jìn)行修改;因此,與現(xiàn)有并行棧算法類(lèi)似,本發(fā)明僅關(guān)注入棧和出棧操作;本領(lǐng)域的技術(shù)人員容易理解,本發(fā)明經(jīng)過(guò)簡(jiǎn)單修改就能支持棧的其它操作。
本發(fā)明公開(kāi)的多核環(huán)境下基于數(shù)組結(jié)構(gòu)的無(wú)等待棧操作方法總體流程如下:
(1)主程序初始化代表?xiàng)5娜謹(jǐn)?shù)組,即分配一個(gè)包含N個(gè)數(shù)組元素的段,用于后續(xù)存放入棧數(shù)據(jù);其中,每個(gè)數(shù)組元素包含用于存儲(chǔ)入棧數(shù)據(jù)的變量val,指向入棧請(qǐng)求的指針push,以及指向出棧請(qǐng)求的指針pop,push和pop的初始值均為0;
(2)設(shè)置初始值為0的全局共享變量T和pc;其中,T為棧頂索引,pc用于指示后續(xù)出棧請(qǐng)求的標(biāo)識(shí)符;
(3)啟動(dòng)m個(gè)線(xiàn)程,每個(gè)線(xiàn)程thi(i=0,1,2,…,m-1)維護(hù)一個(gè)handle結(jié)構(gòu)類(lèi)型的變量hi來(lái)存儲(chǔ)自己的運(yùn)行狀態(tài),該結(jié)構(gòu)還包含指針next、入棧伙伴指針和出?;锇橹羔?;其中,每個(gè)線(xiàn)程的入棧伙伴指針和出?;锇橹羔樉跏贾赶虮揪€(xiàn)程;
(4)利用線(xiàn)程handle結(jié)構(gòu)類(lèi)型變量中的指針next,將所有線(xiàn)程的運(yùn)行狀態(tài)鏈接為環(huán)狀,即hi(i=0,1,2,…,m-2)的next指針指向hi+1,hm-1的next指針指向h0,使任意線(xiàn)程可以訪(fǎng)問(wèn)到其它線(xiàn)程的運(yùn)行狀態(tài);
(5)主程序等待接收線(xiàn)程對(duì)棧進(jìn)行操作的請(qǐng)求;
(6)當(dāng)任意線(xiàn)程thi(i=0,1,2,…,m-1)要對(duì)棧進(jìn)行操作時(shí),如果thi的操作請(qǐng)求為入棧請(qǐng)求,thi執(zhí)行無(wú)等待入棧操作;如果thi的操作請(qǐng)求為出棧請(qǐng)求,thi執(zhí)行無(wú)等待出棧操作;如果thi的操作請(qǐng)求為棧的銷(xiāo)毀請(qǐng)求,主程序會(huì)首先銷(xiāo)毀棧,然后包括主程序在內(nèi)的所有線(xiàn)程結(jié)束執(zhí)行;對(duì)于棧的銷(xiāo)毀請(qǐng)求之外的情況,主程序立刻繼續(xù)步驟(5)。
本發(fā)明提供的無(wú)等待入棧操作會(huì)首先利用比較并交換(Compare and Swap,CAS)原子操作,嘗試將入棧數(shù)據(jù)直接存入棧頂元素的val變量,當(dāng)存入失敗的次數(shù)超過(guò)給定閾值P后,會(huì)在本線(xiàn)程的運(yùn)行狀態(tài)中公布本次入棧請(qǐng)求;其它線(xiàn)程會(huì)在執(zhí)行無(wú)等待出棧操作時(shí)的必要情況下,嘗試為該入棧請(qǐng)求指定合適的入棧位置,以協(xié)助該入棧請(qǐng)求的完成;任意線(xiàn)程thi執(zhí)行無(wú)等待入棧操作的具體步驟如下:
(2-1)利用獲取并自加(Fetch and add,FAA)原子操作,對(duì)棧頂索引T進(jìn)行原子加一,獲取入棧位置索引i,即執(zhí)行i=FAA(&T,1);設(shè)置初始值為0的變量push_failures,用于指示將v存入全局?jǐn)?shù)組失敗的次數(shù);
(2-2)如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于i+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于i+1;在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素,并利用CAS操作,嘗試將入棧數(shù)據(jù)v存入c的val變量;當(dāng)且僅當(dāng)c的val變量尚未被其它入?;虺鰲2僮鞔嫒霐?shù)據(jù)時(shí),v才能被成功存入c的val變量;其中,索引為i的元素指全局?jǐn)?shù)組中第i+1個(gè)元素;
(2-3)判斷數(shù)據(jù)v是否被成功存入c的val變量,如果是,結(jié)束本次入棧操作;否則,執(zhí)行push_failures=push_failures+1,進(jìn)入步驟(2-4);
(2-4)判斷push_failures是否大于閾值P,如果是,在本線(xiàn)程thi的運(yùn)行狀態(tài)中公布本次入棧請(qǐng)求r,并將r的標(biāo)識(shí)符設(shè)置為入棧位置索引i的當(dāng)前值,用于確保v不會(huì)被存入索引小于當(dāng)前i值的元素,從而滿(mǎn)足棧的后進(jìn)先出語(yǔ)義,進(jìn)入步驟(2-5);否則,轉(zhuǎn)步驟(2-1);
(2-5)執(zhí)行i=FAA(&T,1),即獲取入棧位置索引i;
(2-6)如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于i+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于i+1;在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素,并利用CAS操作,嘗試將c的push指針從0改為指向r;當(dāng)且僅當(dāng)c的push指針值為0時(shí),才會(huì)成功指向r;
(2-7)判斷c的push指針是否指向r,如果是,嘗試將c指定為r的入棧位置;否則,轉(zhuǎn)步驟(2-9);
(2-8)判斷c是否指定為r的入棧位置,如果是,轉(zhuǎn)步驟(2-10);
(2-9)判斷r是否已有指定元素進(jìn)行入棧,如果是,進(jìn)入步驟(2-10);否則,轉(zhuǎn)步驟(2-5);
(2-10)將v存入r被指定的入棧元素的val變量中,結(jié)束本次入棧操作。
本發(fā)明提供的無(wú)等待出棧操作會(huì)首先讀取棧頂索引,然后從該索引所指的位置開(kāi)始,朝索引值遞減的方向依次嘗試從相應(yīng)元素出棧,當(dāng)出棧失敗的次數(shù)超過(guò)給定閾值P后,會(huì)在本線(xiàn)程的運(yùn)行狀態(tài)中公布本次出棧請(qǐng)求;其它線(xiàn)程會(huì)在執(zhí)行無(wú)等待出棧操作時(shí)的必要情況下,嘗試為該出棧請(qǐng)求指定合適的出棧位置,以協(xié)助該出棧請(qǐng)求的完成;任意線(xiàn)程thi執(zhí)行無(wú)等待出棧操作的具體步驟如下:
(3-1)讀取棧頂索引的值,并將該值存入一變量t;如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于t+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于t+1;在全局?jǐn)?shù)組中查找索引為t的元素,并將變量c設(shè)置為該元素;
(3-2)設(shè)置初始值為0的變量pop_failures,用于記錄出棧失敗的次數(shù);
(3-3)判斷c是否為棧底,如果是,返回空,結(jié)束本次出棧操作;否則,首先執(zhí)行t=t-1,然后在全局?jǐn)?shù)組中查找索引為t的元素,并將c設(shè)置為該元素;
(3-4)將c作為快速出棧函數(shù)的輸入?yún)?shù),調(diào)用快速出棧函數(shù);當(dāng)且僅當(dāng)快速出棧函數(shù)的返回值為1時(shí),本次出棧操作才能從c中出棧;
(3-5)判斷快速出棧函數(shù)的返回值是否為1,如果是,轉(zhuǎn)步驟(3-11);否則,執(zhí)行pop_failures=pop_failures+1;
(3-6)判斷pop_failures是否大于P,如果是,在本線(xiàn)程thi的運(yùn)行狀態(tài)中公布本次出棧請(qǐng)求q,并在q中記錄c及其索引值,q的標(biāo)識(shí)符通過(guò)對(duì)共享變量pc進(jìn)行原子加一操作來(lái)獲取,即q的標(biāo)識(shí)符=FAA(&pc,1);否則,轉(zhuǎn)步驟(3-3);
(3-7)將本線(xiàn)程thi的handle結(jié)構(gòu)類(lèi)型的變量h作為幫助出棧函數(shù)的輸入?yún)?shù),調(diào)用幫助出棧函數(shù);當(dāng)幫助出棧函數(shù)返回時(shí),當(dāng)前線(xiàn)程thi可以從q中獲取最終出棧結(jié)果;
(3-8)判斷出棧結(jié)果是否為空,如果是,返回空,結(jié)束本次出棧操作;否則,從q中讀取指定出棧元素,并將c設(shè)置為該元素;當(dāng)前線(xiàn)程thi可以從q中獲取最終出棧結(jié)果,分為兩種情況:(1)返回空;(2)有指定出棧元素。
(3-9)統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n;
(3-10)判斷n是否等于N,如果是,刪除s然后進(jìn)行步驟(3-11),否則直接進(jìn)行步驟(3-11);其中,N為s所包含的數(shù)組元素的總數(shù);
(3-11)假定當(dāng)前出?;锇橹羔?biāo)赶虻木€(xiàn)程為th,將th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量h作為幫助出棧函數(shù)的輸入?yún)?shù),調(diào)用幫助出棧函數(shù);
(3-12)假定h的next指針指向h’,將線(xiàn)程thi的出棧伙伴指針指向h’對(duì)應(yīng)的線(xiàn)程;
(3-13)返回c的val變量存放的值,即完成出棧,結(jié)束本次出棧操作。
進(jìn)一步,無(wú)等待出棧操作的特征在于,步驟(3-4)需要調(diào)用快速出棧函數(shù),該函數(shù)包括如下步驟:
(4-1)讀取本函數(shù)的輸入元素c,將c作為幫助入棧函數(shù)的輸入?yún)?shù),調(diào)用幫助入棧函數(shù);
(4-2)競(jìng)爭(zhēng)c的出棧權(quán),即利用CAS操作,嘗試將c的pop指針從0改為-1;如果CAS操作的返回值非0,說(shuō)明競(jìng)爭(zhēng)成功;
(4-3)判斷競(jìng)爭(zhēng)是否成功,如果是,統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n,執(zhí)行步驟(4-4);否則,從c中出棧失敗,返回0,結(jié)束快速出棧函數(shù)的執(zhí)行;
(4-4)判斷n是否等于N,如果是,刪除s;其中,N為s所包含的數(shù)組元素的總數(shù);
(4-5)判斷c的val變量是否存放有效值,如果是,則從c中出棧成功,返回1;否則,從c中出棧失敗,返回0。
進(jìn)一步,無(wú)等待出棧操作的特征在于,步驟(3-7)和步驟(3-11)需要調(diào)用幫助出棧函數(shù),該函數(shù)包括如下步驟:
(5-1)讀取本函數(shù)的輸入?yún)?shù)h;
(5-2)通過(guò)讀取h中存儲(chǔ)的運(yùn)行狀態(tài),判斷與h對(duì)應(yīng)的線(xiàn)程是否存在需要幫助的出棧請(qǐng)求q,如果是,進(jìn)入步驟(5-3);否則,結(jié)束幫助出棧函數(shù)的執(zhí)行;
(5-3)讀取q中記錄的元素c和c的索引,并將c的索引賦值給變量i;
(5-4)判斷c是否為棧底,如果是,嘗試指定q的出棧結(jié)果為返回空,結(jié)束幫助出棧函數(shù)的執(zhí)行;否則,執(zhí)行i=i-1,在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素;
(5-5)將c作為幫助入棧函數(shù)的輸入?yún)?shù),調(diào)用幫助入棧函數(shù);
(5-6)競(jìng)爭(zhēng)c的出棧權(quán),即利用CAS操作,嘗試將c的pop指針從0改為指向q,并使用變量ret保存CAS操作的返回值;
(5-7)判斷c的pop指針是否指向q,如果是,進(jìn)入步驟(5-8);否則,轉(zhuǎn)步驟(5-11);
(5-8)判斷c的val變量是否存放有效值,如果是,嘗試指定q從c中出棧,結(jié)束幫助出棧函數(shù)的執(zhí)行;
(5-9)判斷ret是否為0,如果是,轉(zhuǎn)步驟(5-11);否則,統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n;
(5-10)判斷n是否等于N,如果是,刪除s,然后執(zhí)行步驟(5-11);如果n不等于N,則執(zhí)行(5-11);
(5-11)判斷q是否已被指定出棧結(jié)果,如果是,結(jié)束幫助出棧函數(shù)的執(zhí)行;否則,轉(zhuǎn)步驟(5-4)。
進(jìn)一步,快速出棧函數(shù)和幫助出棧函數(shù)的特征在于,步驟(4-1)和步驟(5-5)需要調(diào)用幫助入棧函數(shù),該函數(shù)包括如下步驟:
(6-1)讀取本函數(shù)的輸入元素c,如果c的val變量中尚未存放有效值,則利用CAS操作,嘗試將無(wú)效值存入c的val變量中,使c成為無(wú)用元素;
(6-2)判斷c的val變量是否存放有效值,如果是,結(jié)束幫助入棧函數(shù)的執(zhí)行;
(6-3)判斷c的push指針是否已指向某入棧請(qǐng)求,如果是,轉(zhuǎn)步驟(6-11);否則,讀取本線(xiàn)程thi的入?;锇橹羔?biāo)赶虻木€(xiàn)程,并將變量th設(shè)置為該線(xiàn)程;
(6-4)判斷本線(xiàn)程thi是否在本周期內(nèi)嘗試幫助過(guò)th入棧,并且相應(yīng)入棧請(qǐng)求已完成,如果否,則直接進(jìn)行步驟(6-5);如果是,則假定th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量為h,h的next指針指向h’,將th設(shè)置為h’對(duì)應(yīng)的線(xiàn)程,并使本線(xiàn)程thi的入?;锇橹羔樦赶騮h;
(6-5)判斷th是否有需要幫助的入棧請(qǐng)求r’,如果是,進(jìn)入步驟(6-6);否則,轉(zhuǎn)步驟(6-8);
(6-6)判斷r’的標(biāo)識(shí)符是否小于或等于c的索引,如果是,嘗試將c預(yù)留給r’,即利用CAS操作,嘗試將c的push指針從0改為指向r’,若成功,CAS操作的返回值為非0;否則,轉(zhuǎn)步驟(6-8);
(6-7)判斷是否成功將c預(yù)留給r’,如果是,進(jìn)入步驟(6-8);否則,轉(zhuǎn)步驟(6-9);
(6-8)假定th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量為h,h的next指針指向h’,將本線(xiàn)程thi的入?;锇橹羔樦赶騢’對(duì)應(yīng)的線(xiàn)程;
(6-9)如果c的push指針當(dāng)前值為0,則利用CAS操作,嘗試將c的push指針從0更改為-1;經(jīng)過(guò)該步驟的處理,c的push指針要么被其它線(xiàn)程設(shè)置為指向某入棧請(qǐng)求,要么被設(shè)置為-1;
(6-10)判斷c的push指針是否為-1,如果是,結(jié)束幫助入棧函數(shù)的執(zhí)行;否則,進(jìn)入步驟(6-11);
(6-11)讀取c的push指針指向的入棧請(qǐng)求r;
(6-12)判斷r的標(biāo)識(shí)符是否小于或等于c的索引,如果是,嘗試將c指定為r的入棧位置;否則,結(jié)束幫助入棧函數(shù)的執(zhí)行;
(6-13)判斷c是否指定為r的入棧位置,如果是,將r的數(shù)據(jù)v存入c的val變量;結(jié)束幫助入棧函數(shù)的執(zhí)行。
與已公開(kāi)的方法相比,本發(fā)明具有如下優(yōu)點(diǎn):
1)高并行度:現(xiàn)有并行棧算法主要利用帶有棧頂指針的單向鏈表來(lái)建立棧結(jié)構(gòu);當(dāng)線(xiàn)程需要執(zhí)行入?;虺鰲2僮鲿r(shí),需要反復(fù)執(zhí)行CAS操作,嘗試修改棧頂指針,直到CAS操作成功為止;在這類(lèi)棧算法中,棧頂指針是線(xiàn)程的唯一競(jìng)爭(zhēng)點(diǎn),使多個(gè)線(xiàn)程只能順序執(zhí)行入棧和出棧操作,極大限制了棧算法的并行度,從而無(wú)法充分利用多核平臺(tái)強(qiáng)大的計(jì)算資源;為了提高棧算法的并行度,本發(fā)明利用數(shù)組來(lái)建立棧結(jié)構(gòu),分散線(xiàn)程的競(jìng)爭(zhēng)點(diǎn),從而提高入棧和出棧操作并行執(zhí)行的可能性;此外,由于一個(gè)數(shù)組元素可能存放的是無(wú)效值或已出棧,為了方便清理這些元素占據(jù)的內(nèi)存空間,本發(fā)明將全局?jǐn)?shù)組劃分為多個(gè)段,當(dāng)一個(gè)段中的所有元素都存放的是無(wú)效值或已出棧時(shí),會(huì)刪除該段并釋放它的內(nèi)存空間。
2)低復(fù)雜度:在基于鏈表的棧算法中,由于棧頂指針在同一時(shí)刻僅能被一個(gè)線(xiàn)程成功修改,某線(xiàn)程會(huì)因?yàn)槠渌€(xiàn)程的阻礙而無(wú)法完成入?;虺鰲2僮?;為了提供無(wú)等待的進(jìn)度保障,現(xiàn)有方法往往需要在所有操作中添加復(fù)雜的控制流程,從而犧牲了算法的性能;本發(fā)明提供的無(wú)等待棧算法將入棧操作分散到不同數(shù)組元素,使入棧操作僅會(huì)因?yàn)槌鰲2僮鞯淖璧K而失敗,并且入棧操作不會(huì)阻礙出棧操作的執(zhí)行,因此僅需在出棧操作中添加控制流程,保障其它線(xiàn)程能在有限步驟內(nèi)完成相應(yīng)操作,該算法設(shè)計(jì)簡(jiǎn)潔,方便應(yīng)用到實(shí)際系統(tǒng)中;
附圖說(shuō)明
圖1是本發(fā)明所基于的數(shù)據(jù)結(jié)構(gòu);
圖2是本發(fā)明的總體流程;
圖3是無(wú)等待入棧操作的執(zhí)行流程;
圖4是無(wú)等待出棧操作的執(zhí)行流程;
圖5是快速出棧函數(shù)的執(zhí)行流程;
圖6是幫助出棧函數(shù)的執(zhí)行流程;
圖7是幫助入棧函數(shù)的執(zhí)行流程。
具體實(shí)施方式
為了使本發(fā)明的目的、技術(shù)方案及優(yōu)點(diǎn)更加清楚明白,以下結(jié)合附圖和實(shí)施例,對(duì)本發(fā)明進(jìn)行進(jìn)一步詳細(xì)說(shuō)明。應(yīng)當(dāng)理解,此處所描述的具體實(shí)施例僅僅用以解釋本發(fā)明,并不用于限定本發(fā)明。凡在本發(fā)明的精神和原則之內(nèi)所作的任何修改、等同替換和改進(jìn)等,均應(yīng)包含在本發(fā)明的保護(hù)范圍之內(nèi)。
本發(fā)明利用如圖1所示數(shù)據(jù)結(jié)構(gòu)來(lái)構(gòu)建棧,將全局?jǐn)?shù)組劃分為多個(gè)包含N個(gè)數(shù)組元素的段,并且段與段之間構(gòu)成雙向鏈表結(jié)構(gòu)。其中,每個(gè)段都有唯一標(biāo)識(shí)符id,指向下一個(gè)段的next指針,指向前一個(gè)段的prev指針,以及用于統(tǒng)計(jì)已出棧元素和無(wú)用元素?cái)?shù)量的變量counter。例如,標(biāo)識(shí)符為1的段的prev和next指針,分別指向標(biāo)識(shí)符為0和2的段。全局?jǐn)?shù)組中的第一個(gè)段的prev指針和最后一個(gè)段的next指針則為空。此外,假定某數(shù)組元素的全局索引為i,那么它對(duì)應(yīng)于標(biāo)識(shí)符為i/N的段中第i%N+1個(gè)數(shù)組元素。本發(fā)明在棧的初始化階段僅分配一個(gè)段,在程序執(zhí)行期間再按需分配更多段。
本發(fā)明公開(kāi)的多核環(huán)境下基于數(shù)組結(jié)構(gòu)的無(wú)等待棧操作方法總體流程如圖2所示,其步驟為:
(1)主程序初始化代表?xiàng)5娜謹(jǐn)?shù)組,即分配一個(gè)包含N個(gè)數(shù)組元素的段,用于后續(xù)存放入棧數(shù)據(jù);其中,每個(gè)數(shù)組元素包含用于存儲(chǔ)入棧數(shù)據(jù)的變量val,指向入棧請(qǐng)求的指針push,以及指向出棧請(qǐng)求的指針pop,push和pop的初始值均為0;
(2)設(shè)置初始值為0的全局共享變量T和pc;其中,T為棧頂索引,pc用于指示后續(xù)出棧請(qǐng)求的標(biāo)識(shí)符;
(3)啟動(dòng)m個(gè)線(xiàn)程,每個(gè)線(xiàn)程thi(i=0,1,2,…,m-1)維護(hù)一個(gè)handle結(jié)構(gòu)類(lèi)型的變量hi來(lái)存儲(chǔ)自己的運(yùn)行狀態(tài),該結(jié)構(gòu)還包含指針next、入棧伙伴指針和出?;锇橹羔?;其中,每個(gè)線(xiàn)程的入棧伙伴指針和出?;锇橹羔樉跏贾赶虮揪€(xiàn)程;
(4)利用線(xiàn)程handle結(jié)構(gòu)類(lèi)型變量中的指針next,將所有線(xiàn)程的運(yùn)行狀態(tài)鏈接為環(huán)狀,即hi(i=0,1,2,…,m-2)的next指針指向hi+1,hm-1的next指針指向h0,使任意線(xiàn)程可以訪(fǎng)問(wèn)到其它線(xiàn)程的運(yùn)行狀態(tài);
(5)主程序等待接收線(xiàn)程對(duì)棧進(jìn)行操作的請(qǐng)求;
(6)當(dāng)任意線(xiàn)程thi(i=0,1,2,…,m-1)要對(duì)棧進(jìn)行操作時(shí),如果thi的操作請(qǐng)求為入棧請(qǐng)求,thi執(zhí)行無(wú)等待入棧操作;如果thi的操作請(qǐng)求為出棧請(qǐng)求,thi執(zhí)行無(wú)等待出棧操作;如果thi的操作請(qǐng)求為棧的銷(xiāo)毀請(qǐng)求,主程序會(huì)首先銷(xiāo)毀棧,然后包括主程序在內(nèi)的所有線(xiàn)程結(jié)束執(zhí)行;對(duì)于棧的銷(xiāo)毀請(qǐng)求之外的情況,主程序立刻繼續(xù)步驟(5)。
本發(fā)明提供的無(wú)等待入棧操作流程如圖3所示,具體步驟如下:
(2-1)對(duì)棧頂索引T進(jìn)行原子加一,獲取入棧位置索引i,即執(zhí)行i=FAA(&T,1);設(shè)置初始值為0的變量push_failures,用于指示將v存入全局?jǐn)?shù)組失敗的次數(shù);
(2-2)如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于i+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于i+1;在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素,并利用CAS操作,嘗試將入棧數(shù)據(jù)v存入c的val變量;當(dāng)且僅當(dāng)c的val變量尚未被其它入?;虺鰲2僮鞔嫒霐?shù)據(jù)時(shí),v才能被成功存入c的val變量;其中,索引為i的元素指全局?jǐn)?shù)組中第i+1個(gè)元素;
(2-3)判斷數(shù)據(jù)v是否被成功存入c的val變量,如果是,結(jié)束本次入棧操作;否則,執(zhí)行push_failures=push_failures+1,進(jìn)入步驟(2-4);
(2-4)判斷push_failures是否大于閾值P,如果是,在本線(xiàn)程thi的運(yùn)行狀態(tài)中公布本次入棧請(qǐng)求r,并將r的標(biāo)識(shí)符設(shè)置為入棧位置索引i的當(dāng)前值,用于確保v不會(huì)被存入索引小于當(dāng)前i值的元素,從而滿(mǎn)足棧的后進(jìn)先出語(yǔ)義,進(jìn)入步驟(2-5);否則,轉(zhuǎn)步驟(2-1);
(2-5)執(zhí)行i=FAA(&T,1);
(2-6)如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于i+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于i+1;在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素,并利用CAS操作,嘗試將c的push指針從0改為指向r;當(dāng)且僅當(dāng)c的push指針值為0時(shí),才會(huì)成功指向r;
(2-7)判斷c的push指針是否指向r,如果是,嘗試將c指定為r的入棧位置;否則,轉(zhuǎn)步驟(2-9);
(2-8)判斷c是否指定為r的入棧位置,如果是,轉(zhuǎn)步驟(2-10);
(2-9)判斷r是否已有指定元素進(jìn)行入棧,如果是,進(jìn)入步驟(2-10);否則,轉(zhuǎn)步驟(2-5);
(2-10)將v存入r被指定的入棧元素的val變量中,結(jié)束本次入棧操作。
本發(fā)明提供的無(wú)等待出棧操作流程如圖4所示,具體步驟如下:
(3-1)讀取棧頂索引的值,并將該值存入一變量t;如果全局?jǐn)?shù)組的當(dāng)前長(zhǎng)度小于t+1,首先通過(guò)分配更多包含N個(gè)數(shù)組元素的段,將數(shù)組長(zhǎng)度擴(kuò)展為不小于t+1;在全局?jǐn)?shù)組中查找索引為t的元素,并將變量c設(shè)置為該元素;
(3-2)設(shè)置初始值為0的變量pop_failures,用于記錄出棧失敗的次數(shù);
(3-3)判斷c是否為棧底,如果是,返回空,結(jié)束本次出棧操作;否則,首先執(zhí)行t=t-1,然后在全局?jǐn)?shù)組中查找索引為t的元素,并將c設(shè)置為該元素;
(3-4)將c作為快速出棧函數(shù)的輸入?yún)?shù),調(diào)用快速出棧函數(shù);當(dāng)且僅當(dāng)快速出棧函數(shù)的返回值為1時(shí),本次出棧操作才能從c中出棧;
(3-5)判斷快速出棧函數(shù)的返回值是否為1,如果是,轉(zhuǎn)步驟(3-11);否則,執(zhí)行pop_failures=pop_failures+1;
(3-6)判斷pop_failures是否大于P,如果是,在本線(xiàn)程thi的運(yùn)行狀態(tài)中公布本次出棧請(qǐng)求q,并在q中記錄c及其索引值,q的標(biāo)識(shí)符通過(guò)對(duì)共享變量pc進(jìn)行原子加一操作來(lái)獲取,即q的標(biāo)識(shí)符=FAA(&pc,1);否則,轉(zhuǎn)步驟(3-3);
(3-7)將本線(xiàn)程thi的handle結(jié)構(gòu)類(lèi)型的變量h作為幫助出棧函數(shù)的輸入?yún)?shù),調(diào)用幫助出棧函數(shù);當(dāng)幫助出棧函數(shù)返回時(shí),當(dāng)前線(xiàn)程thi可以從q中獲取最終出棧結(jié)果;
(3-8)判斷出棧結(jié)果是否為空,如果是,返回空,結(jié)束本次出棧操作;否則,從q中讀取指定出棧元素,并將c設(shè)置為該元素;當(dāng)前線(xiàn)程thi可以從q中獲取最終出棧結(jié)果,分為兩種情況:(1)返回空;(2)有指定出棧元素。
(3-9)統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n;
(3-10)判斷n是否等于N,如果是,刪除s然后進(jìn)行步驟(3-11),否則直接進(jìn)行步驟(3-11);其中,N為s所包含的數(shù)組元素的總數(shù);
(3-11)假定當(dāng)前出?;锇橹羔?biāo)赶虻木€(xiàn)程為th,將th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量h作為幫助出棧函數(shù)的輸入?yún)?shù),調(diào)用幫助出棧函數(shù);
(3-12)假定h的next指針指向h’,將線(xiàn)程thi的出?;锇橹羔樦赶騢’對(duì)應(yīng)的線(xiàn)程;
(3-13)返回c的val變量存放的值,即完成出棧,結(jié)束本次出棧操作。
進(jìn)一步,無(wú)等待出棧操作的特征在于,步驟(3-4)需要調(diào)用快速出棧函數(shù),該函數(shù)的執(zhí)行流程如圖5所示,具體步驟如下:
(4-1)讀取本函數(shù)的輸入元素c,將c作為幫助入棧函數(shù)的輸入?yún)?shù),調(diào)用幫助入棧函數(shù);
(4-2)競(jìng)爭(zhēng)c的出棧權(quán),即利用CAS操作,嘗試將c的pop指針從0改為-1;如果CAS操作的返回值非0,說(shuō)明競(jìng)爭(zhēng)成功;
(4-3)判斷競(jìng)爭(zhēng)是否成功,如果是,統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n,執(zhí)行步驟(4-4);否則,從c中出棧失敗,返回0,結(jié)束快速出棧函數(shù)的執(zhí)行;
(4-4)判斷n是否等于N,如果是,刪除s;其中,N為s所包含的數(shù)組元素的總數(shù);
(4-5)判斷c的val變量是否存放有效值,如果是,則從c中出棧成功,返回1;否則,從c中出棧失敗,返回0。
進(jìn)一步,無(wú)等待出棧操作的特征在于,步驟(3-7)和步驟(3-11)需要調(diào)用幫助出棧函數(shù),該函數(shù)的執(zhí)行流程如圖6所示,具體步驟如下:
(5-1)讀取本函數(shù)的輸入?yún)?shù)h;
(5-2)通過(guò)讀取h中存儲(chǔ)的運(yùn)行狀態(tài),判斷與h對(duì)應(yīng)的線(xiàn)程是否存在需要幫助的出棧請(qǐng)求q,如果是,進(jìn)入步驟(5-3);否則,結(jié)束幫助出棧函數(shù)的執(zhí)行;
(5-3)讀取q中記錄的元素c和c的索引,并將c的索引賦值給變量i;
(5-4)判斷c是否為棧底,如果是,嘗試指定q的出棧結(jié)果為返回空,結(jié)束幫助出棧函數(shù)的執(zhí)行;否則,執(zhí)行i=i-1,在全局?jǐn)?shù)組中查找索引為i的元素,并將變量c設(shè)置為該元素;
(5-5)將c作為幫助入棧函數(shù)的輸入?yún)?shù),調(diào)用幫助入棧函數(shù);
(5-6)競(jìng)爭(zhēng)c的出棧權(quán),即利用CAS操作,嘗試將c的pop指針從0改為指向q,并使用變量ret保存CAS操作的返回值;
(5-7)判斷c的pop指針是否指向q,如果是,進(jìn)入步驟(5-8);否則,轉(zhuǎn)步驟(5-11);
(5-8)判斷c的val變量是否存放有效值,如果是,嘗試指定q從c中出棧,結(jié)束幫助出棧函數(shù)的執(zhí)行;
(5-9)判斷ret是否為0,如果是,轉(zhuǎn)步驟(5-11);否則,統(tǒng)計(jì)c所在段s中已出棧元素和無(wú)用元素的總數(shù)n;
(5-10)判斷n是否等于N,如果是,刪除s,然后執(zhí)行步驟(5-11);如果n不等于N,則執(zhí)行(5-11);
(5-11)判斷q是否已被指定出棧結(jié)果,如果是,結(jié)束幫助出棧函數(shù)的執(zhí)行;否則,轉(zhuǎn)步驟(5-4)。
進(jìn)一步,快速出棧函數(shù)和幫助出棧函數(shù)的特征在于,步驟(4-1)和步驟(5-5)需要調(diào)用幫助入棧函數(shù),該函數(shù)的執(zhí)行流程如圖7所示,具體步驟如下:
(6-1)讀取本函數(shù)的輸入元素c,如果c的val變量中尚未存放有效值,則利用CAS操作,嘗試將無(wú)效值存入c的val變量中,使c成為無(wú)用元素;
(6-2)判斷c的val變量是否存放有效值,如果是,結(jié)束幫助入棧函數(shù)的執(zhí)行;
(6-3)判斷c的push指針是否已指向某入棧請(qǐng)求,如果是,轉(zhuǎn)步驟(6-11);否則,讀取本線(xiàn)程thi的入?;锇橹羔?biāo)赶虻木€(xiàn)程,并將變量th設(shè)置為該線(xiàn)程;
(6-4)判斷本線(xiàn)程thi是否在本周期內(nèi)嘗試幫助過(guò)th入棧,并且相應(yīng)入棧請(qǐng)求已完成,如果否,則直接進(jìn)行步驟(6-5);如果是,假定th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量為h,h的next指針指向h’,將th設(shè)置為h’對(duì)應(yīng)的線(xiàn)程,并使本線(xiàn)程thi的入?;锇橹羔樦赶騮h;
(6-5)判斷th是否有需要幫助的入棧請(qǐng)求r’,如果是,進(jìn)入步驟(6-6);否則,轉(zhuǎn)步驟(6-8);
(6-6)判斷r’的標(biāo)識(shí)符是否小于或等于c的索引,如果是,嘗試將c預(yù)留給r’,即利用CAS操作,嘗試將c的push指針從0改為指向r’,若成功,CAS操作的返回值為非0;否則,轉(zhuǎn)步驟(6-8);
(6-7)判斷是否成功將c預(yù)留給r’,如果是,進(jìn)入步驟(6-8);否則,轉(zhuǎn)步驟(6-9);
(6-8)假定th維護(hù)的handle結(jié)構(gòu)類(lèi)型的變量為h,h的next指針指向h’,將本線(xiàn)程thi的入?;锇橹羔樦赶騢’對(duì)應(yīng)的線(xiàn)程;
(6-9)如果c的push指針當(dāng)前值為0,則利用CAS操作,嘗試將c的push指針從0更改為-1;經(jīng)過(guò)該步驟的處理,c的push指針要么被其它線(xiàn)程設(shè)置為指向某入棧請(qǐng)求,要么被設(shè)置為-1;
(6-10)判斷c的push指針是否為-1,如果是,結(jié)束幫助入棧函數(shù)的執(zhí)行;否則,進(jìn)入步驟(6-11);
(6-11)讀取c的push指針指向的入棧請(qǐng)求r;
(6-12)判斷r的標(biāo)識(shí)符是否小于或等于c的索引,如果是,嘗試將c指定為r的入棧位置;否則,結(jié)束幫助入棧函數(shù)的執(zhí)行;
(6-13)判斷c是否指定為r的入棧位置,如果是,將r的數(shù)據(jù)v存入c的val變量;結(jié)束幫助入棧函數(shù)的執(zhí)行。