楊霄翼
(國家測繪地理信息局 第三地理信息制圖院,四川 成都 610100)
由美國環(huán)境系統(tǒng)研究所(ESRI)研發(fā)的ArcGIS系列平臺軟件中的地理處理(GP)幾乎包含了地理處理的全部核心功能和工具模塊,是軟件的精華所在,也是其被廣泛應(yīng)用的原因之一[1]。為實現(xiàn)高效執(zhí)行地理數(shù)據(jù)分析、數(shù)據(jù)轉(zhuǎn)換、數(shù)據(jù)管理和地圖自動化創(chuàng)建等GP功能,ESRI構(gòu)建了基于Python編程語言的Arcpy站點包,Python通過Arcpy調(diào)用全部的GP工具[2],已在多個領(lǐng)域得到廣泛推廣與應(yīng)用[3-6]。
隨著一個城市、一個國家乃至全球地理空間數(shù)據(jù)的綜合處理需求日益增加,待處理的時空數(shù)據(jù)量呈現(xiàn)爆發(fā)性增長,為此裝配多核CPU以及高速存儲設(shè)備的高性能計算機(HPC)在GIS領(lǐng)域被廣泛采用。而常規(guī)方式下,利用Arcpy調(diào)用GP處理空間數(shù)據(jù)時,CPU不能滿載,即不能充分利用機能,導(dǎo)致數(shù)據(jù)處理效率偏低的問題愈發(fā)突出。并行處理是充分發(fā)揮多核CPU強大運算能力的有效途徑,因此實現(xiàn)GP的并行化執(zhí)行,不僅能延續(xù)其既有優(yōu)勢,還能實現(xiàn)一定硬件環(huán)境條件下地理數(shù)據(jù)處理效率的最大化,具有十分廣闊的應(yīng)用價值。
GP工具作為ESRI所打造的GIS平臺的核心組成部分,因其全面性、專業(yè)性和易用性,在諸多與地理空間科學(xué)有交集的學(xué)術(shù)和工程領(lǐng)域得到應(yīng)用。工具的使用方式包含了常規(guī)的ArcToolBox訪問[1]、ModelBuilder可視化編程方式[7],以及Arcpy獨立模塊訪問方式[8]。但這3種方式均無法自動利用計算機全部的運算能力,只因待執(zhí)行的多個GP工具任務(wù)將會以“串行”的方式逐個執(zhí)行,這是導(dǎo)致迭代型地理處理任務(wù)的執(zhí)行過程只利用了單核心運算能力,進而影響執(zhí)行效率的根本原因。Python是一種模塊化設(shè)計的,具有簡潔和高可讀性語法的高級編程語言[9],且支持多種方式的并行編程[10],Arcpy站點包的訪問方式在Python語言的框架內(nèi)實現(xiàn)了GP工具調(diào)用的高度靈活性,也為GP工具并行運行提供了可能。
利用Arcpy調(diào)用GP工具可分為函數(shù)方式和工具箱別名方式,兩者沒有本質(zhì)的區(qū)別,通常使用函數(shù)式[11]。函數(shù)以GP工具命名,參數(shù)在函數(shù)被調(diào)用時直接傳遞,若GP工具執(zhí)行完畢,函數(shù)將返回Result對象,執(zhí)行中斷則會拋出異常。
GP工具的執(zhí)行默認(rèn)為串行機制,用戶只能通過改變相關(guān)的隱含參數(shù),如默認(rèn)工作空間、矢量數(shù)據(jù)XY分辨率和容差、處理范圍等環(huán)境變量的方式干預(yù)執(zhí)行過程,起決定性作用的是非隱含輸入?yún)?shù)和輸入數(shù)據(jù)。其中,前者是指數(shù)值或字符串變量,后者指文件類數(shù)據(jù),即地理處理任務(wù)直接相關(guān)的數(shù)據(jù)文件。因此,根據(jù)數(shù)據(jù)文件在單個GP工具執(zhí)行前后的存在方式,可以將其分為以下四類,如圖1所示。
圖1 GP工具分類Fig.1 Classification of GP tools
a類工具的特點表現(xiàn)為,輸入數(shù)據(jù)被GP工具直接修改,無新的文件類數(shù)據(jù)輸出,如定義空間參考、字段計算工具等;b、c、d均有異于輸入文件的成果數(shù)據(jù)輸出,b中的輸入輸出端均為單一文件,如構(gòu)建柵格金字塔、投影變換等;c和d分別在輸入端和輸出端存在多種數(shù)據(jù)文件的情況,常見于合并和分割類GP工具。當(dāng)一個GP工具并行運行時,其所屬類型從整體看會有不同。如裁切工具本屬于b類,但若多個裁切結(jié)果需要放入同一個工作空間,則應(yīng)歸為c類。
基于Python語言的并行編程技術(shù)發(fā)展至今,形成了5種模式,分別是:異步編程模式、分布式并行模式、GPU并行模式、基于多線程的并行模式和基于多進程的并行模式[10]。其中,異步編程模式適用于因復(fù)雜任務(wù)中的子任務(wù)爭奪運算資源,而需要在程序運行期間協(xié)調(diào)CPU使用權(quán)的情況;分布式并行模式應(yīng)用于集群運算;GPU并行模式則應(yīng)用于圖形及科學(xué)運算[10]。因此,“多線程”和“多進程”是GP工具并行運行的主要實現(xiàn)方式。
線程是程序執(zhí)行的最小單位,而進程包含至少一個線程,是資源管理的最小單位,多線程之間共享內(nèi)存資源,通信相對容易,且生成新線程的開銷遠(yuǎn)小于新進程[12]。但是由于Python語言使用了全局解釋鎖(Global Interpretor Lock,GIL)[12],導(dǎo)致CPU在同一進程的同一時刻只會執(zhí)行一個線程。當(dāng)前GP工具默認(rèn)的串行執(zhí)行方式不能充分利用CPU運算能力,故希望并行狀態(tài)下,能讓多個工具分別利用CPU的多個核心,實現(xiàn)運算能力的最大化利用,因此“多進程”模式才能滿足需求。
目前,被ArcGIS產(chǎn)品所支持的Python語言版本為2.7或3.4,其標(biāo)準(zhǔn)庫中,以multiprocessing模塊為基礎(chǔ),可將待并行運行的代碼以函數(shù)形式傳遞給進程對象,從而實現(xiàn)“多進程”編程。對進程總數(shù)的動態(tài)控制方式分為進程池控制和逐個控制2種,前者通過multiprocessing模塊中的Pool對象實現(xiàn):在開始并行時,給定進程數(shù)量的最大值,整個并行過程,每個進程動態(tài)地獲取任務(wù),適用于待并行的函數(shù)代碼,此處即為GP工具,具有唯一性的情形;后者需要利用Process對象:在開始并行前,配置不同GP工具信息列表,并為不同工具分別啟動進程,且該進程在執(zhí)行一次后結(jié)束生命周期,適用多種工具的并行。由于地理處理任務(wù)中,不同類工具通常位于工作流的不同階段,多個工具并行的實際需求可以拆分為單個工具各自相繼并行,所以進程池控制方式適用范圍更廣。
并行運行的難點在于解決數(shù)據(jù)競爭、同步通信等關(guān)鍵技術(shù)問題[13]。而在基于Arcpy的地理處理任務(wù)中,類似問題需要根據(jù)GP工具的特點尋求相應(yīng)解決方案。
對于a類工具而言,由于處理過程直接修改輸入數(shù)據(jù)作為輸出數(shù)據(jù),沒有數(shù)據(jù)競爭以及通信問題。因此,利用multiprocessing站點包建立進程池,動態(tài)映射任務(wù),即可實現(xiàn)并行。其核心代碼如下:
import multiprocessing #導(dǎo)入多進程支持模塊
def dispose_parameters_list(): #編寫函數(shù)配置GP工
具所需的參數(shù)列表
……
return parameters_list #返回參數(shù)列表
def gp_tool(parameters): #編寫GP工具函數(shù),在其中實現(xiàn)具體的地理處理
……
if __name__=='__main__':
parameters_list = dispose_parameters_list #調(diào)用函數(shù)
p = Pool(multiprocessing.cpu_count) #以當(dāng)前CPU物理核心數(shù)作為最大進程數(shù),建立進程池
p.map(gp_tool, parameters_list) #映射函數(shù)與對應(yīng)的參數(shù),并啟動并行
p.close() #關(guān)閉進程池
p.join() #阻塞主進程
圖1中b、c、d三類工具都存在數(shù)據(jù)競爭的可能,由于進程具有相對獨立性,所以,改造后的GP工具執(zhí)行時,競爭將只會發(fā)生在輸入或者輸出端。
3.2.1 輸入端競爭型
為了保證GP工具在運行過程中,輸入地理數(shù)據(jù)的穩(wěn)定性,ArcGIS會為不支持共享方式訪問的數(shù)據(jù)資源設(shè)定文件獨占鎖[14],排斥處理過程中其他應(yīng)用程序或進程對數(shù)據(jù)的訪問,這是造成輸入端競爭的原因。表1列舉了常見的數(shù)據(jù)文件類型的獨占鎖特征。
表1 常用數(shù)據(jù)類型獨占鎖特征Tab.1 Characteristic of exclusive lock for common data type
為了消除輸入端競爭,也為了減少磁盤IO的時間消耗,需要利用multiprocessing模塊所包含的進程鎖以及ArcGIS所獨有的“內(nèi)存工作空間(in_memory)”特性。其中,進程鎖是保證共享資源在某一時期內(nèi),被某個進程獨占而避免訪問沖突的一種特殊對象。在創(chuàng)建進程時,給每個進程傳遞一個所有子進程可共享的全局對象進程鎖,當(dāng)某個進程需要訪問數(shù)據(jù)前,獲取進程鎖,確保對數(shù)據(jù)的獨占訪問,并在訪問完畢后釋放進程鎖,可保證數(shù)據(jù)在不同進程的GP工具執(zhí)行過程中的正常訪問和輸入?!皟?nèi)存工作空間”是一個可寫入工具輸出要素類、表和柵格數(shù)據(jù)集的、基于操作系統(tǒng)內(nèi)存的工作空間[15]。由于存儲于內(nèi)存中,所以其讀寫速度顯著優(yōu)于永久性存儲設(shè)備,常用于存放中間數(shù)據(jù)。在前述讀取數(shù)據(jù)的過程中,若被讀取的數(shù)據(jù)將被同一進程中反復(fù)使用,便可在首次讀取時,直接存放于內(nèi)存工作空間中,既避免了此后的數(shù)據(jù)競爭,更可提高處理效率。
為了使用進程鎖,需要在前述實現(xiàn)方式基礎(chǔ)上,編寫初始化函數(shù)并傳遞進程鎖。
from multiprocessing import Pool,Lock
……
def gp_tool(parameters):
lock.acquire()
#在獨占狀態(tài)處理輸入端數(shù)據(jù)
lock.release()
def init(l): #初始化函數(shù)
global lock #表明為全局變量
lock=l #用進程鎖給全局變量賦值if __name__=='__main__':
lock_in = Lock() #得到一個進程鎖對象……
p = Pool(multiprocessing.cpu_count(),initiali
zer=init,initargs=(lock_in,))#傳遞給每個新建
立的進程
p.map(gp_tool, parameters_list) #啟動并行
3.2.2 輸出端競爭型
c類工具常出現(xiàn)輸出端競爭,與輸入端競爭不同的是,此時需要以成果數(shù)據(jù)為導(dǎo)向,并根據(jù)中間數(shù)據(jù)的存在形式,設(shè)定相適應(yīng)的解決方案。
對于不受內(nèi)存工作空間支持的中間數(shù)據(jù),只能以進程鎖為基礎(chǔ),在每次執(zhí)行完畢前寫入目標(biāo)工作空間;相反,通過內(nèi)存工作空間存放中間數(shù)據(jù)則能優(yōu)化該過程,將數(shù)據(jù)的磁盤寫入次數(shù)減少至并行進程總數(shù),為此需要確定寫入時機。通過提前為每個進程確定任務(wù)列表,以多進程間共享內(nèi)存的Value對象記錄任務(wù)進度,在進程生命周期結(jié)束前,將內(nèi)存工作空間中的數(shù)據(jù)寫入目標(biāo)工作空間。核心代碼如下所示:
from multiprocessing import Pool,Value def init(c,l): #初始化函數(shù)
global counter,lock
counter = c; lock = l#用進程鎖給全局變量賦值def gp_tool(parax):
counter.value+=1 #更新已分配任務(wù)數(shù),可掌握處理進度……#將單個進程每次處理的成果數(shù)據(jù)存放在’in_memory’空間
#若子任務(wù)清單已完成則轉(zhuǎn)移數(shù)據(jù)至目標(biāo)工作空間if __name__=='__main__':
counter_in = Value('i',0) #創(chuàng)建為整型變量
lock_in = Lock() #得到一個進程鎖對象#分拆任務(wù)總清單為與進程數(shù)相協(xié)調(diào)的子清單#如paras 分拆為 para0 para1等
p = Pool(multiprocessing.cpu_count(),initiali zer=init,initargs=(counter_in,lock_in))
#傳遞給進程池中的進程
i = 0
for i in range(0, multiprocessing.cpu_count()):
p.apply_async(gp_tool, (eval('para'+str(i)),))#以非阻塞方式并行
i+=1
選用大比例尺地形圖生產(chǎn)中常見的“按照標(biāo)準(zhǔn)分幅圖框裁切總數(shù)據(jù)庫”的地理處理任務(wù),構(gòu)建前述并行運行方案,在一臺當(dāng)前常見的工作站上對比測試傳統(tǒng)方式與并行運行方式的硬件資源使用情況及數(shù)據(jù)處理效率。測試平臺的關(guān)鍵參數(shù)見表2。
表2 平臺配置Tab.2 Configuration of test platform
待處理數(shù)據(jù)庫為某測區(qū)1:500地形圖MDB總數(shù)據(jù)庫,文件體積約50兆字節(jié),實際面積約25km2,共包含30個要素類,使用測區(qū)范圍內(nèi)100個250m×250m標(biāo)準(zhǔn)分幅圖框,以不同方式裁切該庫,選取圖號作為文件名,裁切結(jié)果保存為GDB格式數(shù)據(jù)庫,得到以下測試數(shù)據(jù)。
測試結(jié)果表明,傳統(tǒng)方式耗時最長,硬件資源使用率最低;1個進程的并行模式實為串行執(zhí)行,但因為利用了“內(nèi)存工作空間”特性,減少了磁盤IO次數(shù),效率提高較顯著;開啟8個進程利用多核處理能力后,耗時縮減至傳統(tǒng)方式的1/5,此時CPU負(fù)載過半,但加倍進程數(shù)至16個,處理效率提升不明顯,此時瓶頸為硬盤IO負(fù)載。
表3 測試結(jié)果Tab.3 Test result
傳統(tǒng)運行方式下,地理處理任務(wù)中的多個GP工具會以“串行”的方式逐個執(zhí)行,不能充分利用多核高性能計算機的運算資源,導(dǎo)致任務(wù)執(zhí)行效率低下。通過基于Python的Arcpy站點包方式調(diào)用GP工具可以實現(xiàn)并行運行,綜合分析后得到“多進程模式”是適用于GP工具并行運行的Python并行編程模式。利用multiprocessing站點包的Pool類實現(xiàn)了基礎(chǔ)形式的并行,并利用Lock進程鎖、Value進程通信以及非阻塞并行方式,消除了輸入端競爭和輸出端競爭,結(jié)合ArcGIS所獨有的“內(nèi)存工作空間”特性構(gòu)建的解決方案,達(dá)到了工具執(zhí)行效率的最大化。測試結(jié)果表明,并行運行的效率較傳統(tǒng)方式提升顯著,若能突破硬盤IO瓶頸,則效率可進一步提高。