Erlang語言研究綜述

摘 要: 本文前半部分主要是關於Erlang編程語言相關的內容;着重就通常學習編程語言的通常的關注點來闡述了Erlang編程語言的基本語法點,主要包括:Erlang的變量、Erlang的數據類型、Erlang的語句和Erlang編程語言的函數與模塊四個方面;本文的後半部分主要就Erlang語言的並行化編程的實踐:Erlang的並行化編程與Erlang並行化編程在矩陣乘積的實際應用,經過實踐,能夠發現,Erlang語言確實在並行化編程方面表現得很優秀。css

關鍵詞:並行計算;Erlang;編程語言;矩陣相乘java

      自從計算機出現的時候開始,其就迅速發展。短暫的幾十年,計算機已經從當初的單核走過,向多核發展,直到如今的多機系統。提高計算機的計算性能和計算速率一直是計算機發展的不變的主題,可是隨着單位面積晶體管數目的飽和,單純的以晶體管的數目來衡量計算機的發展水平已經成爲過去,單純地以增長單位面積晶體管的數目來提高計算的性能已經走到了盡頭,就像C++標準委員會主席Herb Sutter所說的那樣咱們的《免費的午飯已經結束》,計算機的發展開始向着多核的方向發展,可是爲了達到必定的計算速率,僅僅是依靠多核來提高的代價是昂貴的,且依靠這樣的方式有必定的侷限性。爲了節省成本與獲得更大的計算性能,多機系統逐漸出現。利用多個通常的計算核心來組成計算機集羣進行並行計算或分佈式計算來提高計算的能力,獲得更高的計算能力。一個典型的應用的例子就是雲計算。Google實踐代表,用廉價的服務器組成的服務器集羣,在計算能力、可靠性等方面達到了價格昂貴的大型計算機的水準,毫無疑問,這是大型、超大型網站和網絡應用的福音。c++

      在國家發展方面,高性能計算的發展也是必要的。目前,如氣象、 石油勘探、生物製藥、核爆模擬等國家命脈的科學研究,都離不開高性能大規模的並行計算。在這些科研活動中,須要大量的科學計算。提高在這些科學研究中的計算就是提高我國的綜合實力,極大提高我國在國際中的地位。程序員

1 引言

       目前發展的大趨勢是並行計算。爲了從新利用目前的並行化硬件資源,須要並行化的軟件編程語言支持。Herb Sutter曾表示,若是一門語言不可以以優雅可靠的方式處理並行計算的問題,那它就失去在21世紀的生存權。「主流語言」如C/C++、Java等傳統的編程語言固然不想喪失這個生存權,分別以不一樣的方式來適應將來的並行計算的挑戰。如C/C++,除了標準委員會致力於以標準的並行化庫來支持並行計算,標準化的OPENMP和MPI。以及Intel的Threading Building Blocks庫也都爲並行計算提供了可靠的優雅的解決方案;Java在其的5.0版本引入了意義重大的concurrency庫,來支持Java的並行化,得到了Java社區的追捧;而微軟更是採用了多種技術手段來應對並行計算時代的到來:先是在.Net裏面引入了APM(Asynchronous Programming Model),隨後又在Robotics Studio中提供了CCR(Concurrency and Coordination Runtime)庫,後來還發布了Parallel FX和MPI.Net。目前的主流的語言,雖然如今提供了對並行化的支持,但都是後來的拓展,原來的架構仍是不支持並行化計算,強加的並行化支持打亂的人們之前的使用習慣。可是有一門語言,在誕生之初就支持並行化程序設計¾Erlang編程語言。算法

      Erlang語言誕生很早,大概與Perl語言同歲,比C++年輕四歲,長Java十多歲。以前由於計算的串行化,雖然Erlang存在可是沒有流行起來。可是隨着並行計算的出現與發展,Erlang在並行化環境下,其優秀的並行化計算才真正體現了出來,引發了人們的注意,才逐漸爲人們所熟知。shell

2 Erlang相關概念

