2010-06-09

程式開發工具隨意簡介()->1.

現在CPU因為時脈還有加速機制的設計碰到發展瓶頸,單Core性能提身不同於十年前一日千里的態勢,但是依然在逐漸縮小的製程,卻拓寬了處理器單晶片多核心的發展路線,使得今日多核心CPU隨處可見。所以即使是普羅大眾在用的程式工具,都得充份運用多顆CPU來提升程式效率。然而在大多數使用者享受程式的便利性時,又有多少人知道,要做個能夠充份運用多核心,甚至是多台電腦運算資源的程式有多困難。畢竟多個程序同時在不同CPU執行時,中間的溝通往往是少不了的,要找到能讓多個同時執行的程序之間不用互相交換資訊的場合,幾乎可以說是沒有,如何設計出一個程序間能高效率溝通的程式,就成為十分重要的課題。
當前平行運算程序之間交換資訊的主流方法,大致上可分為共享記憶體區域(share memory)以及訊息傳遞(message passing)兩種,採用前者的程式目前多存在於單機電腦環境,而後者則多存在於server farm,通訊機房,或是超級電腦等環境。採用共享記憶體區域策略的平行運算程式,特別是一般常見的多執行緒(multi-thread)程式,即使執行點不同,卻依然共用同樣的記憶體區域,因此在資料的交換和共用上十分便利且有效率,一個執行點只要把需要交手給其他執行點數值或是資料放在記憶體中,別的執行點就能很方便的拿到,然而因為程式中各個執行點跑到哪裡,存取同個記憶體位置時誰先誰後,都不如單執行點的程式一樣規律,所以沒有掌握到執行點的動向而導致程式不穩定或是當機的情形,可說是時有所聞。採用訊息傳遞方法的程式中,各個程序之間的溝通只能使用規定的管道(pipe,buffer,TCP/UDP)傳輸資料給別的程序,每個程序則各自擁有獨立、不受干擾的記憶體空間,因此簡化了執行點之間的互動與溝通關係,但反面來說,訊息傳遞的方式就得犧牲程式交換資料的效率,任何資料都得複製或是搬移到傳遞給其他程序的溝通介面,才能夠進行資料交換,更不用說有時還會碰到很難採用這種設計的情形。
前面說了這麼多,其實這系列文的重點是要介紹一些跟平行處理有關的程式工具,從lib到程式語言本身內建的都有,不過這裡只做粗淺的介紹,讓大家知道有這樣的一個東西,至於詳細的使用方式就請大家自己去找書還是電子文件檔來看吧,總之第1篇是這個在電信業或是網路服務打滾的人應該都有聽過甚至用過的Erlang。
Erlang誕生於1986年,當初是Ericsson針對通信系統服務而特別設計的程式語言。名稱由來據說是為了紀念丹麥數學家Agner Krarup Erlang,另一個說法是因為這個語言是由Ericsson催生的,故名為Ericsson Language。由於當初Erlang是特別為電信服務打造的程式語言,因此官方的執行環境才會命名為OTP(Open Telecom Platform)。據說現在各國著名電信業相關公司有不少家都已經在他們的系統中使用這個程式語言了,不過可以確定的是用在Internet服務的也不少,像是facebook的網路服務就有局部採用Erlang,plurk的comet server也有用到Erlang。
Erlang程式碼大概長這個樣子(摘自wikipedia的Erlang條目的快速排序範例):

%% quicksort:quicksort(List)
%% 依順序排列一串資料
-module(quicksort).     % 這個原始檔的檔名為 'quicksort.erl'
-export([quicksort/1]). % 將 'quicksort' 這個函數export出去,提供外來程式呼叫

quicksort([]) -> []; % 如果串列是 [] (空串列),回傳 []
quicksort([Pivot|Rest]) -> % 若串列不是空的,則取出串列第一個項目 'Pivot',剩下的為 'Rest'
% 比 'Pivot' 小/大的項目分別遞迴給下一層排序,各自回傳排好的串列後,
% 再依照'比Pivot小','Pivot','比Pivot大'的順序連接成大串列。
% 建立一個串列,串列成員為 'Rest' 串列中小於 'Pivot' 者
qsort([Front || Front <- Rest, Front < Pivot])
    ++ [Pivot] ++
    qsort([Back || Back <- Rest, Back >= Pivot]).

如果熟悉同樣是declarative language,但是分類為logic programming的Prolog,就會發現這個東西的語法實在是超像Prolog的。不可否認的,Erlang是由Prolog改良而來的程式語言,只是Prolog的運算結果本來是放在未知參數中的,而在求解的過程中推得未知參數的資料為何;而Erlang改為回傳運算結果,並且引入了許多lambda calculus的特性,使得Erlang雖然有著和Prolog類似的敘述式,符號,關鍵字,以及變數命名法則(大寫開頭的英文單字為變數,小寫的為atom),但是骨子裡卻是個十分道地的functional programming語言。由於Erlang採用訊息傳遞做為concurrency程序之間溝通的唯一方式,為確保所有函數都不能有side effect(也就是函數被呼叫過後,函數的狀態不會有任何改變)以減少平行運算時side effect造成的問題,所有的變數一旦指定了數值,就不能再更改,寫習慣imperative language的人一定會對不能使用A=A+1這種寫法感到很莫名其妙,但是這也是functional programming語言能輕易設計成平行運算的重要法寶。
因為Erlang統一採用訊息傳遞做為程序間溝通的方式,所以訊息傳遞的機能基本上都是語言內建的,甚至還可以傳遞訊息到遠端的機器。我們可以從這個例子看出來(摘自wikipedia的Erlang條目的distributed processes範例):

% 啟動新的process並執行web:start_server(Port, MaxConnections),spawn(),函數會傳回ServerProcess
ServerProcess = spawn(web, start_server, [Port, MaxConnections]),

% 向ServerProcess傳送訊息,內容為一個tuple,tuple內容依序為atom pause與整數10
ServerProcess ! {pause, 10},

% 接收來自其他process的訊息
receive
a_message -> do_something;
{data, DataContent} -> handle(DataContent);
{hello, Text} -> io:format("Got hello message: ~s", [Text]);
{goodbye, Text} -> io:format("Got goodbye message: ~s", [Text])
end.

當process因為bug或記憶體出錯等諸多原因當掉時,Erlang的錯誤機制能輕易防止掛掉的process拖垮其他process,這也是為什麼其開發者寫Erlang的書時拍胸保證其穩定性,號稱採用Erlang的系統幾乎沒有造成程式停止的嚴重當機過。
Erlang除了平行處理機能非常強大以外,另外一個最可怕的地方是可以在程式不停止執行的狀況下換上新版的程式模組,這對於不能停機的網路服務而言無疑是一大利器,若運用了Erlang中的熱抽換特性,就不需要為了新增服務而公告主機停機維護。
不過Erlang其實也有很多缺陷,例如OTP到R13A之後才支援Unicode的處理,而且還不是很完善(這會不會太瞎了),沿用自Prolog的符號和語法導致新手水土不服,缺乏強大便利的向量運算機能,和影音訊號處理還是電腦圖學等領域相性不合。但是其穩定度和平行處理的方便性,還是很受網路服務以及通訊業界的喜愛。也希望已經體會到Erlang強大的使用者繼續支持,而到現在還不知道有這東西的人,就利用空閒時間試試看這神奇的程式語言吧。

1 則留言: