王 ?。ū本┤吠ㄐ判盘?hào)研究設(shè)計(jì)院有限公司,北京 100073)
王俊,男,碩士,畢業(yè)清華大學(xué),助理工程師,主要從事區(qū)控系統(tǒng)開(kāi)發(fā),在CBTC系統(tǒng)項(xiàng)目中擔(dān)任維護(hù)子系統(tǒng)負(fù)責(zé)人。
隨著計(jì)算機(jī)技術(shù)的發(fā)展,多核處理器以及廣泛應(yīng)用于服務(wù)器、臺(tái)式機(jī)和便攜式計(jì)算機(jī)[1],用于維護(hù)系統(tǒng)服務(wù)器的處理器已多是四核以上。而用于服務(wù)器的操作系統(tǒng)如windows server 2003/2008均能支持多核,能夠?qū)⒍鄠€(gè)線程在多個(gè)內(nèi)核上并行的執(zhí)行。這樣,只要維護(hù)系統(tǒng)應(yīng)用軟件在設(shè)計(jì)時(shí)充分考慮多核系統(tǒng)的特性,將復(fù)雜的處理任務(wù)劃分為若干可并行執(zhí)行的任務(wù),就能充分利用多核來(lái)提高應(yīng)用軟件的執(zhí)行效率。
一般而言,并發(fā)(并行)編程比較復(fù)雜,需要處理線程同步、數(shù)據(jù)共享等一系列問(wèn)題。而JAVA作為一種持續(xù)發(fā)展的、先進(jìn)的編程語(yǔ)言,它一直在嘗試提供更好的手段來(lái)降低并發(fā)(并行)編程的難度。另一方面,JAVA語(yǔ)言功能完善,易于代碼編寫和維護(hù)。它提供的Swing框架適合開(kāi)發(fā)桌面維護(hù)系統(tǒng),而J2EE對(duì)網(wǎng)絡(luò)開(kāi)發(fā)的支持,十分適合開(kāi)發(fā)Browser-Server架構(gòu)的維護(hù)系統(tǒng)。本文將在介紹JAVA語(yǔ)言并行計(jì)算技術(shù)發(fā)展的基礎(chǔ)上,探討JAVA并行計(jì)算技術(shù)在信號(hào)系統(tǒng)的維護(hù)子系統(tǒng)開(kāi)發(fā)中的應(yīng)用。
首先說(shuō)明下一并行編程與并發(fā)編程的區(qū)別。并行編程關(guān)注的是充分利用計(jì)算資源更快的獲取結(jié)果,而并發(fā)編程關(guān)注的是正確高效地控制多個(gè)線程訪問(wèn)共享的資源[2]。
傳統(tǒng)的并發(fā)編程基于java.lang.Thread類和java.lang.Runnable接口實(shí)現(xiàn)。由程序員自己處理線程同步和數(shù)據(jù)共享的問(wèn)題。這樣的并發(fā)編程對(duì)于多個(gè)線程協(xié)同完成一個(gè)工作的情況下很容易產(chǎn)生錯(cuò)誤,并且調(diào)試相當(dāng)復(fù)雜,因?yàn)楦鱾€(gè)線程的調(diào)用時(shí)機(jī)會(huì)對(duì)執(zhí)行結(jié)果產(chǎn)生影響。
正式因?yàn)閭鹘y(tǒng)并發(fā)編程的問(wèn)題,JAVA SE6為我們提供了一系列的并發(fā)編程基元、集合和特性[1]。尤其是它提供了執(zhí)行器,可以執(zhí)行線程任務(wù)類似的任務(wù),提供了線程池和調(diào)度策略,而且可以同步或者異步地獲取執(zhí)行的結(jié)果。
執(zhí)行器是JAVA并發(fā)編程的一大進(jìn)步,然而它的并行處理還存在問(wèn)題。當(dāng)一個(gè)并發(fā)任務(wù)需要等待另一并發(fā)任務(wù)的結(jié)果時(shí),這個(gè)并發(fā)任務(wù)將被置于閑置狀態(tài),因此浪費(fèi)了執(zhí)行另一個(gè)等待的并發(fā)任務(wù)的機(jī)會(huì)。而JAVA SE 7中引入了分解/合并(Fork/Join)框架來(lái)幫助并行處理。
分解/合并框架的核心功能是新的Fork-JoinPool執(zhí)行器,它用來(lái)執(zhí)行ForkJoinTask任務(wù)。而ForkJoinTask可以分派子任務(wù),并等待子任務(wù)完成。這樣,F(xiàn)orkJoinPool執(zhí)行器可以在任務(wù)等待另一個(gè)子任務(wù)完成時(shí),利用計(jì)算資源處理其他待處理的任務(wù)。分解/合并框架提供了一種表示可并行化算法的簡(jiǎn)單方式[3],極大地簡(jiǎn)化了編寫并發(fā)程序的瑣碎工作[4]。
文獻(xiàn)[1]在Oracle的Sun Fire T2000服務(wù)器上,通過(guò)指定Java虛擬機(jī)可用的內(nèi)核數(shù),測(cè)試了分解/合并方式和單線程形式以找出“import”在JDK源代碼文件中出現(xiàn)的次數(shù)消耗的時(shí)間。結(jié)果表明分解/合并方式相對(duì)與單線程處理的性能提高隨內(nèi)核數(shù)目增加線性增長(zhǎng)。
因此,在信號(hào)系統(tǒng)維護(hù)子系統(tǒng)開(kāi)發(fā)中利用好JAVA的并行計(jì)算技術(shù),能夠大大提高“計(jì)算密集型”任務(wù)的執(zhí)行效率,從而使得維護(hù)系統(tǒng)的響應(yīng)時(shí)間更快,使得用戶得到更好的體驗(yàn)。在CBTC系統(tǒng)中,隨著各個(gè)子系統(tǒng)交互的信息增加,可以監(jiān)測(cè)的信息也隨之增加,提高維護(hù)系統(tǒng)的信息處理效率,能夠讓維護(hù)系統(tǒng)盡可能多的監(jiān)測(cè)CBTC系統(tǒng)運(yùn)行信息,給運(yùn)行狀態(tài)監(jiān)測(cè)、故障預(yù)警分析提供更加充分的信息。
信號(hào)系統(tǒng)維護(hù)子系統(tǒng)的定位是對(duì)信號(hào)系統(tǒng)設(shè)備進(jìn)行就地監(jiān)測(cè)和遠(yuǎn)程報(bào)警,并輔助進(jìn)行故障預(yù)警、分析,指導(dǎo)維修作業(yè)。維護(hù)子系統(tǒng)一般應(yīng)具備的功能有:監(jiān)測(cè)數(shù)據(jù)存儲(chǔ)、信號(hào)設(shè)備狀態(tài)監(jiān)測(cè)、故障報(bào)警、查詢回放、統(tǒng)計(jì)報(bào)表等。
為監(jiān)測(cè)盡可能多的信息,維護(hù)子系統(tǒng)持續(xù)的接收大流量監(jiān)測(cè)數(shù)據(jù),處理這些數(shù)據(jù)的有以下幾個(gè)瓶頸。1)數(shù)據(jù)的解析:由于監(jiān)測(cè)信息的增加,數(shù)據(jù)解析的工作量也增加了。高效的解析數(shù)據(jù)能夠?qū)崟r(shí)流暢的顯示信號(hào)系統(tǒng)設(shè)備的運(yùn)行狀態(tài),并及時(shí)的給出故障報(bào)警。
2)數(shù)據(jù)的篩選:如果采用數(shù)據(jù)庫(kù),專業(yè)的商用數(shù)據(jù)庫(kù)能高效地幫我們實(shí)現(xiàn)基本的數(shù)據(jù)查詢,但是有時(shí)我們需要對(duì)查詢的數(shù)據(jù)進(jìn)一步篩選,如果篩選效率過(guò)低就會(huì)影響用戶體驗(yàn);如果采用文件系統(tǒng),那么從數(shù)千萬(wàn)條記錄中篩選我們需要的記錄所需的響應(yīng)時(shí)間會(huì)極大地降低用戶體驗(yàn)。
3)數(shù)據(jù)的壓縮與解壓:為了節(jié)省磁盤空間,可能會(huì)選擇在存儲(chǔ)數(shù)據(jù)時(shí)對(duì)數(shù)據(jù)進(jìn)行壓縮。在數(shù)據(jù)導(dǎo)入導(dǎo)出過(guò)程中,為了便于數(shù)據(jù)的拷貝,也可能選擇在導(dǎo)出時(shí)對(duì)數(shù)據(jù)進(jìn)行壓縮,在導(dǎo)入時(shí)在進(jìn)行解壓的策略。存儲(chǔ)數(shù)據(jù)時(shí)的壓縮如果耗時(shí)太大可能影響完整記錄數(shù)據(jù)的功能,而導(dǎo)入導(dǎo)出時(shí)數(shù)據(jù)壓縮、解壓耗時(shí)太大又會(huì)降低用戶體驗(yàn)。
以上幾個(gè)維護(hù)子系統(tǒng)的瓶頸,都可以認(rèn)為是“計(jì)算密集型”任務(wù),可以利用并行計(jì)算技術(shù)提高效率。下文針對(duì)其中的數(shù)據(jù)解析問(wèn)題,研究并行計(jì)算的實(shí)現(xiàn)方法。
數(shù)據(jù)解析任務(wù)是從一個(gè)或多個(gè)緩存數(shù)據(jù)中按照協(xié)議解析出數(shù)據(jù)包含信息的過(guò)程。雖然待監(jiān)測(cè)數(shù)據(jù)協(xié)議可能各有不同,但是大多協(xié)議可以歸納為一個(gè)典型的通信協(xié)議的格式如圖 1所示。
對(duì)于典型通信協(xié)議,我們對(duì)通信內(nèi)容進(jìn)行域劃分,直到最小的域,即一個(gè)字段。將單個(gè)字段的解析作為最細(xì)的操作任務(wù),它可能是進(jìn)行字節(jié)合并、移位和與或等簡(jiǎn)單的操作。這樣,我們就可以套用JAVA分解/合并(Fork/Join)框架。對(duì)整個(gè)解析任務(wù)進(jìn)行分解,如果當(dāng)前解析任務(wù)可以拆分為子解析任務(wù),則對(duì)解析任務(wù)進(jìn)行分解;如果解析任務(wù)已經(jīng)是最細(xì)解析任務(wù),則進(jìn)行數(shù)據(jù)解析,其偽代碼如下。
依照偽代碼的方式,解析任務(wù)按照如圖2所示方式被分解。
經(jīng)過(guò)這樣的分解過(guò)程,解析任務(wù)不斷地被分解為最細(xì)的解析任務(wù),并放入ForkJoinPool執(zhí)行器的線程池中,由JAVA的分解/合并框架來(lái)根據(jù)可用線程數(shù)來(lái)調(diào)度這些最細(xì)解析任務(wù)的操作,達(dá)到充分利用多核處理器提高解析信息效率的目的。
對(duì)于解析任務(wù),也可以采用傳統(tǒng)的并發(fā)編程的方式,對(duì)每一個(gè)最細(xì)的解析任務(wù)創(chuàng)建一個(gè)線程來(lái)實(shí)現(xiàn)。但是如果同時(shí)的線程數(shù)大于處理器支持線程數(shù)則會(huì)使處理器處理碎片化,不僅不會(huì)提高效率還可能降低處理;而如果同時(shí)的線程數(shù)小于處理器支持的線程數(shù)則又不能充分利用計(jì)算資源。并發(fā)編程中考慮線程數(shù)與內(nèi)核數(shù)的匹配會(huì)增加編程的復(fù)雜度。而且隨著硬件升級(jí),內(nèi)核數(shù)可能增加,這樣并發(fā)編程就需要重新設(shè)計(jì)來(lái)適應(yīng)。
而采用分解/合并框架,F(xiàn)orkJoinPool可以指定并行度,由分解/合并框架實(shí)現(xiàn)并行度與可用的硬件處理單元數(shù)目匹配,開(kāi)發(fā)者只需要按照分解/合并的思路去分解“計(jì)算密集型”任務(wù)就能簡(jiǎn)單的實(shí)現(xiàn)并行計(jì)算,從而可以把更多精力投入業(yè)務(wù)設(shè)計(jì)中。
本文介紹JAVA并行編程的發(fā)展,分析了在CBTC維護(hù)系統(tǒng)中應(yīng)用JAVA并行編程的方法。JAVA并行編程在多核平臺(tái)上能夠提高維護(hù)系統(tǒng)“計(jì)算密集型”任務(wù)的效率,使維護(hù)系統(tǒng)能夠?qū)崿F(xiàn)更全面的維護(hù)監(jiān)測(cè)并提供友好的時(shí)間響應(yīng)性能。
[1] Julien Ponge.Fork and Join:Java Can Excel at Painless Parallel Programming Too![EB/OL].[2013-01-02].http://www.oracle.com/technetwork/articles/java/fork-join-422606.html.
[2] Dan Grossman.A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency[EB/OL].[2013-01-02].http://wenku.baidu.com/view/e5438300eff9aef8941e067f.html###.
[3] Brian Goetz.Java理論與實(shí)踐:應(yīng)用fork-join 框架——學(xué)習(xí)如何使用Java 7中的fork-join框架實(shí)現(xiàn)細(xì)粒度并行性[EB/OL].[2013-01-01].http://www.ibm.com/developerworks/cn/java/j-jtp11137.html.
[4] 甘志,戴曉君.JDK 7中的Fork/Join模式——輕松實(shí)現(xiàn)多核時(shí)代的并行計(jì)算[EB/OL].[2013-01-02].http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/index.html.