2.1 Erlang簡介

      Erlang得名于丹麥數學家及統計學家Agner Krarup Erlang,同時Erlang還能夠表示Ericsson Language。Erlang並不是一門新語言,它出現於1987年,只是當時對併發、分佈式需求尚未今天這麼廣泛,當時可謂英雄無用武之地。Erlang語言創始人Joe Armstrong當年在愛立信作電話網絡方面的開發,他使用Smalltalk,惋惜那個時候Smalltalk太慢,不能知足電話網絡的高性能要求。但Joe實在喜歡Smalltalk,因而定購了一臺Tektronix Smalltalk機器。但機器要兩個月時間纔到,Joe在等待中百無聊賴,就開始使用Prolog,結果等Tektronix到來的時候,他已經對Prolog更感興趣,Joe固然不知足於精通Prolog,通過一段時間的試驗,Joe給Prolog加上了併發處理和錯誤恢復,因而Erlang就誕生了。1987年Erlang測試版推出,並在用戶實際應用中不斷完善,於1991年向用戶推出第一個版本,帶有了編譯器和圖形接口等更多功能。1992年,Erlang迎來更多用戶,如RACE項目等。同期Erlang被移植到VxWorks、PC和 Macintosh等多種平臺,兩個使用Erlang的產品項目也開始啓動。1993愛立信公司內部獨立的組織開始維護和支持Erlang實現和Erlang工具。編程

       Erlang是一種通用的面向併發的編程語言,它由瑞典電信設備製造商愛立信所轄的CS-Lab開發,目的是創造一種能夠應對大規模併發活動的編程語言和運行環境。Erlang問世於1987年,通過十年的發展,於1998年發佈開源版本。Erlang是運行於虛擬機的解釋性語言,可是如今也包含有烏普薩拉大學高性能Erlang計劃(HiPE)開發的本地代碼編譯器,自R11B-4版本開始,Erlang也開始支持腳本式解釋器。在編程範型上,Erlang屬於多重範型編程語言,涵蓋函數式、併發式及分佈式。順序執行的Erlang是一個及早求值, 單次賦值和動態類型的函數式編程語言。服務器

      Erlang是一個結構化,動態類型編程語言,內建並行計算支持。最初是由愛立信專門爲通訊應用設計的,好比控制交換機或者變換協議等,所以很是適合於構建分佈式,實時軟並行計算系統。使用Erlang編寫出的應用運行時一般由成千上萬個輕量級進程組成,並經過消息傳遞相互通信。進程間上下文切換對於Erlang來講僅僅只是一兩個環節,比起C/C++程序的線程切換要高效得多得多了。網絡

Erlang具備併發性、分佈式、健壯性、軟實時性、熱代碼升級、遞增式代碼裝載、快速準確暴露錯誤、面向併發性編程、函數式編程等特色。這些特色都會在Erlang的應用中有很好的體現。數據結構

2.2 Erlang基本語法

        Erlang是一種函數式編程語言。函數式編程是一種編程範型,它將計算機的運行視爲函數的計算,避免了變量與狀態的概念。咱們經常使用的C/C++、Java等是指令語言。

        Erlang的運行環境能夠是基本的shell或編輯成erl文件而後編譯運行。如下對兩種基本的方式進行說明。這裏假設你已經安裝好Erlang運行環境。

      A. Shell下運行Erlang代碼

      打開shell,輸入erl & enter進入Erlang的shell運行環境,如圖2-1。

圖2-1 進入Erlang的運行環境

能夠看到,我使用的Erlang的版本是17.退出能夠用鍵盤組合按鍵^G退出shell的Erlang運行環境。

      1>表示目前的運行環境是準備就緒,能夠輸入代碼運行了。前面的數字會隨着輸入代碼的成功運行的數目的增長而遞增的。如圖2-2展現的那樣,先輸入錯誤的代碼:hello world.,其會提示錯誤後繼續準備接受輸入,可是前面的數字並無發生變化,接着,輸入能夠接受的「hello world」.,則輸出「hello world」,同時前面的數字加1,變成2。

