第五章 消息傳遞接口MPI_第1頁(yè)
第五章 消息傳遞接口MPI_第2頁(yè)
第五章 消息傳遞接口MPI_第3頁(yè)
第五章 消息傳遞接口MPI_第4頁(yè)
第五章 消息傳遞接口MPI_第5頁(yè)
已閱讀5頁(yè),還剩12頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、第5章 消息傳遞接口MPIMPI(消息傳遞接口)是用于分布式存儲(chǔ)器并行計(jì)算機(jī)的標(biāo)準(zhǔn)編程環(huán)境。MPI的核心構(gòu)造是消息傳遞:一個(gè)進(jìn)程將信息打包成消息,并將該消息發(fā)送給其他進(jìn)程。但是,MPI包含比簡(jiǎn)單的消息傳遞更多的內(nèi)容。MPI包含一些例程,這些例程可以同步進(jìn)程、求分布在進(jìn)程集中的數(shù)值的總和、在同一個(gè)進(jìn)程集中分配數(shù)據(jù),以及實(shí)現(xiàn)更多的功能。在20世紀(jì)90年代早期人們創(chuàng)建了MPI,以提供一種能夠運(yùn)行在集群、MPP、甚至是共享存儲(chǔ)器機(jī)器中的通用消息傳遞環(huán)境。MPI以一種庫(kù)的形式發(fā)布,官方的規(guī)范定義了對(duì)C和Fortran的綁定(對(duì)其他語(yǔ)言的綁定也已經(jīng)被定義)。當(dāng)今MPI程序員主要使用MPI版本1.1(199

2、5年發(fā)行)。在1997年發(fā)行了一個(gè)增強(qiáng)版本的規(guī)范,MPI 2.0,它具有并行I/O、動(dòng)態(tài)進(jìn)程管理、單路通信和其他高級(jí)功能。遺憾的是,由于它對(duì)原有的標(biāo)準(zhǔn)增加了復(fù)雜的內(nèi)容,使得到目前為止,僅有少量的MPI實(shí)現(xiàn)支持MPI 2.0。因?yàn)檫@個(gè)原因,我們將在本章中集中介紹MPI1.1。5.1 MPI編程的基本概念對(duì)MPI的定義是多種多樣的,但不外乎下面三個(gè)方面,它們限定了MPI的內(nèi)涵和外延。1 MPI是一個(gè)庫(kù),而不是一門語(yǔ)言。許多人認(rèn)為MPI就是一種并行語(yǔ)言,這是不準(zhǔn)確的。但是按照并行語(yǔ)言的分類,可以把FORTRAN+MPI或C+MPI,看作是一種在原來串行語(yǔ)言基礎(chǔ)之上擴(kuò)展后得到的并行語(yǔ)言。MPI庫(kù)可以被

3、FORTRAN77/C/Fortran90/C+調(diào)用,從語(yǔ)法上說,它遵守所有對(duì)庫(kù)函數(shù)/過程的調(diào)用規(guī)則,和一般的函數(shù)/過程沒有什么區(qū)別。2 MPI是一種標(biāo)準(zhǔn)或規(guī)范的代表,而不特指某一個(gè)對(duì)它的具體實(shí)現(xiàn)。迄今為止,所有的并行計(jì)算機(jī)制造商都提供對(duì)MPI的支持,可以在網(wǎng)上免費(fèi)得到MPI在不同并行計(jì)算機(jī)上的實(shí)現(xiàn),一個(gè)正確的MPI程序,可以不加修改地在所有的并行機(jī)上運(yùn)行。3 MPI是一種消息傳遞編程模型,并成為這種編程模型的代表和事實(shí)上的標(biāo)準(zhǔn)。MPI雖然很龐大,但是它的最終目的是服務(wù)于進(jìn)程間通信這一目標(biāo)的。關(guān)于什么是MPI的問題設(shè)計(jì)到多個(gè)不同的方面。當(dāng)我們提到MPI時(shí),不同的上下文中會(huì)有不同的含義,它可以是

4、一種編程模型,也可以是一種標(biāo)準(zhǔn),當(dāng)然也可以指一類庫(kù)。只要全面把握了MPI的概念,這些區(qū)別是不難理解的。的三個(gè)主要目的1 較高的通信性能;2 較好的程序可移植性;3 強(qiáng)大的功能。MPI為自己制定了一個(gè)雄心勃勃的目標(biāo),總結(jié)概括起來,它包括幾個(gè)在實(shí)際使用中都十分重要但有時(shí)又是相互矛盾的三個(gè)方面,具體地說,包括以下幾個(gè)方面:提供應(yīng)用程序編程接口。 提高通信效率。措施包括避免存儲(chǔ)器到存儲(chǔ)器的多次重復(fù)拷貝,允許計(jì)算和通信的重疊等。 可在異構(gòu)環(huán)境下提供實(shí)現(xiàn)。提供的接口可以方便 C 語(yǔ)言和 Fortran 77的調(diào)用。提供可靠的通信接口。即用戶不必處理通信失敗。定義的接口和現(xiàn)在已有接口(如PVM,NX,Exp

5、ress,p4等)差別不能太大,但是允許擴(kuò)展以提供更大的靈活性。定義的接口能在基本的通信和系統(tǒng)軟件無(wú)重大改變時(shí),在許多并行計(jì)算機(jī)生產(chǎn)商的平臺(tái)上實(shí)現(xiàn)。接口的語(yǔ)義是獨(dú)立于語(yǔ)言的。 接口設(shè)計(jì)應(yīng)是線程安全的。MPI提供了一種與語(yǔ)言和平臺(tái)無(wú)關(guān),可以被廣泛使用的編寫消息傳遞程序的標(biāo)準(zhǔn),用它來編寫消息傳遞程序,不僅實(shí)用、可移植、高效和靈活,而且和當(dāng)前已有的實(shí)現(xiàn)沒有太大的變化。的語(yǔ)言綁定與實(shí)現(xiàn)在MPI-1中,明確提出了MPI和FORTRAN 77與C語(yǔ)言的綁定,并且給出了通用接口和針對(duì)FORTRAN 77與C的專用接口說明,MPI-1的成功說明MPI選擇的語(yǔ)言綁定策略是正確和可行的。Fortran90是FOR

6、TRAN的擴(kuò)充,它在表達(dá)數(shù)組運(yùn)算方面有獨(dú)特的優(yōu)勢(shì),還增加了模塊等現(xiàn)代語(yǔ)言的方便開發(fā)與使用的各種特征,它目前面臨的一個(gè)問題是Fortran90編譯器遠(yuǎn)不如FORTRAN 77編譯器那樣隨處可見,但提供Fortran90編譯器的廠商正在逐步增多。C+作為面向?qū)ο蟮母呒?jí)語(yǔ)言,隨著編譯器效率和處理器速度的提高,它可以取得接近于C的代碼效率,面向?qū)ο蟮木幊趟枷胍呀?jīng)被廣為接受,因此在MPI-2中,除了和原來的FORTRAN 77和C語(yǔ)言實(shí)現(xiàn)綁定之外,進(jìn)一步與Fortran90和C+結(jié)合起來,提供了四種不同的接口,為編程者提供了更多選擇的余地。但是MPI-2目前還沒有完整的實(shí)現(xiàn)版本。下面列出了一些主要的MP

7、I免費(fèi)實(shí)現(xiàn)。表5.1 MPI的一些實(shí)現(xiàn)實(shí)現(xiàn)名稱研制單位網(wǎng)址MpichArgonne and MSU/mpi/mpichChimpEdinburghftp:/ftp.epcc.ed.ac.uk/pub/packages/chimp/LamOhio State University/lam/MPICH是一種最重要的MPI實(shí)現(xiàn),它可以免費(fèi)從取得。更為重要的是,MPICH是一個(gè)與MPI-1規(guī)范同步發(fā)展的版本,每當(dāng)MPI推出新的版本,就會(huì)有相應(yīng)的MPICH的實(shí)現(xiàn)版本,目前MPICH的最新版本是,它支持部分的 MP

