在上一篇文章中,我照自己的需求實(shí)作了一個job system,其中有個功能不是很令人滿意。
假設(shè)目前有job A與job B,A做完才能做B。在上文架構(gòu)中,能夠在執(zhí)行job A時產(chǎn)生job C,且可允許C做完才會做B。但是在使用該架構(gòu)時,無法以直觀的方式達(dá)成這功能。理由是job內(nèi)部不能等待其他job完成,更精確地說是任一worker不能以任何原因等待其他worker。效率問題暫且不提,若每個worker都產(chǎn)生新的job然後等待其他worker完成該job,就沒有worker會做事了。為了減少worker等待的情況,該架構(gòu)還實(shí)作了限制特定job同時被執(zhí)行的數(shù)量。
現(xiàn)在有新的方法可以解決這些問題了,Naughty Dog在GDC 2015發(fā)表了他們的job system,
裡頭提到利用Fiber使得job內(nèi)的等待動作成為可能。原理是job要等待時就把目前的fiber存起來,然後從fiber pool拉出一個新fiber來執(zhí)行別的job;要切換回等待中的job時,就把目前的fiber丟進(jìn)pool裡,再還原先前暫存的fiber。
此法可使所有worker無時無刻都在做事,且由於等待的限制解除,job程式碼的撰寫也變的更直觀。這個方法看起來太美好了,因此我又實(shí)作了一份job system。這次的實(shí)作目標(biāo)有以下幾點(diǎn):
以下再貼一次之前的測試數(shù)據(jù)來對比新架構(gòu)的效能,設(shè)定Job數(shù)量為2000個。值得一提的是新架構(gòu)為了測試Fiber切換效率,特地新增一個job專門用來送出2000個job,並等待該2000個job完成。2000個job的程式碼與上篇文章相同。
對照組,完全不使用平行處理,測試1000次得到的平均時間
使用我的簡化版Doom3 BFG Job架構(gòu),測試1000次得到的平均時間
使用上篇文章架構(gòu)測試1000次得到的平均時間
使用Fiber的新架構(gòu),2000+1個job測試1000次得到的平均時間
假設(shè)目前有job A與job B,A做完才能做B。在上文架構(gòu)中,能夠在執(zhí)行job A時產(chǎn)生job C,且可允許C做完才會做B。但是在使用該架構(gòu)時,無法以直觀的方式達(dá)成這功能。理由是job內(nèi)部不能等待其他job完成,更精確地說是任一worker不能以任何原因等待其他worker。效率問題暫且不提,若每個worker都產(chǎn)生新的job然後等待其他worker完成該job,就沒有worker會做事了。為了減少worker等待的情況,該架構(gòu)還實(shí)作了限制特定job同時被執(zhí)行的數(shù)量。
現(xiàn)在有新的方法可以解決這些問題了,Naughty Dog在GDC 2015發(fā)表了他們的job system,
裡頭提到利用Fiber使得job內(nèi)的等待動作成為可能。原理是job要等待時就把目前的fiber存起來,然後從fiber pool拉出一個新fiber來執(zhí)行別的job;要切換回等待中的job時,就把目前的fiber丟進(jìn)pool裡,再還原先前暫存的fiber。
此法可使所有worker無時無刻都在做事,且由於等待的限制解除,job程式碼的撰寫也變的更直觀。這個方法看起來太美好了,因此我又實(shí)作了一份job system。這次的實(shí)作目標(biāo)有以下幾點(diǎn):
- 使用boost coroutine來搞定fiber機(jī)制
- 拔掉manager thread
- 拔掉限制特定job同時執(zhí)行數(shù)量的功能
- 保留其他已實(shí)作的功能
由於少了manager thread來搶CPU,料想效率應(yīng)該會比上個版本還快,但測得的時間卻不如預(yù)期,乾脆趁此機(jī)會來個大改版。從如何送job給worker到worker如何取出與執(zhí)行job,實(shí)驗了許多種版本。最後程式碼大部份被改寫,核心概念改為偏向Doom3 BFG的方式,無論是傳遞job或執(zhí)行job的效率都勝過上個版本。
以下再貼一次之前的測試數(shù)據(jù)來對比新架構(gòu)的效能,設(shè)定Job數(shù)量為2000個。值得一提的是新架構(gòu)為了測試Fiber切換效率,特地新增一個job專門用來送出2000個job,並等待該2000個job完成。2000個job的程式碼與上篇文章相同。
對照組,完全不使用平行處理,測試1000次得到的平均時間
迴圈1000,需時2.359ms
迴圈5000,需時11.705ms
迴圈10000,需時23.343ms使用我的簡化版Doom3 BFG Job架構(gòu),測試1000次得到的平均時間
迴圈1000,需時0.704ms,效能提升3.35倍
迴圈5000,需時3.127ms,效能提升3.74倍
迴圈10000,需時6.200ms,效能提升3.77倍
使用上篇文章架構(gòu)測試1000次得到的平均時間
迴圈1000,需時0.782ms,效能提升3.02倍
迴圈5000,需時3.181ms,效能提升3.68倍
迴圈10000,需時6.194ms,效能提升3.78倍
使用Fiber的新架構(gòu),2000+1個job測試1000次得到的平均時間
迴圈1000,需時0.653ms,效能提升3.61倍
迴圈5000,需時3.072ms,效能提升3.81倍
迴圈10000,需時6.084ms,效能提升3.84倍
這回總算是全面贏了我的簡化版Doom3 BFG,主要應(yīng)是少了job list內(nèi)sync機(jī)制的關(guān)係。Worker在衝刺job list的時候不需要每執(zhí)行一個job就檢查一次sync,也沒有被sync中斷的可能,切換job的成本可減到最低。以往一直認(rèn)為coroutine在多核心環(huán)境下無用武之處,要求效能的程式會選擇使用thread而不是coroutine,現(xiàn)在看來我錯了,而且錯得很開心。