圖2-2 基本的輸入演示

       在輸入的時候,要注意在每一個語句的後面都有一個小圓點(.),是不能省略的,否則的話,控制檯就會繼續等待輸入,詳細的會在後面介紹。

        B. Erlang源代碼文件的執行

         Erlang源代碼的文件後綴爲.erl,每一個文件就是一個模塊。hello.erl實例代碼以下:

-module(hello).

-export([hello_world/0]).

hello_world()->
io:format("Hello world~n").

       進入Erlang運行環境進行編譯,返回{ok,hello}則編譯經過。並運行hello:hello_world().則輸出Hello world,如圖2-3所示。

圖2-3 模塊編譯與運行

        以上就是兩種運行Erlang代碼的方式,在後面的實驗中,會採用以上的其中的一種。爲了不後面的過多的截圖,在這裏約定,輸入與輸出按照以下的方式來表示,輸入和輸出的內容用斜體來表示,且前面以加粗的輸入/輸出來加以提醒。以運行hello_world()爲例:


輸入:輸入的代碼

輸出:輸出的運行結果

輸入:hello:hello_world().

輸出:hello world

ok


2.2.1 Erlang的變量

       在Erlang中,變量的首字母都是大寫的,且變量是一次性「賦值」,不可改變的。在大多數的語言中,=表示的是變量賦值,可是在Erlang中=表示的是模式匹配。在Erlang中,Lhs=Rhs的真正的意思是:先計算Rhs的值,而後將Rhs的值與Lhs進行匹配。

       變量(好比X)是模式的一種簡單的形式。X在第一次執行X=SomeExpression的時候,Erlang會對本身說,我要作些什麼才能使這句話真?由於X尚未值,它能夠綁定SomeExpression在X上,這條語句就成立了。而後,咱們再執行X=AnotherExpression的時候,Erlang就會將AnotherExpression的值與SomeExpression的值進行匹配,若相同則成功;不然,失敗。以下例:

輸入X=23. 輸出23

輸入X=43. 輸出** exception error: no match of right hand side value 43

上述例子中,先輸入X=23.則第一次匹配成功;再輸入X=43.的時候就會報匹配失敗的錯誤。

2.2.2 Erlang的數據類型

      Erlang的數據類型有數值(整數和浮點數)、二進制串/位串、原子、元組、列表(和字符串)、惟一標識符(端口,pid,引用)、Fun函數。

A. 數值

      Erlang的數值有整數和浮點數。Erlang的整數沒有大小的限制,較小的數字會被放置在單個機器字裏面,較大的數字是按需分配的,對程序員是透明的。Erlang的浮點數採用的是IEEE 745-1985格式(雙精度),其語法與大部分的編程語言是相同的,可是Erlang要求必須以數字開頭。

      Erlang的數值運算也有+、-、*、/等,其操做與通常的是操做的順序和意義是相同的,在這裏再也不贅述。

B. 二進制串/位串

      在Erlang中,二進制串是無符號8位字節的序列,用於存放和處理數據塊。位串是廣義的二進制串,其長度沒必要是8的整數倍,如3/2個字節的長度是12位。

    二進制串的基本的語法以下:

<<0,1,2,3,4,…,255>>

    也就是一個包含在<<…>>內的逗號分隔的整數序列,整數的取值範圍是0~255。兩邊的分隔符是兩個連續的小於號(大於號)。固然,咱們還能夠用字符串來構建二進制串,如:

<<」hello」,」zhangwenwen」>>

     只要字符串裏面的字符的編碼是ASCII/Latin-1編碼的,其取值在0~255之間就OK。

     位串的基本的語法是:

<<Segment1,Segment2,…,SegmentN>>

其中,SegmentN能夠是如下方式的中的一種:

Data

Data:Size

Data/TypeSpecifiers

Data:Size/TypeSpecifiers

在以上的模式中,Data指的是實際的數據,Size指的是位串的大小,TypeSpecifiers指的是位串的一些解碼的細節,如大小端等的定義。

C. 原子

