企業(yè)應(yīng)用系統(tǒng),尤其是金融保險(xiǎn)行業(yè)的應(yīng)用系統(tǒng)涂邀,常常會(huì)涉及大量繁雜的計(jì)算瘟仿。例如,在保險(xiǎn)行業(yè)比勉,客戶購買了一份投連保險(xiǎn)產(chǎn)品劳较,系統(tǒng)要為客戶展示未來數(shù)十年的利益驹止。這些利益按月計(jì)算,按年累計(jì)观蜗,各項(xiàng)金額前后關(guān)聯(lián)臊恋,計(jì)算形態(tài)多種多樣。這類保險(xiǎn)產(chǎn)品的利益展示可能涉及萬千次的計(jì)算墓捻。如果這些計(jì)算沒有經(jīng)過良好的設(shè)計(jì)抖仅,系統(tǒng)的性能常常會(huì)變得低效。
計(jì)算是軟件行業(yè)中的一個(gè)重要領(lǐng)域砖第。對于企業(yè)應(yīng)用系統(tǒng)來說撤卢,計(jì)算不僅包含了算法,還包含了一套應(yīng)用算法的框架梧兼。仍以保險(xiǎn)行業(yè)為例放吩,保險(xiǎn)產(chǎn)品形形色色,計(jì)算規(guī)則羽杰、計(jì)算公式渡紫、計(jì)算過程千差萬別。計(jì)算框架要有能力對這些看似繁雜無序的內(nèi)容做出歸納和抽象考赛,以最小的代價(jià)容納它們惕澎,并使之享受到算法的好處。
值得強(qiáng)調(diào)的是颜骤,計(jì)算框架和算法是計(jì)算領(lǐng)域中兩個(gè)不同的子領(lǐng)域集灌。這兩個(gè)子領(lǐng)域的目標(biāo)和內(nèi)容都是不同的。計(jì)算框架試圖容納變化复哆,并使之用到算法的好處;算法則是基于一套數(shù)據(jù)結(jié)構(gòu)腌零,在空間和速度的權(quán)衡下進(jìn)行高效計(jì)算梯找。清晰的領(lǐng)域劃分可以使我們更加專注在領(lǐng)域內(nèi)的工作,而模糊的領(lǐng)域認(rèn)識益涧,常常會(huì)使我們迷失方向锈锤,做出錯(cuò)誤的選擇。在實(shí)踐中闲询,我們常尘妹猓看到計(jì)算框架和算法之間沒有明顯的邊界,算法中嵌入計(jì)算框架的元素扭弧,這使得算法無法進(jìn)行充分優(yōu)化阎姥。
最近,我正在設(shè)計(jì)一個(gè)計(jì)算服務(wù)鸽捻。面對數(shù)百頁各類產(chǎn)品的業(yè)務(wù)計(jì)算說明呼巴,從中找到計(jì)算特征泽腮,提煉高效的算法,并設(shè)計(jì)一個(gè)框架來容納各種變化衣赶,這真的是一次有趣的挑戰(zhàn)诊赊。由于具體的業(yè)務(wù)內(nèi)容不適合公開展示,因此府瞄,本文將只是著眼于計(jì)算設(shè)計(jì)的方法和高度抽象的算法模型碧磅。
了解業(yè)務(wù)領(lǐng)域,熟悉業(yè)務(wù)需求遵馆,當(dāng)然是軟件開發(fā)的前提鲸郊。接下來,我們需要從繁雜的業(yè)務(wù)描述中找到計(jì)算特征团搞。我不知道這一步是否有可供操作的方法严望,事實(shí)上,我發(fā)現(xiàn)這似乎是經(jīng)驗(yàn)積累后的一種轉(zhuǎn)化逻恐。當(dāng)你閱讀需求的時(shí)候像吻,很多工作已經(jīng)在頭腦中自然地展開,識別复隆、判斷拨匆、證實(shí)、歸類挽拂、忽略惭每、抽象,一些想法漸漸從模糊到清晰亏栈。好吧台腥,我得到了未經(jīng)處理的計(jì)算特征。見圖1绒北。
從圖1可知黎侈,一組計(jì)算子(通過計(jì)算獲得,計(jì)算形態(tài)不限)將作為輸入塊(A)闷游。這些計(jì)算子之間存在依賴關(guān)系峻汉,即某些計(jì)算子通過其他的計(jì)算子參與才能完成計(jì)算。這種依賴關(guān)系可能是多級的脐往,例如休吠,計(jì)算子1依賴于計(jì)算子2,計(jì)算子2依賴于計(jì)算子3业簿。
這一組計(jì)算子被送入下方緊鄰的一個(gè)計(jì)算塊(B)瘤礁,這個(gè)計(jì)算塊(B)內(nèi)部的計(jì)算子之間不僅存在輸入塊(A)中的計(jì)算子規(guī)則,還可能會(huì)依賴輸入塊(A)中的計(jì)算子辖源。
當(dāng)前的計(jì)算塊(B)會(huì)被送入其下方緊鄰的另一個(gè)計(jì)算塊(B')蔚携,計(jì)算塊(B')不僅像計(jì)算塊(B)一樣希太,依賴于輸入塊(A),還依賴于計(jì)算塊(B)酝蜒。
計(jì)算塊(B)還會(huì)被送入其右方緊鄰的計(jì)算塊(C)誊辉,計(jì)算塊(C)和計(jì)算塊(B’)類似,依賴于輸入塊(A)和計(jì)算塊(B)亡脑。
計(jì)算塊(C)會(huì)被送入其下方緊鄰的計(jì)算塊(C’)堕澄,計(jì)算塊(C’)依賴于輸入塊(A),計(jì)算塊(C)霉咨,計(jì)算塊(B’)蛙紫。
上述這種規(guī)則將沿著Y軸向右方延伸,沿著X軸向下方延伸途戒。
最終坑傅,數(shù)據(jù)將到達(dá)輸出塊(O)。輸出塊(O)與輸入塊(A)及緊鄰其上方的一排計(jì)算塊有依賴關(guān)系喷斋。
上述計(jì)算特征表明唁毒,這類計(jì)算的難點(diǎn)在于計(jì)算子之間的依賴關(guān)系。這些繁雜的依賴關(guān)系如果沒有經(jīng)過清晰的梳理星爪,會(huì)使你不得不保存大量的歷史數(shù)據(jù)浆西,并不得不進(jìn)行串行化的低效計(jì)算。失控的大空間顽腾,以及閑置的CPU計(jì)算能力近零,對于算法來說都是致命的缺陷。
在總結(jié)出計(jì)算特征后抄肖,我們將開始算法設(shè)計(jì)久信。算法設(shè)計(jì)通常以硬件資源為基礎(chǔ)(一臺40核的服務(wù)器可供使用)。我們采用了并行計(jì)算漓摩,并確立了一個(gè)基本原則入篮,即以最大限度并行計(jì)算為優(yōu)先,換句話說幌甘,不是以業(yè)務(wù)邏輯為優(yōu)先。這個(gè)基本原則要求我們必須在更高層次上進(jìn)行抽象痊项,確保只有在無法避免時(shí)才進(jìn)入串行計(jì)算锅风。顯然,由于計(jì)算特征中的依賴關(guān)系鞍泉,我們很難完全避免這一點(diǎn)皱埠。不過,從另一個(gè)角度來看咖驮,這種缺陷未嘗不是一件好事边器,完全占用CPU會(huì)導(dǎo)致其他的嚴(yán)重問題训枢,我們必須留出CPU的計(jì)算能力給其他的并發(fā)請求。現(xiàn)在忘巧,我們的并行計(jì)算模型像是一個(gè)鋸齒形恒界,見圖2,對比了串行計(jì)算和鋸齒形并行計(jì)算砚嘴。
算法設(shè)計(jì)和業(yè)務(wù)領(lǐng)域的設(shè)計(jì)有些類似十酣,兩者都需要對數(shù)據(jù)(結(jié)構(gòu))進(jìn)行建模,并基于數(shù)據(jù)模型進(jìn)行邏輯處理际长。不同之處在于耸采,算法顯然更偏向于求值計(jì)算,而且更加關(guān)注指令的簡潔和內(nèi)存空間的使用工育。任何設(shè)計(jì)都不是一蹴而就虾宇,你需要在頭腦中設(shè)想計(jì)算的過程,并不斷迭代如绸,最終提煉出恰當(dāng)?shù)母拍顏砣菁{你對于計(jì)算的描述嘱朽。事實(shí)上,我同樣不知道該如何針對這個(gè)過程給出可操作的具體方法竭沫≡锍幔總之,我構(gòu)思了這些概念蜕提,見圖3森书。
我們將有一個(gè)計(jì)算的上下文(Calculation Context),這個(gè)上下文包含了輸入(Input)谎势,輸出(Output)凛膏,以及一組計(jì)算布局(Calculation Layout)。每一個(gè)計(jì)算布局都可以迭代執(zhí)行脏榆,執(zhí)行的結(jié)果會(huì)進(jìn)入計(jì)算布局棧區(qū)(Calculation Layout Stack Area)猖毫,棧區(qū)的數(shù)據(jù)來自于并行計(jì)算區(qū)(Calculation Parallel Area),由于并行計(jì)算须喂,棧區(qū)的數(shù)據(jù)是無序的吁断,需要經(jīng)過后續(xù)處理,進(jìn)入計(jì)算布局輸出(Calculation Layout Output)坞生。一個(gè)計(jì)算布局包含了多個(gè)有序的并行計(jì)算區(qū)仔役。每一個(gè)計(jì)算區(qū)又包含了多個(gè)計(jì)算分組(Calculation Item Group),這些計(jì)算分組是有序的是己,而每個(gè)計(jì)算分組又包含了多個(gè)計(jì)算子(Calculation Item)又兵,這些計(jì)算子是無序的。理論上,無序的分組都是可以進(jìn)行并行計(jì)算的沛厨。
當(dāng)有了這套數(shù)據(jù)模型后宙地,我們的算法實(shí)現(xiàn)就變得相對簡單了。當(dāng)然逆皮,在算法實(shí)現(xiàn)過程中宅粥,仍然有很多算法領(lǐng)域內(nèi)的問題需要給出答案。例如页屠,如何保證盡可能少地占用內(nèi)存空間粹胯?如何采用最合適的基本數(shù)據(jù)結(jié)構(gòu)?如何使指令變得最大限度的精簡辰企?