8、I-2的特征。Argonne and MSU(阿爾貢)國(guó)家試驗(yàn)室和MSU(密西根州立大學(xué))對(duì)MPICH作出了重要的貢獻(xiàn)。在本書中,未特別說明,均指在基于Linux集群的MPICH實(shí)現(xiàn)。CHIMP是Edinburgh(愛丁堡、英國(guó)蘇格蘭首府)開發(fā)的另一個(gè)免費(fèi)MPI實(shí)現(xiàn),是在EPCC(Edinburgh Parallel Computing Centre)的支持下進(jìn)行的,從可以免費(fèi)下載該軟件,CHIMP的開發(fā)從1991年到1994年,主要開發(fā)人員有Alasdair Bruce, James (Hamish Mills,和Gordon Smith。LAM (Local Area Multicompu

9、ter也是免費(fèi)的MPI實(shí)現(xiàn),由Ohio State University美國(guó)俄亥俄州國(guó)立大學(xué) 開發(fā),它目前的最新版本是,可以從下載。它主要用于異構(gòu)的計(jì)算機(jī)網(wǎng)絡(luò)計(jì)算系統(tǒng)。編程的基本概念一個(gè)MPI并行程序由一組運(yùn)行在相同或不同計(jì)算機(jī)/計(jì)算結(jié)點(diǎn)上的進(jìn)程或線程構(gòu)成。這些進(jìn)程或線程可以運(yùn)行在不同處理機(jī)上,也可以運(yùn)行在相同的處理機(jī)上。為統(tǒng)一起見,MPI 程序中一個(gè)獨(dú)立參與通信的個(gè)體稱為一個(gè)進(jìn)程(process。一個(gè)MPI進(jìn)程通常對(duì)應(yīng)于一個(gè)普通進(jìn)程或線程,但是在共享存儲(chǔ)/消息傳遞混合模式程序中,一個(gè)MPI 進(jìn)程可能代表一組UNIX線程。一個(gè)MPI程序中由部分或全部進(jìn)程構(gòu)成的一個(gè)有序集合稱為一個(gè)進(jìn)程組(pro

10、cess group。進(jìn)程組中每個(gè)進(jìn)程被賦予一個(gè)該組中的序號(hào)(rank,用于在該組中標(biāo)識(shí)該進(jìn)程,稱為進(jìn)程號(hào)。進(jìn)程號(hào)的取值范圍從0開始。MPI程序中進(jìn)程間的通信、同步等通過通信器(communicator進(jìn)行(一些資料中將通信器翻譯成通信子,本書中將統(tǒng)一使用術(shù)語(yǔ)通信器。MPI的通信器有域內(nèi)通信器(intra-communicator和域間通信器(inter-communicator兩種,前者用于屬于同一進(jìn)程組的進(jìn)程間的通信,后者用于分屬兩個(gè)不同進(jìn)程組的進(jìn)程間的通信。這里只對(duì)域內(nèi)通信器進(jìn)行介紹,后文中除非特別提及,“通信器”一詞一律指域內(nèi)通信器。一個(gè)通信器由它所包含的進(jìn)程組及與之相關(guān)聯(lián)的一組屬性(

11、例如進(jìn)程間的拓?fù)溥B接關(guān)系 構(gòu)成。通信器提供進(jìn)程間通信的基本環(huán)境,MPI 程序中所有通信都必須在特定的通信器中完成。MPI 程序啟動(dòng)時(shí)會(huì)自動(dòng)創(chuàng)建兩個(gè)通信器,一個(gè)稱為MPI_COMM_WORLD,它包含程序中的所有進(jìn)程,另一個(gè)稱為MPI_COMM_SELF,它是每個(gè)進(jìn)程獨(dú)自構(gòu)成的、僅包含自己的通信器。在MPI 程序中,一個(gè)MPI 進(jìn)程由一個(gè)通信器(或進(jìn)程組 和進(jìn)程在該通信器(或進(jìn)程組 中的進(jìn)程號(hào)唯一標(biāo)識(shí)。注意進(jìn)程號(hào)是相對(duì)于通信器或進(jìn)程組而言的:同一個(gè)進(jìn)程在不同的通信器(或進(jìn)程組中可以有不同的進(jìn)程號(hào)。進(jìn)程號(hào)是在通信器(或進(jìn)程組 被創(chuàng)建時(shí)賦予的。MPI 系統(tǒng)提供了一個(gè)特殊進(jìn)程號(hào)MPI_PROC_NUL

12、L,它代表空進(jìn)程(不存在的進(jìn)程,與MPI_PROC_NULL進(jìn)行通信相當(dāng)于一個(gè)空操作,對(duì)程序的運(yùn)行沒有任何影響,它的引入可以方便一些程序的編寫。MPI程序中進(jìn)程間的通信(communications 通過消息的收發(fā)或同步操作完成。一個(gè)消息(message 指在進(jìn)程間進(jìn)行的一次數(shù)據(jù)交換。MPI消息包括信封和數(shù)據(jù)兩個(gè)部分,信封指出了發(fā)送或接收消息的對(duì)象及相關(guān)信息,而數(shù)據(jù)是本消息將要傳遞的內(nèi)容。信封和數(shù)據(jù)又分別包括三個(gè)部分。可以用一個(gè)三元組來表示。信封:<源/目,標(biāo)識(shí),通信域>數(shù)據(jù):<起始地址,數(shù)據(jù)個(gè)數(shù),數(shù)據(jù)類型>以MPI_SEND和MPI_RECV (receive為例,下

13、圖分別給出了它們的信封和數(shù)據(jù)部分。圖5.1 MPI_SEND語(yǔ)句的消息信封和消息數(shù)據(jù)圖5.2 MPI_RECV語(yǔ)句的消息信封和消息數(shù)據(jù)在消息信封中除了源/目外,為什么還有tag標(biāo)識(shí)呢?這是因?yàn)椋?dāng)發(fā)送者發(fā)送兩個(gè)相同類型的數(shù)據(jù)給同一個(gè)接收者時(shí),如果沒有消息標(biāo)識(shí),接收者將無(wú)法區(qū)別這兩個(gè)消息。一個(gè)接收操作對(duì)消息的選擇是由消息的信封管理的。如果消息的信封與接收操作所指定的值source,tag和comm相匹配,那么這個(gè)接收操作能接收這個(gè)消息。接收者可以給source指定一個(gè)任意值MPI_ANY_SOURCE,標(biāo)識(shí)任何進(jìn)程發(fā)送的消息都可以接收,即本接收操作可以匹配任何進(jìn)程發(fā)送的消息,但其它的要求還必須滿

14、足,比如tag的匹配;如果給tag一個(gè)任意值MPI_ANY_TAG,則任何tag都是可接收的。在某種程度上,類似于統(tǒng)配符的概念。MPI_ANY_SOURCE和MPI_ANY_TAG可以同時(shí)使用或分別單獨(dú)使用。但是不能給comm指定任意值。如果一個(gè)消息被發(fā)送到接收進(jìn)程,接收進(jìn)程有匹配的通信域,有匹配的 source (或其source = MPI_ANY_SOURCE,有匹配的tag(或其tag = MPI_ANY_TAG,那么這個(gè)消息能被這個(gè)接收操作接收。5.2 MPI的原始數(shù)據(jù)類型MPI系統(tǒng)中數(shù)據(jù)的發(fā)送與接收操作都必須指定數(shù)據(jù)類型。數(shù)據(jù)類型可以是MPI系統(tǒng)預(yù)定義的,稱為原始數(shù)據(jù)類型,也可以是

15、用戶在原始數(shù)據(jù)類型的基礎(chǔ)上自己定義的數(shù)據(jù)類型。在此只介紹原始數(shù)據(jù)類型,自定義數(shù)據(jù)類型請(qǐng)參看參考文獻(xiàn)2。MPI為C預(yù)定義的原始數(shù)據(jù)類型在表1中給出。除表中列出的外,某些MPI系統(tǒng)可能支持更多的原始數(shù)據(jù)類型,如MPI_INTEGER2,MPI_LONG_LONG_INT,等等。表5.2 MPI原始數(shù)據(jù)類型MPI數(shù)據(jù)類型C類型MPI_INTMPI_FLOATMPI_DOUBLEMPI_SHORTMPI_LONGMPI_CHAR MPI_UNSIGNED_CHARMPI_UNSIGNED_SHORTMPI_UNSIGNEDMPI_UNSIGNED_LONGMPI_LONG_DOUBLEMPI_BYTE

16、MPI_PACKEDintfloatdoubleshortlongcharunsigned charunsigned shortunsignedunsigned longlong doubleunsigned char無(wú)MPI系統(tǒng)的原始數(shù)據(jù)類型只適合于收發(fā)一組在內(nèi)存中連續(xù)存放的數(shù)據(jù)。當(dāng)要收發(fā)的數(shù)據(jù)在內(nèi)存中不連續(xù),或由不同數(shù)據(jù)類型構(gòu)成時(shí),則需要將數(shù)據(jù)打包或者使用自定義的數(shù)據(jù)類型。自定義數(shù)據(jù)類型用于描述要發(fā)送或接收的數(shù)據(jù)在內(nèi)存中的確切分布。數(shù)據(jù)類型是MPI的一個(gè)重要特征,它的使用可有效地減少消息傳遞的次數(shù),增大通信粒度,并且,與數(shù)據(jù)打包相比,在收/發(fā)消息時(shí)可以避免或減少數(shù)據(jù)在內(nèi)存中的拷貝、復(fù)制。關(guān)

17、于MPI自定義數(shù)據(jù)類型的詳細(xì)使用,限于篇幅,在此不作詳細(xì)說明,有興趣的讀者請(qǐng)參看參考文獻(xiàn)文獻(xiàn)2。5.3 MPI程序的基本結(jié)構(gòu)一個(gè)MPI程序的各個(gè)進(jìn)程通過調(diào)用MPI函數(shù)進(jìn)行通信,協(xié)同完成一項(xiàng)計(jì)算任務(wù)。在MPI的C語(yǔ)言接口中,所有函數(shù)名均采用MPI_Xxxxx的形式,如MPI_Send,MPI_Type_commit等等,它們以MPI_開始,以便與其他函數(shù)名相區(qū)別,前綴MPI_之后的第一個(gè)字母大寫,其余字母一律小寫。MPI的C語(yǔ)言接口函數(shù)通常返回一個(gè)整數(shù)值表示操作成功與否,返回值為MPI_SUCCESS (0 表示操作成功,否則表示操作的錯(cuò)誤碼。MPI接口中除了函數(shù)和SUBROUTINE外,還定義

18、了一組常量及C變量類型,它們的命名規(guī)則為:所有常量的名稱全部大寫,如MPI_COMM_WORLD,MPI_INT等;而C變量類型的命名則與C函數(shù)一樣,如MPI_Datatype,MPI_Status等。MPI 并行程序和串行程序沒有很大的差別,它們通過對(duì)MPI函數(shù)的調(diào)用來實(shí)現(xiàn)特定的并行算法。一個(gè)MPI 并行程序主要由三個(gè)部分組成:1 進(jìn)入并行環(huán)境:調(diào)用MPI_Init來啟動(dòng)并行計(jì)算環(huán)境,它包括在指定的計(jì)算結(jié)點(diǎn)上啟動(dòng)構(gòu)成并行程序的所有進(jìn)程以及構(gòu)建初始的MPI 通信環(huán)境和通信器MPI_COMM_WORLD、MPI_COMM_SELF。2 主體并行任務(wù):這是并行程序的實(shí)質(zhì)部分,所有需要并行來完成的任

19、務(wù)都在這里進(jìn)行。在這個(gè)部分中,實(shí)現(xiàn)并行算法在并行計(jì)算機(jī)上的執(zhí)行過程。3 退出并行環(huán)境:調(diào)用MPI_Finalize退出并行環(huán)境。一般說來,退出并行計(jì)算環(huán)境后程序的運(yùn)行亦馬上結(jié)束。下面是C語(yǔ)言MPI程序的典型結(jié)構(gòu)#include "mpi.h". .int main(int argc, char *argvint myrank, nprocs;. .MPI_Init(&argc, &argv;MPI_Comm_size(MPI_COMM_WORLD, &nprocs;MPI_Comm_rank(MPI_COMM_WORLD, &myrank;.

20、 .MPI_Finalize(;. .用C語(yǔ)言編寫的MPI 程序中每個(gè)源文件必須包含MPI的C語(yǔ)言頭文件mpi.h,以便得到MPI函數(shù)的原型說明及MPI預(yù)定義的常量和類型。注意源文件中包含頭文件“mpi.h” 時(shí)不要含路徑,必要時(shí)可在編譯時(shí)通過“-I” 選項(xiàng)指定mpi.h所在的路徑,以方便程序在不同MPI 系統(tǒng)間的移植。MPI_Init函數(shù)用于初始化MPI系統(tǒng)。在調(diào)用其他MPI函數(shù)前(除MPI_Initialized外 必須先調(diào)用該函數(shù)。在許多MPI系統(tǒng)中,第一個(gè)進(jìn)程通過MPI_Init來啟動(dòng)其他進(jìn)程。注意要將命令行參數(shù)的地址(指針 &argc和&argv傳遞給MPI_Init

21、,因?yàn)镸PI程序啟動(dòng)時(shí)一些初始參數(shù)是通過命令行傳遞給進(jìn)程的,這些參數(shù)被添加在命令行參數(shù)表中,MPI_Init通過它們得到MPI程序運(yùn)行的相關(guān)信息,如需要啟動(dòng)的進(jìn)程數(shù)、使用那些結(jié)點(diǎn)、以及進(jìn)程間的通信端口等,返回時(shí)會(huì)將這些附加參數(shù)從參數(shù)表中去掉。因此一個(gè)MPI程序如果需要處理命令行參數(shù),最好在調(diào)用MPI_Init之后再進(jìn)行處理,這樣可以避免遇到MPI系統(tǒng)附加的額外參數(shù)。函數(shù)MPI_Comm_size與MPI_Comm_rank分別返回指定通信器(這里是MPI_COMM_WORLD,它包含了所有進(jìn)程 中進(jìn)程的數(shù)目以及本進(jìn)程的進(jìn)程號(hào)。MPI_Finalize函數(shù)用于退出MPI系統(tǒng)。調(diào)用MPI_Fina

22、lize之后不能再調(diào)用任何其他MPI 函數(shù)。各MPI函數(shù)的詳細(xì)使用方法請(qǐng)參看下一節(jié)的例子程序及附錄3,在此先不作詳細(xì)說明。5.4 MPI通信簡(jiǎn)介點(diǎn)到點(diǎn)通信和通信模式MPI最基本的通信模式是在一對(duì)進(jìn)程之間進(jìn)行的消息收發(fā)操作:一個(gè)進(jìn)程發(fā)送消息,另一個(gè)進(jìn)程接收消息。這種通信方式稱為點(diǎn)對(duì)點(diǎn)通信(point to point communications。MPI提供兩大類型的點(diǎn)對(duì)點(diǎn)通信函數(shù)。第一種類型稱為阻塞型(blocking,第二種類型稱為非阻塞型(non blocking。阻塞型函數(shù)需要等待指定操作的實(shí)際完成,或至少所涉及的數(shù)據(jù)已被MPI系統(tǒng)安全地備份后才返回。如MPI_Send 和MPI_Rec

23、v都是阻塞型的。MPI_Send 函數(shù)返回時(shí)表明數(shù)據(jù)已經(jīng)發(fā)出或已被MPI系統(tǒng)復(fù)制,隨后對(duì)發(fā)送緩沖區(qū)的修改不會(huì)影響所發(fā)送的數(shù)據(jù)。而MPI_Recv返回時(shí),則表明數(shù)據(jù)已經(jīng)接收到并且可以立即使用。阻塞型函數(shù)的操作是非局部的,它的完成可能需要與其他進(jìn)程進(jìn)行通信。非阻塞型函數(shù)調(diào)用總是立即返回,而實(shí)際操作則由MPI系統(tǒng)在后臺(tái)進(jìn)行。非阻塞型函數(shù)名MPI_前綴之后的第一個(gè)字母為“I”,最常用的非阻塞型點(diǎn)對(duì)點(diǎn)通信函數(shù)包括MPI_Isend 和MPI_Irecv。在調(diào)用了一個(gè)非阻塞型通信函數(shù)后,用戶必須隨后調(diào)用其他函數(shù),如MPI_Wait 或MPI_Test等,來等待操作完成或查詢操作的完成情況。在操作完成之前對(duì)

24、相關(guān)數(shù)據(jù)區(qū)的操作是不安全的,因?yàn)殡S時(shí)可能與正在后臺(tái)進(jìn)行的通信發(fā)生沖突。非阻塞型函數(shù)調(diào)用是局部的,因?yàn)樗姆祷夭恍枰c其他進(jìn)程進(jìn)行通信。在有些并行系統(tǒng)上,通過非阻塞型函數(shù)的使用可以實(shí)現(xiàn)計(jì)算與通信的重疊進(jìn)行。此外,對(duì)于點(diǎn)對(duì)點(diǎn)消息發(fā)送,MPI 提供四種發(fā)送模式,這四種發(fā)送模式的相應(yīng)函數(shù)具有一樣的調(diào)用參數(shù),但它們發(fā)送消息的方式或?qū)邮辗降臓顟B(tài)要求不同。標(biāo)準(zhǔn)模式(standard mode 由MPI系統(tǒng)來決定是先將消息拷貝至一個(gè)緩沖區(qū)然后立即返回(此時(shí)消息的發(fā)送由MPI系統(tǒng)在后臺(tái)進(jìn)行,還是等待將數(shù)據(jù)發(fā)送出去后再返回。大部分MPI系統(tǒng)預(yù)留了一定大小的緩沖區(qū),當(dāng)發(fā)送的消息長(zhǎng)度小于緩沖區(qū)大小時(shí)會(huì)將消息拷貝到緩

25、沖區(qū)然后立即返回,否則則當(dāng)部分或全部消息發(fā)送完成后才返回。標(biāo)準(zhǔn)模式發(fā)送操作是非局部的,因?yàn)樗耐瓿尚枰c接收方聯(lián)絡(luò)。標(biāo)準(zhǔn)模式阻塞型發(fā)送函數(shù)是MPI_Send。緩沖模式(buffered mode MPI系統(tǒng)將消息拷貝至一個(gè)用戶提供的緩沖區(qū)然后立即返回,消息的發(fā)送由MPI系統(tǒng)在后臺(tái)進(jìn)行。用戶必須確保所提供的緩沖區(qū)足以容下采用緩沖模式發(fā)送的消息。當(dāng)消息大小超過緩沖區(qū)容量時(shí),應(yīng)用程序會(huì)收到錯(cuò)誤匯報(bào)。緩沖模式發(fā)送操作是局部的,因?yàn)楹瘮?shù)不需要與接收方聯(lián)絡(luò)即可立即完成(返回。緩沖模式阻塞型發(fā)送函數(shù)為MPI_Bsend。同步模式(synchronous mode 在標(biāo)準(zhǔn)模式的基礎(chǔ)上要求確認(rèn)接收方已經(jīng)開始接收

26、數(shù)據(jù)后函數(shù)調(diào)用才返回。即發(fā)送動(dòng)作的結(jié)束不僅意味著發(fā)送緩沖區(qū)已經(jīng)可以用于其它用途,而且還表示接收端也執(zhí)行了一定程序的接收工作。顯然,同步模式的發(fā)送是非局部的。同步模式阻塞型發(fā)送函數(shù)為MPI_Ssend。就緒模式(ready mode 調(diào)用就緒模式發(fā)送時(shí)必須確保接收方已經(jīng)處于就緒狀態(tài)(正在等待接收該消息,否則將產(chǎn)生一個(gè)錯(cuò)誤。該模式設(shè)立的目的是在一些以同步方式工作的并行系統(tǒng)上由于發(fā)送時(shí)可以假設(shè)接收方已經(jīng)準(zhǔn)備好接收而減少一些握手開銷。如果一個(gè)使用就緒模式的MPI 程序是正確的,則將其中所有就緒模式的消息發(fā)送改為標(biāo)準(zhǔn)模式后也應(yīng)該是正確的。就緒模式阻塞型發(fā)送函數(shù)為MPI_Rsend。表5.3 MPI點(diǎn)對(duì)點(diǎn)

27、通信類型及模式匯總函數(shù)類型通信模式阻塞型非阻塞型消息發(fā)送函數(shù)標(biāo)準(zhǔn)模式MPI_SendMPI_Isend緩沖模式MPI_BsendMPI_Ibsend同步模式MPI_SsendMPI_Issend就緒模式MPI_RsendMPI_Irsend消息接收函數(shù)MPI_RecvMPI_Irecv消息檢測(cè)函數(shù)MPI_ProbeMPI_Iprobe等待通信完成或查詢完成情況MPI_WaitMPI_TestMPI_WaitallMPI_TestallMPI_WaitanyMPI_TestanyMPI_WaitsomeMPI_Testsome釋放通信請(qǐng)求MPI_Request_free取消通信MPI_Cance

28、lMPI_Test_cancelled具體函數(shù)的使用請(qǐng)參看附錄3。各通信模式的詳細(xì)分析和舉例限于篇幅在此不作詳細(xì)說明,有興趣的讀者請(qǐng)參看參考文獻(xiàn)2。聚合通信指在一個(gè)通信器的所有進(jìn)程間同時(shí)進(jìn)行的通信。聚合通信總是在一個(gè)通信器中的所有進(jìn)程間進(jìn)行,調(diào)用一個(gè)聚合通信函數(shù)時(shí),通信器中的所有進(jìn)程必須同時(shí)調(diào)用同一函數(shù),共同參與操作。聚合通信包括障礙同步(MPI_Barrier、廣播(MPI_Bcast、數(shù)據(jù)收集(MPI_Gather、數(shù)據(jù)散發(fā)(MPI_Scatter、數(shù)據(jù)轉(zhuǎn)置(MPI_Alltoall和歸約(MPI_Reduce。1. 障礙同步障礙同步函數(shù)MPI_Barrier用于一個(gè)通信器中所有進(jìn)程的同步

29、。調(diào)用該函數(shù)時(shí)進(jìn)程將處于等待狀態(tài),直到通信器中所有進(jìn)程都調(diào)用了該函數(shù)后才繼續(xù)執(zhí)行。2. 廣播指一個(gè)進(jìn)程(稱為根進(jìn)程 同時(shí)發(fā)送同樣的消息給通信器中的所有其他進(jìn)程。MPI 的廣播函數(shù)是MPI_Bcast。3. 數(shù)據(jù)收集數(shù)據(jù)收集操作指一個(gè)進(jìn)程,稱為根進(jìn)程,從指定通信器中的所有進(jìn)程,包括根進(jìn)程自己,收集數(shù)據(jù)。MPI的基本數(shù)據(jù)收集函數(shù)為MPI_Gather,它從每個(gè)進(jìn)程收集相同長(zhǎng)度的數(shù)據(jù)。如果從各個(gè)進(jìn)程收集的數(shù)據(jù)長(zhǎng)度不同,則應(yīng)該調(diào)用函數(shù)MPI_Gatherv。函數(shù)MPI_Allgather用于在通信器中的所有進(jìn)程中同時(shí)進(jìn)行數(shù)據(jù)收集,它的作用相當(dāng)于先用MPI_Gather將數(shù)據(jù)收集到一個(gè)進(jìn)程中,緊接著用M

30、PI_Bcast將收集到的數(shù)據(jù)廣播給其他進(jìn)程。類似地,MPI_Allgatherv用于收集不同長(zhǎng)度的數(shù)據(jù)到通信器中的所有進(jìn)程中。4. 數(shù)據(jù)散發(fā)數(shù)據(jù)散發(fā)函數(shù)MPI_Scatter正好是數(shù)據(jù)收集函數(shù)MPI_Gather的逆向操作,它將一個(gè)進(jìn)程中的數(shù)據(jù)按塊散發(fā)給通信器中的所有進(jìn)程,散發(fā)給每個(gè)進(jìn)程的數(shù)據(jù)塊長(zhǎng)度相同。函數(shù)MPI_Scatterv用于散發(fā)不同長(zhǎng)度的數(shù)據(jù)塊。5. 數(shù)據(jù)轉(zhuǎn)置函數(shù)MPI_Alltoall用于同時(shí)進(jìn)行收集和散發(fā)操作:通信器中所有進(jìn)程從其他進(jìn)程收集數(shù)據(jù),同時(shí)將自己的數(shù)據(jù)散發(fā)給其他進(jìn)程。它的作用相當(dāng)于將一個(gè)分布式存儲(chǔ)的數(shù)據(jù)場(chǎng)在處理機(jī)間進(jìn)行一次轉(zhuǎn)置。函數(shù)MPI_Alltoall要求參與操

31、作的所有數(shù)據(jù)塊長(zhǎng)度一樣,如果數(shù)據(jù)塊長(zhǎng)度不同,則應(yīng)該調(diào)用MPI_Alltoallv函數(shù)。6. 歸約歸約運(yùn)算是指在分布在不同進(jìn)程的數(shù)據(jù)間進(jìn)行指定的運(yùn)算,常用的運(yùn)算有求和、求最大或最小值等。MPI 的歸約函數(shù)中可以使用預(yù)定義的運(yùn)算(如MPI_SUM,MPI_MAX 等,參看附錄3,也可以使用用戶自行定義的運(yùn)算(參看MPI_Op_create。MPI 用于歸約操作的基本函數(shù)是MPI_Reduce,它在指定的進(jìn)程(稱為根進(jìn)程 中返回歸約運(yùn)算結(jié)果。如果希望所有進(jìn)程都得到歸約運(yùn)算的結(jié)果,則可使用函數(shù)MPI_Allreduce。此外,MPI 還提供一個(gè)函數(shù)MPI_Scan,稱為前綴歸約或掃描歸約,用于計(jì)算數(shù)據(jù)

32、的部分和。表5.4 MPI 點(diǎn)對(duì)點(diǎn)通信類型及模式匯總類型函數(shù)名含義同步MPI_Barrier路障同步廣播MPI_Bcast一對(duì)多廣播同樣的消息數(shù)據(jù)收集MPI_Gather多對(duì)一收集各個(gè)進(jìn)程的消息MPI_GathervMPI_Gather的一般化MPI_Allgather全局收集MPI_AllgathervMPI_Allgather的一般化數(shù)據(jù)散發(fā)MPI_Scatter一對(duì)多散播不同的消息MPI_SvcatterMPI_Scatter的一般化數(shù)據(jù)轉(zhuǎn)置MPI_Alltoall多對(duì)多全局交換消息MPI_AlltoallvMPI_Alltoallv的一般化歸約MPI_Reduce多對(duì)一歸約MPI_Al

33、lreduce全歸約MPI_Reduce_scatter歸約并散播MPI_Scan掃描具體函數(shù)的使用請(qǐng)參看附錄3。5.5 一個(gè)經(jīng)典的MPI程序?qū)嵗龜?shù)值積分(值計(jì)算代碼5.1 是取自MPICH 的一個(gè)程序?qū)嵗故玖薈語(yǔ)言MPI并行程序的結(jié)構(gòu)。該程序用下面的公式計(jì)算定積分的近似值: 式5.1其中n>0為積分區(qū)間數(shù)。為積分步長(zhǎng)。 為積分區(qū)間的中點(diǎn)。被積函數(shù)。 假設(shè)總進(jìn)程數(shù)為p (程序中的numprocs 變量,各進(jìn)程分別負(fù)責(zé)計(jì)算式5.1中的一部分計(jì)算區(qū)間,然后再調(diào)用MPI_Reduce將各進(jìn)程的結(jié)果加起來。代碼中計(jì)算區(qū)間采用循環(huán)分配的方式,即將計(jì)算公式寫成:每個(gè)進(jìn)程獨(dú)立計(jì)算上式中的一個(gè)內(nèi)層

34、求和,然后再將這些結(jié)果加起來,如圖5.1。圖5.3 值計(jì)算分10小塊時(shí)圖示(10小塊的面積相加近似于曲線f(x發(fā)在0-1區(qū)間所圍的面積)代碼5.1 MPI程序?qū)嵗簲?shù)值積分(值計(jì)算。文件名: code/mpi/cpi.c1 /* 程序來源:MPICH examples/cpi.c */2 #include "mpi.h"3 #include 45 double f( double a return (4.0 / (1.0 + a*a; /*定義被積函數(shù)f(x67 int main( int argc, char *argv /* argc 和argv 分別是命令行參數(shù)的個(gè)數(shù)

35、和參數(shù)數(shù)組的指針8 9 int n, myid, numprocs, i, namelen; /*n :計(jì)算區(qū)間分區(qū)(條)數(shù);myid:本進(jìn)程的進(jìn)程號(hào); numprocs :進(jìn)程組中進(jìn)程數(shù);i:進(jìn)程中計(jì)算各個(gè)小區(qū)間的循環(huán)控制變量;namelen:處理器名長(zhǎng)度;11 double mypi, pi, h, sum, x; /*mypi :各進(jìn)程中所有小區(qū)間面積的求和值;pi:最終的計(jì)算值; h:小區(qū)間寬度; sum:進(jìn)程中所有小區(qū)間高的和; x:每個(gè)小區(qū)間中點(diǎn)的x值;12 double startwtime, endwtime;13 char processor_nameMPI_MAX_PROC

36、ESSOR_NAME; /*processor_name:處理器名存儲(chǔ)單元1415 MPI_Init(&argc,&argv;16 MPI_Comm_size(MPI_COMM_WORLD,&numprocs;17 MPI_Comm_rank(MPI_COMM_WORLD,&myid;18 MPI_Get_processor_name(processor_name,&namelen; 19 fprintf(stderr,"Process%don %sn", myid, processor_name;20 if (myid = 0 21

37、 n=10000;22 startwtime = MPI_Wtime(;23 24 MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD; /*0:root根進(jìn)稱號(hào)25 h = 1.0 / (double n;26 sum = 0.0;27 for (i = myid; i < n; i += numprocs 28 x = h * (doublei + 0.5;29 sum += f(x;30 31 mypi = h * sum;32 MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM,

38、0, MPI_COMM_WORLD;33 if (myid = 0 34 endwtime = MPI_Wtime(;35 printf("pi is approximately %.16f, error is %.16fn", pi, pi - PI25DT;36 printf("wall clock time = %fn", endwtime-startwtime;37 38 MPI_Finalize(;3940 return 0;41 程序詳解:第2行:#include "mpi.h"是預(yù)處理指令,用于包含mpi的頭文件。第5行

39、:double f(double a return (4.0/(1.0+a*a;定義被積函數(shù)f(x。第7行:int main( int argc, char *argv是主函數(shù),包含了argc和argv參數(shù),它們將被傳入用于初始化MPI的函數(shù)。第10行:定義一個(gè)比較精確的25位值作為標(biāo)準(zhǔn)值,以分析本程序計(jì)算結(jié)果的誤差。第12行:double startwtime, endwtime定義變量開始時(shí)間startwtime和結(jié)束時(shí)間endwtime。均為MPI_Wtime(的返回值。第15行:MPI_Init(&argc,&argv被每一個(gè)MPI進(jìn)程調(diào)用的MPI函數(shù)都是MPI_Ini

40、t。該函數(shù)指示系統(tǒng)完成所有初始化工作,以備對(duì)后續(xù)MPI庫(kù)的調(diào)用進(jìn)行處理,因此,MPI_Init要在調(diào)用任何MPI函數(shù)之前調(diào)用。argc 和argv 分別是命令行參數(shù)的個(gè)數(shù)和參數(shù)數(shù)組的指針(通過C的main 函數(shù)得到,必須將它們?nèi)鐚?shí)傳遞給MPI 系統(tǒng)。MPI 系統(tǒng)通過它們得到所需的參數(shù),并且會(huì)將MPI 系統(tǒng)專用的參數(shù)刪除而僅留下供用戶程序使用的參數(shù)。第16行:MPI_Comm_size(MPI_COMM_WORLD,&numprocs當(dāng)MPI初始化后,每一個(gè)活動(dòng)進(jìn)程變成一個(gè)叫做MPI_COMM_WORLD的默認(rèn)通信域中的成員。一個(gè)通信域是不透明的對(duì)象,提供了在進(jìn)程之間傳遞消息的環(huán)境。MP

41、I_Comm_size(MPI_COMM_WORLD,&numprocs用numprocs返回通信域MPI_COMM_WORLD中的進(jìn)程數(shù)。第17行:MPI_Comm_rank(MPI_COMM_WORLD,&myid用myid返回通信域MPI_COMM_WORLD中本進(jìn)程的進(jìn)程號(hào)。第18行:MPI_Get_processor_name(processor_name,&namelen 該函數(shù)返回運(yùn)行本進(jìn)程的處理器名稱。參數(shù)processor_name應(yīng)該提供不少于MPI_MAX_PROCESSOR_NAME 個(gè)字節(jié)的存儲(chǔ)空間用于存放處理器名稱。第20-23行:是僅進(jìn)程0

42、執(zhí)行的代碼,給n賦值10000意味著將0-1的積分區(qū)間分成10000小塊,對(duì)每一個(gè)小塊計(jì)算面積。同時(shí),獲取計(jì)算機(jī)開始的時(shí)間。第24行:MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD是廣播函數(shù)。MPI_Bcast(void *buffer, int count,MPI_Datatype datatype, int root,MPI_Comm comm表示通信器comm 中進(jìn)程號(hào)為root 的進(jìn)程(稱為根進(jìn)程 將自己buffer 中的內(nèi)容發(fā)送給通信器中所有其他進(jìn)程。參數(shù)buffer、count 和datatype 的含義與點(diǎn)對(duì)點(diǎn)通信函數(shù)(如MPI_S

43、end 和MPI_Recv 相同。則MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD表示通信器MPI_COMM_WORLD中進(jìn)程號(hào)為0的進(jìn)程將自己n 中的內(nèi)容發(fā)送給通信器中所有其他進(jìn)程。第25-26行:為即將開始的計(jì)算做準(zhǔn)備。第27-30行:計(jì)算本進(jìn)程所分配的各小塊的高度和。每一進(jìn)程均從i= myid開始,每做一次計(jì)算往后跳numprocs塊。這樣,每個(gè)進(jìn)程均計(jì)算總小塊數(shù)10000的一1/numprocs。第31行:mypi = h * sum將每個(gè)進(jìn)程所得的各小塊的高度和與小塊寬度相乘即得本進(jìn)程所得的小塊面積和即PI的部分值。第32行:MPI_R

44、educe(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD將各進(jìn)程所得的PI的部分值進(jìn)行歸約。&mypi為各個(gè)進(jìn)程的歸約元素的地址,&pi為歸約計(jì)算(MPI_SUM)之后結(jié)果的存放地址,為執(zhí)行歸約的次數(shù),MPI_DOUBLE為元素類型,MPI_SUM為歸約操作符(其它的操作符請(qǐng)參看附錄),0為得到結(jié)果的進(jìn)程號(hào),MPI_COMM_WORLD為通信域。第33-37行: 0號(hào)進(jìn)程執(zhí)行的代碼,進(jìn)程首先獲取時(shí)間,再用現(xiàn)在的時(shí)候減去初始獲取的時(shí)間即得到程序執(zhí)行的時(shí)間并顯示。第38行:MPI_Finalize(在一個(gè)

45、進(jìn)程執(zhí)行完其全部MPI庫(kù)函數(shù)調(diào)用后,將調(diào)用函數(shù)MPI_Finalize,從而讓系統(tǒng)釋放分配給MPI的資源(例如內(nèi)存等)。所有進(jìn)程均從前到后地執(zhí)行本程序,各進(jìn)程均為變量分配內(nèi)存。部分只有特定進(jìn)程才執(zhí)行的代碼均放在條件語(yǔ)句中,一般用myid作為條件來決定本進(jìn)程是否執(zhí)行,如第20-23行。每個(gè)進(jìn)程都可有輸出到屏幕操作,但其它進(jìn)程的輸出內(nèi)容只會(huì)顯示在根進(jìn)程所在節(jié)點(diǎn)的屏幕上。運(yùn)行結(jié)果:1 mpinode2 examples$ su2 Password3 mpinode2 examples# ./bin/mpicc o mycpi ./cpi.c4 mpinode2 examples# scp /usr/

46、local/mpich/examples/mycpi node1: /usr/local/mpich/examples/5 mpinode2 examples# scp /usr/local/mpich/examples/mycpi node3: /usr/local/mpich/examples/6 mpinode2 examples# scp /usr/local/mpich/examples/mycpi node4: /usr/local/mpich/examples/7 mpinode2 examples#exit8 exit9 mpinode2 examples$ ./bin/mpi

47、run -np 4 mycpi10 Process 0 on node211 Process 1 on node112 Process 2 on node313 Process 3 on node415 wall clock time = 0.048537說明:本MPICH安裝在路徑/usr/local下,文件cpi.c默認(rèn)在/usr/local/mpich/examples下。例子中為用4個(gè)節(jié)點(diǎn)運(yùn)算的結(jié)果。要編譯程序,首先進(jìn)入root用戶,如第1-2行所示。第3行編譯cpi.c文件,用mpich提供的mpicc編譯時(shí)要加上路徑,否則系統(tǒng)默認(rèn)mpicc為L(zhǎng)AM實(shí)現(xiàn),導(dǎo)致編譯失敗,參數(shù)“-o”后

48、為編譯后的文件名。第4-7行將編譯所得的mycpi文件分別復(fù)制到節(jié)點(diǎn)1、節(jié)點(diǎn)3、節(jié)點(diǎn)4的同樣的目錄下,因?yàn)椴僮髟诠?jié)點(diǎn)2上進(jìn)行,節(jié)點(diǎn)2已有mycpi文件。第7-8行退出root用戶,因?yàn)榕渲貌⑿协h(huán)境時(shí)各節(jié)點(diǎn)只給其它節(jié)點(diǎn)的mpi用戶放權(quán),因此進(jìn)行運(yùn)算時(shí)需在用戶mpi下進(jìn)行。第9行用mpirun運(yùn)行mpi程序編譯后所得的文件mycpi,-np后為節(jié)點(diǎn)數(shù)目。第10-15行為此并行程序的輸出結(jié)果。5.6 MPI編程模式MPI 并行程序從程序結(jié)構(gòu)上可以分成三種編程模式,包括主從模式(Masterslave、單程序多數(shù)據(jù)模式(SPMD,即Single Program Multiple Data 和多程序多數(shù)

49、據(jù)模式(MPMD,即Multiple Programs Multiple Data。這些編程模式既可以從源代碼的組織形式來劃分,也可以從實(shí)際程序所執(zhí)行的代碼來劃分。它們之間有時(shí)并沒有非常明確的界線。如果從源代碼的組織形式來劃分,SPMD模式的MPI程序中只有一套源代碼,所有進(jìn)程運(yùn)行的都是該代碼;master/slave 模式的MPI程序包含兩套源代碼,主進(jìn)程運(yùn)行其中一套代碼,而從進(jìn)程運(yùn)行另一套代碼;MPMD模式則包含多套源代碼,不同進(jìn)程分別執(zhí)行其中的一套代碼。如果從實(shí)際程序所執(zhí)行的代碼來劃分,一個(gè)并行程序?qū)儆谀姆N編程模式,取決于程序中各進(jìn)程實(shí)際執(zhí)行的代碼是否相同,以及是否具有client/server的特征。如果各進(jìn)程執(zhí)行的代碼大體是一樣的則可以看作SPMD模式,如果具有client/server特征則被認(rèn)為是master/slave模式,否則則為MPMD模式。在這種編程模式劃分中,master/slave和MPMD模式也可以只用一套源碼,不同進(jìn)程執(zhí)行的代碼通過在程序中對(duì)進(jìn)程號(hào)的條件判斷來實(shí)現(xiàn)。實(shí)際編程時(shí),相對(duì)于使用多套不同的源碼而言,使用一套源碼更便于代碼的維護(hù),并且MPI并行程序

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論