在Erlang中,原子是一種僅由字符序列來標識的特殊字符串常量,所以兩個原子只要具備相同的字符串表示,就徹底等同。

       一般狀況下,原子以小寫字母開頭,如hello,world等都是原子,在開頭字母以後可使用大寫字母,@,#等其餘的字符,可是爲了在其餘的狀況下引發歧義,可能在定義的時候要引入單引號定義,如:

‘this is an atom’

若不加入單引號就會報錯。

      原子就像是Java或C語言中的enum類型,你能夠在任何地方使用,且不須要事先定義。

D. 元組

元組是Erlang中的定長序列。元組用大括號來定義,如:

{1,2,3}

{one,two,three,four}

{}

      元組的元素數目能夠是0、1或多個。

E. 列表(和字符串)

     列表是Erlang各類數據結構中的主力軍---這點在大部分函數式編程語言都相通的。這是由於列表簡單、高效、靈活和自然遵循引用透明性。

以[]來定義列表,以下:

[]

[1,2,3]

[「about」,」boy」,」cow」,」dog」]

      還有一種特殊的列表就是字符串,在Erlang中,用雙引號來定義,與其餘的編程語言相似:

「hello world「

[104,101,108,108,111,32,119,111,114,108,100]

等價,也能夠寫成

[$h,$e,$l,$l,$o,$\ ,$w,$o,$r,$l,$d]。

F. 惟一標識符(端口,pid,引用)

         Erlang支持進程編程,任何代碼都須要一個Erlang進程做爲載體才能執行。每一個進程都有一個惟一的標識符,一般稱爲pid。Pid是一種特殊的Erlang數據類型,應被視爲一種不透明對象,但shell會以<0.35.0>這樣的格式來打印一個pid。在shell中你不能用這個語法來建立pid,該格式僅用於調試目的,方便對pid的比較。

端口標識符:端口和進程差很少,只是還能與外部通訊。Shell打印端口的格式是#Port<0.472>.

在Erlang中能夠用make_ref()來生成,其shell的輸出格式爲#Ref<0.0.0.39>

G. Fun函數

       Erlang被稱爲函數式語言,這類語言的一個顯著的特徵就是能夠像處理數據同樣處理函數---也就是說,函數式能夠稱爲其餘函數的輸入,也能夠稱爲其餘函數求值結果,還能夠把函數存儲在數據結構中供後續的使用。在Erlang中,將函數包裝成數據的函數叫作Fun函數。

2.2.3 Erlang的語句

       在Erlang中,對標點符號的使用與主流的編程語言是有差異的,在Erlang中使用標點符號的方式更像是在咱們平常生活中交流語言所使用的標點符號的習慣。

       在Erlang中,語句的結束用」.」而不是通常的編程語言中的」;」,」;」表示的是語句的短暫的停頓,若是是一系列語句,這些語句之間表示的是一種「或」的關係;而在Erlang中,」,」表示的是一種「且」的關係。

       在Erlang中,語句塊有:if、case、try…catch.

If語句的通常格式是:

if
Guard1 –> Expr_seq2;
Guard2->Expr_seq2;
…
end.

其中,Guard在Erlang中指的是條件判斷語句。

Case語句的通常格式:

case Expression of
Pattern1 –>Expr_seq1;
Pattern->Expr_seq2;
…
end.

Case語句與通常的編程語言的原理是同樣的,只不過是換了不一樣的表達格式而已。

Try…catch語句主要是爲了抓取語言的異常信息,通常的格式以下:

try FunOrExpressionSeq of
Pattern1 ->Expressions1;
Pattern1 ->Expressions1;
…
catch
ExceptionType1:ExPattern1->ExExpressions;
ExceptionType1:ExPattern1->ExExpressions;
…
after
AfterExpressions
end.

與java的try…catch的結構相似:

try {
…
} catch (Exception e) {
…
} finally {
// TODO: handle finally clause
…
}

after與final等價。

在Erlang中沒有for語句,可是咱們能夠用列表來模擬for語句達到的效果。

2.2.4 Erlang的函數和模塊

     在Erlang中,函數是最基本的功能單元,其定義也是很簡單的。如兩個數相加sum(A,B)的定義以下:

sum(A,B) ->A+B.

    Erlang的函數沒有像其餘語言那樣有明確的返回值,可是每個都會有一個返回值,爲最後一個表達式的計算的值。在Erlang中,函數的函數名和函數主體的分隔符爲 –>.

    在Erlang中,存放代碼的地方是模塊(module)。Module至關於java語言中的類,是一系列方法的集合,下面是一個基本的模塊的定義格式:

%% @author zhangwenwen
%% @doc @todo Add description to demo.
-module(demo).
%% ===================================================
%% API functions
%% ===================================================
-export([funcdemo/1,max/2]).
%% ===================================================
%% Internal functions
%% ===================================================
f(A,B) ->A+B.
funcdemo(Test) ->Test.
max(A,B) when A>=B ->A;
max(A,B) when A<B ->B.

在上述代碼中

%(%%)表示爲Erlang的註釋,在Erlang中沒有塊註釋,只有行註釋,以%開頭;

-module指的是該模塊的名稱,與模塊的文件名相同;

-export指的是能夠在外部訪問的API,導出的是一個列表,元素的格式是 函數名/參數的個數;

module文件的本體是相關API的實現。

注意:在模塊源文件中,-module和-export前面還有字符‘-’,初學者應該在編寫模塊的時候注意,比較容易忽略。

以上內容簡單介紹了Erlang編程語言的順序編程的基本的概念和數據結構,詳細的語法規則請參考相關Erlang編程書籍。

3 Erlang並行編程

        從Erlang的發展的歷史咱們知道,Erlang在很早就出現了,可是其並非被大多數人知道,儘管它仍是開源的。可是,近年來,隨着並行化硬件的發展,Erlang的得天獨厚的特色使得其逐漸被人們所瞭解。

可見,Erlang在並行化編程方面有很大的優點和很好的應用前景。在本節,就是對Erlang的並行編程的進行相關的介紹。

3.1 現實中的並行

       在咱們生活的這個世界是並行的,咱們參與的一切活動都是並行的,咱們的大腦對並行也有着深入的理解。大腦裏有一個稱爲「杏仁核」的區域讓咱們對刺激作出迅速的反應。咱們在高速路上開汽車的時候,有了須要踩下剎車的想法的時候咱們已經踩下了剎車。若是咱們首先是想應該踩下剎車,而後再去執行的話,咱們多半已經躺在了醫院裏或者是蹲在了交警大隊裏了。

      並行能夠提升資源的利用率,能夠節省一件工做的工做時間。咱們一直在強調團隊的合做的重要性,就是由於在一個團隊裏面,一個工做被分配給不一樣的個體,各個個體之間進行並行完成,縮短了工做的時間間距。若是全部的這些工做都是由一我的來完成,那麼工做的時間間距將大大增長。並行是必要的,是提升工做效率很好的方式。

3.2 Erlang中的並行

      在計算機世界裏,並行一樣重要。經過並行化完成任務來完成任務來節省時間是目前最新的計算髮展趨勢。之前,在特定的時間完成特定計算的不可能經過對並行的引入已經變成了可能。

      Erlang的並行是基於進程的。進程是一些獨立的小型的虛擬機,能夠執行Erlang函數。Erlang的進程隸屬於編程語言,而不是操做系統。這就意味着,Erlang的進程在任何操做系統上都具備相同的邏輯行爲,咱們就能夠編寫可移植的併發代碼。Erlang的進程與進程之間是獨立的個體,不進行內存共享,而是經過消息傳遞來傳遞數據。Erlang進行不共享內存,在使用內存的時候就不存在內存鎖來防止衝突。一個Erlang程序可能有幾十個、幾百個甚至幾十萬個小的進程。全部的這些進程都是獨立運做的。每一個進程都有一塊獨立的內存區域。它們表現的就像是在一個很大的房子一大羣喋喋不休。

      這樣就使得Erlang程序天生易於管理和擴展。假如咱們有10個這樣的進程,而它們有太多的工做要作,咱們怎麼辦?固然是找更多的進程過來。那麼,如何管理這些進程呢?很簡單,就是大聲將消息廣播給它們就能夠了。

3.3 Erlang的並行編程

     利用Erlang進行並行化編程是十分簡單的。要想編寫並行的Erlang的程序,只須要三個新的基本的函數:spawn、send和receive。spawn函數建立一個並行進程,返回一個咱們在前面提到的數據類型pid;send向某個進程發送消息;receive則是接收消息。

spawn函數的使用格式:

Pid=spwan(Mod,Func,Args).

     建立新的併發進程來執行Mod:Func(Args)。這個新建立進程和調用進程並列運行。spawn返回一個Pid,process identifier的簡稱,即進程標識符。可使用Pid來給此進程發送消息。

     在模塊內部能夠直接使用Pid=spawn(Func)來建立一個並行的進程,Func能夠不導出。這種形式的spawn老是被執行Func的最新值。

      Pid!Message:向標識符爲Pid的進程發送消息Message。消息發送是異步的,發送方並不等待,而是會繼續以前的工做。!被稱爲發送操做符。Pid!M的值被定義爲M,Pid1!Pid2!Pid3!…!Pidn!M,由於是從後往前計算,前面的表達式就是將消息M發送給全部的進程Pid一、Pid二、…、Pidn。

receive … end,接收某個進程發送來的消息,它的語法以下:

receive
Pattern1 ->Expressions1;
Pattern2 ->Expressions2;
…
end

當某個消息達到時,系統會嘗試將收到的消息與Pattern進行匹配,若匹配成功,就執行後面的表達式;若是匹配不成功,就匹配後面的pattern…。若是沒有匹配成功的模式就會將消息保存起來後面來進行處理。

下面以一個簡單的計算面積的實例來對以上的函數進行簡單的使用。模塊的文件名稱是area_server0.erl,具體的代碼以下:

%%模塊的定義
-module(area_server0).

%%導出的API,在外部能夠進行訪問
-export([loop/0]).

%%循環接收消息的函數
loop() ->
receive
{rectangle,Width,Height} ->
io:format("Area of rectangle is ~p~n",[Width*Height]),
loop();
{square,Side} ->
io:format("Area of square is ~p~n",[Side*Side]),
loop();
{circle,Radus} ->
io:format("Arae of circle is ~p~n",[3.14159*Radus*Radus]),
loop()
end.

編譯並在shell中輸入運行如圖3-1:

圖3-1 進程建立以及消息發送

       能夠在以上的基礎上,對代碼進行進一步的包裝,將spawn函數徹底封裝在模塊的一個函數裏面,實現全面的封裝,將消息發送也封裝在模塊中。經過以上的實踐,咱們能夠知道,Erlang確實是能夠很好地進行並行編程。

4 Erlang並行編程實例

      之因此並行計算在這幾年發展迅速,是由於其併發能夠很好很好實現計算的加速。在本節中,經過實驗說明並行計算確實是能夠提高計算的速度,減小計算的時間;同時也是爲了驗證Erlang的並行編程的實際應用的能力。

4.1 問題提出與分析

      利用串行與並行的方式進行大矩陣的乘法並比較這兩種狀況下運行的時間,並與理論運行時間值做出比較,得出結論。

      本實驗的目的是爲了驗證串行的矩陣乘法與並行的矩陣乘法的運行時間的比較,比較的前提是矩陣的大小是相同的。目前,假設有矩陣AM*N和矩陣BN*Q,相乘的結果是CM*Q

串行的數據矩陣乘積採用的是傳統的矩陣相乘的算法:

結果C的每一項是對應的A與B的行與列的乘積的和。

      而並行計算的矩陣乘積這裏採用的是將矩陣A的每一行發送給每個進程與矩陣B相乘,獲得結果C的相應的行列。

4.2 求解過程

實驗的環境:操做系統 Windows 7 旗艦版 Erlang/OPT版本:18.0

硬件設施:CPU:Intel(R) Core(TM)i5 CPU M480 @2.67GHz

內存:8GB 內存

A. 隨機生成矩陣M*N的矩陣,在實驗中爲了方便,僅僅生成的是M*M的方陣,生成方陣的代碼以下:

generateMatrix(M,N) ->
row(M,N,[]).

row(M,N,Result) ->
case 0=:=M of
true ->Result;
false->row(M-1,N,Result++[column(N,[])])
end.
column(N,Result) ->
case 0=:=N of
true ->Result;
false->column(N-1,Result++[random:uniform(10)])
end.

B. 分別進行串行與並行計算矩陣A與矩陣B的乘積,相關的代碼以下:

串行計算的相關的代碼:

singleProcess(A,B)->
_R=lists:foldl(fun(Row_A,Acc)->Acc++[rowMultiplymatrix(Row_A,B,1,[])]end, [], A).

並行計算的相關的代碼:

multiProcess(A,B) ->
P=self(),
lists:foldl(fun(A_Row,_Acc)-> spawn(fun()->P!{self(),rowMultiplymatrix(A_Row,B,1,[])} end) end, [], A),
receiveMessage(length(A),[]).

其餘相關的調用的代碼:

rowMultiplymatrix(Row_A,Matrix_B,Count_Col,Result)->
case Count_Col=:=length(lists:nth(1, Matrix_B))+1 of
true ->Result;
false ->rowMultiplymatrix(Row_A,Matrix_B,Count_Col+1,Result++[multiply(Row_A,getColume(Matrix_B,Count_Col),0)])
end.
receiveMessage(Count,Result) ->
receive
{_Pid,_Result}->
case Count=:=1 of
true ->Result++[_Result];
false ->
receiveMessage(Count-1,Result++[_Result])
end
end.

  

C. 首先用較小的矩陣來驗證代碼的正確性

1. 生成4*4的矩陣A和矩陣B,生成的結果以下:

生成的矩陣A的數據以下:

[[1,8,4,9],[9,8,2,4],[3,7,4,5],[7,2,8,9]]

生成的矩陣B的數據以下:

[[3,9,9,6],[9,6,3,10],[10,8,1,3],[9,3,4,9]]

2. 將矩陣A和矩陣B分別按照串行並並行的方式來計算獲得矩陣C。

串行計算的矩陣乘積以下:

["ÄtI³",[155,157,123,176],[157,116,72,145],"Ȧq§"]

並行計算的矩陣乘積以下:

["ÄtI³",[155,157,123,176],[157,116,72,145],"Ȧq§"]

      在上面的結過中,咱們發現有「ÄtI³」、「Ȧq§」等可見的字符,這是正確的。在前面咱們已經提到過,Erlang中的字符串是特殊的列表,且若是這些字符都是可見的,則Erlang就將這些列表顯示爲可見的字符串,因此以上的結果就是可見字符對應的ASCII碼。通過手工計算,能夠發現,串行和並行的計算的結果都是正確的。

D. 分別生成100*100、200*200、…、500*500的矩陣進行計算,記錄其計算的時間,其中計算串行的執行時間和並行的執行時間的代碼分別以下:

計算串行執行的時間的代碼:

test_p(A,B) ->
Start=milliseconds(),
singleProcess(A,B),
End=milliseconds(),
End-Start.

計算並行的執行時間的代碼:

test_m(A,B) ->
Start=milliseconds(),
multiProcess(A,B),
End=milliseconds(),
End-Start.

其中獲得當前時間的微秒數的函數代碼以下:

milliseconds()->

{Megaseconds,Seconds,Milliseconds}=erlang:timestamp(),

Megaseconds*1000000000000+Seconds*1000000+Milliseconds.

分別記錄不一樣的大小的方陣的乘積的時間以下表:

表4-1 不一樣矩陣大小的串並行計算時間

矩陣大小

100*100

200*200

300*300

400*400

500*500

串時間/μs

3961984

45411822

196529231

887329534

2012385931

並時間/μs

1574994

21824914

121664524

374340732

1019530991

完整的代碼見附錄。

4.3 實驗結果分析

由以上的實驗,能夠明顯看到,相同的計算量,並行方式下的計算時間明顯小於串行方式下的計算時間,更直觀的對好比圖4-1:

圖4-1 並行與串行計算時間的比較

     並行計算之因此可以在較短的時間內完成對矩陣的計算,主要是其對並行計算資源的充分利用從而達到減小執行時間的目的。這點能夠從程序在執行不一樣的方式的CPU的利用率能夠看出。

圖4-2 並行計算時CPU的利用率

圖4-3 串行計算時CPU的利用率

       從以上的兩圖能夠看出,並行計算下的CPU的使用率遠遠大於串行計算的CPU的使用率。並行計算中,CPU的資源被充分利用,這也體現了Erlang的優秀的並行編程的特性:將全部的進程分配給全部的CPU,使得全部的計算資源都充分利用。

5 總結

      Erlang編程語言對並行化的支持是良好的。經過這段時間對Erlang語言的學習研究,不只瞭解了更多的關於編程語言的知識,掌握了一門編程語言,瞭解了一種編程範式;同時,對並行計算有了更加深入的認識。在學習了基本的理論的同時,將相關的理論應用在了實踐中---矩陣乘積的並行計算與串行計算的比較,能夠知道,並行的技術確實在並行的硬件的環境下,相比串行計算,節省一半多的時間。可是,目前知道的僅僅是皮毛,在本文中也有不少的瑕疵,須要學習的還不少,但願在未來將學習到的知道多應用,同時也但願看到本文的各位不吝賜教。

附錄

實驗相關的完整代碼以下:

%% @author Andy
%% @doc @todo Add description to demo.
-module(demo).
%% ====================================================
%% API functions
%% ====================================================
-export([]).
-compile(export_all).
%% ====================================================
%% Internal functions
%% ====================================================
%%隨機生成M*N的矩陣
generateMatrix(M,N) ->
row(M,N,[]).
row(M,N,Result) ->
case 0=:=M of
true ->Result;
false->row(M-1,N,Result++[column(N,[])])
end.
column(N,Result) ->
case 0=:=N of
true ->Result;
false->column(N-1,Result++[random:uniform(10)])
end.

%%A矩陣的行與矩陣B相乘獲得新的行
rowMultiplymatrix(Row_A,Matrix_B,Count_Col,Result)->
case Count_Col=:=length(lists:nth(1, Matrix_B))+1 of
true ->Result;
false ->rowMultiplymatrix(Row_A,Matrix_B,Count_Col+1,Result++[multiply(Row_A,getColume(Matrix_B,Count_Col),0)])
end.
multiply([H1|T1],[H2|T2],Sum) when length(T1)=:=length(T2) ->
multiply(T1,T2,Sum+H1*H2);
multiply([],[],Sum)->Sum.

%%獲得矩陣M的Col列
getColume(M,Col) ->
lists:foldl(fun(A,Acc)->Acc++[lists:nth(Col, A)] end, [], M).

%%串行的矩陣相乘
singleProcess(A,B)->
_R=lists:foldl(fun(Row_A,Acc)->Acc++[rowMultiplymatrix(Row_A,B,1,[])]end, [], A).

%%並行的矩陣相乘
multiProcess(A,B) ->
P=self(),
lists:foldl(fun(A_Row,_Acc)-> spawn(fun()->P!{self(),rowMultiplymatrix(A_Row,B,1,[])} end) end, [], A),
receiveMessage(length(A),[]).

%%循環接收計算的結果
receiveMessage(Count,Result) ->
receive
{_Pid,_Result}->
case Count=:=1 of
true ->Result++[_Result];
false ->
receiveMessage(Count-1,Result++[_Result])
end
end.

%%獲得當前時間的微秒數
milliseconds()->
{Megaseconds,Seconds,Milliseconds}=erlang:timestamp(),
Megaseconds*1000000000000+Seconds*1000000+Milliseconds.

%%並行矩陣相乘的時間計算
test_m(A,B) ->
Start=milliseconds(),
multiProcess(A,B),
End=milliseconds(),
End-Start.

%%串行的矩陣相乘的時間計算
test_p(A,B) ->
Start=milliseconds(),
singleProcess(A,B),
End=milliseconds(),
End-Start.
相關文章
相關標籤/搜索