亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        一種Fortran 到CUDA C 的轉(zhuǎn)換方法①

        2022-06-27 03:54:16劉穎輝遲學(xué)斌姜金榮
        計算機系統(tǒng)應(yīng)用 2022年5期
        關(guān)鍵詞:程序語言

        劉穎輝, 遲學(xué)斌, 姜金榮, 張 峰

        1(中國科學(xué)院 計算機網(wǎng)絡(luò)信息中心, 北京 100190)

        2(中國科學(xué)院大學(xué), 北京 100049)

        1 背景介紹

        隨著計算機科學(xué)的發(fā)展, 不同特色的程序語言不斷涌現(xiàn), 由于Fortran 更接近數(shù)學(xué)語言, 執(zhí)行效率較高,有著較低的開發(fā)成本, 因此在數(shù)值計算、科學(xué)和工程技術(shù)領(lǐng)域, Fortran 依舊占據(jù)主流地位; 大量的數(shù)值計算工程軟件, 開發(fā)語言同樣仍以Fortran 為主. 但在氣候、流體力學(xué)等多個領(lǐng)域的模擬過程中, 隨著計算規(guī)模的擴大以及對于計算精度的更高要求, 程序計算所需的時間也在不斷延長, 部分大型程序需要幾天甚至幾十天才能完成計算. 傳統(tǒng)的優(yōu)化方式是將Fortran 程序與MPI 進行結(jié)合, 通過實現(xiàn)粗粒度的并行來提高計算效率. 近些年來, 隨著高性能計算機的迅速發(fā)展, 通過GPU 和CPU 的異構(gòu)計算平臺[1]來實現(xiàn)細粒度的并行這一課題成為了學(xué)術(shù)界和工業(yè)界的研究熱點. 基于Fortran 所開發(fā)的科學(xué)計算軟件及程序, 通過異構(gòu)并行來實現(xiàn)性能的巨大提升, 在工程上有著急切的需求. 在異構(gòu)計算平臺上, 以用于GPU 編程的CUDA C 語法為主流的行業(yè)標準, AMD 平臺使用的ROCm 語法也參照CUDA C 規(guī)范. 但是從Fortran 到CUDA C 的移植過程, 不僅要求開發(fā)人員對Fortran 與CUDA C 均有較高的熟悉度, 而且在復(fù)雜的文件和模塊中檢索相關(guān)變量的信息并編寫相應(yīng)CUDA 函數(shù)往往有著巨大的工程量, 如果完全通過人工完成, 不僅效率低下、極易出錯, 且后期難以維護和調(diào)試. 因此從Fortran 到CUDA C 的自動轉(zhuǎn)碼工具, 有著極大的需求.

        現(xiàn)如今僅有一些將C 語言轉(zhuǎn)換為CUDA 的工具,加拿大多倫多大學(xué)的Han 等人設(shè)計了一種基于指令的CUDA 編程語言hiCUDA[2], 西安交大的Li 等人實現(xiàn)了一種源到源自動并行化工具GPU-S2S[3], 孫香玉提出了一種面向 CUDA 的源到源并行化架構(gòu) STSCUDA[4], 三者都是用于實現(xiàn)C 語言到CUDA 的自動轉(zhuǎn)換, 但對于大型計算程序來說, 需要人工檢索程序中大量變量的相關(guān)信息, 再對內(nèi)存的分配拷貝釋放等諸多操作插入編譯指導(dǎo)語句. 通過這種方式來進行轉(zhuǎn)化所需的工作量同樣是十分巨大的. 荷蘭埃因霍芬理工大學(xué)的Nugteren 等人設(shè)計了自動轉(zhuǎn)換編譯器Bones[5],Bones 是一個概念性的驗證而非工業(yè)性的編譯器, 其側(cè)重點在于對特定結(jié)構(gòu)的程序進行轉(zhuǎn)換以及自動優(yōu)化,對于實際程序中的復(fù)雜情況不能很好的支持. 除此之外, 部分學(xué)者設(shè)計了與CUDA 轉(zhuǎn)化相關(guān)的編譯器[6-8],取得了不錯的效果. 而從Fortran 向CUDA C 的轉(zhuǎn)化工具, 僅美國國家海洋和大氣管理局地球系統(tǒng)研究實驗室開發(fā)了一種名為F2C-ACC 的編譯器[9], 但作者并未給出具體實現(xiàn)細節(jié)以及實際效果.

        2 應(yīng)用技術(shù)簡介

        2.1 正則表達式

        正則表達式是對字符串進行邏輯過濾的一種邏輯操作[10]. 將某類字符抽象為一些特定的字符, 通過某些特定字符的組合來描述字符串匹配的模式.

        正則表達式具有強大的靈活性與功能性, 僅需非常簡便的代碼就可以實現(xiàn)復(fù)雜的字符串操作. 通過正則表達式, 可以實現(xiàn)如下功能: (1)匹配. 判斷目標字符串是否與描述的模式相匹配; (2)獲取. 從字符串中提取需要的特定信息; (3)編輯. 對字符串的子集進行切割、替換等操作.

        2.2 shell 腳本

        shell 腳本是一種為 shell 編寫的腳本程序, 能夠輕易的處理文件與目錄之類的對象. 通過結(jié)合正則表達式以及sed、grep、awk 等命令可以在短時間內(nèi)完成一個功能強大又好用的腳本, 如下代碼實現(xiàn)了獲取使用操作符“( )”的變量名的功能:

        如果采用C 語言編寫實現(xiàn)上述功能, 可能需要幾十行甚至上百行的代碼, 由此可見shell 腳本在字符串處理方面的強大功能.

        3 轉(zhuǎn)換算法的設(shè)計

        3.1 轉(zhuǎn)換難點分析

        Fortran 與CUDA C 在語法規(guī)范、內(nèi)存分配等方面均有較大的差異, 直接從Fortran 轉(zhuǎn)換為CUDA C 有著較大的難度. 而C 語言與Fortran 無論是從結(jié)構(gòu)方面還是語法的角度都比較相似, 同時在CUDA C 程序中,運行在主機端的代碼仍然采用C 語言進行編寫, C 語言很好的建立了從Fortran 到CUDA C 的橋梁. 因此將Fortran 程序轉(zhuǎn)換為CUDA C 的過程分為如下兩部分: (1) Fortran 語言到C 語言的轉(zhuǎn)換; (2)從C 語言轉(zhuǎn)化為CUDA C. 結(jié)合Fortran 與CUDA C 兩種語言的特點, 轉(zhuǎn)換過程需要解決如下問題:

        (1) 數(shù)組的處理. Fortran 中數(shù)組下標可以采用形如A(index1:index2)形式來自定義起始下標與終止下標, 若采用默認定義, 則起始下標為1[11]; 而在C 語言中所有數(shù)組下標均從0 開始, 且不支持自定義的方式來訪問元素. 對于多維數(shù)組, Fortran 采用列優(yōu)先的方式進行存儲, 而C 語言為行優(yōu)先. 因此如何完成下標映射將成為轉(zhuǎn)換過程中的一大難點. 對于動態(tài)分配的數(shù)組,定義中的維度用“:”來進行, 需要額外檢索allocate 語句來獲取對應(yīng)的數(shù)組維度. Fortran 對數(shù)組的訪問也更加靈活, 如對于維度相同的一維數(shù)組A、B, 則可以直接通過A(:)=B(:)來完成對應(yīng)元素的賦值. 因此對于使用“:”作為維度的相關(guān)語句需要進行較為復(fù)雜的處理.

        (2) 引用外部模塊. Fortran90 采用module 將一系列的數(shù)據(jù)與函數(shù)封裝起來, 任何程序都可以通過use語句來引用該module 中的內(nèi)容; 而在C 語言中引用其他變量通過include 或者extern 關(guān)鍵字來實現(xiàn). 因此轉(zhuǎn)換過程中, 文件的檢索范圍不僅限于本模塊, 還需要包含引用的所有外部模塊. 同時為了將轉(zhuǎn)換后的CUDA C 程序與原有的Fortran 程序鏈接起來, 對變量的名稱需要根據(jù)編譯器進行額外的處理, 因此如何將引用的外部模塊轉(zhuǎn)換為同時兼容C 程序、Fortran 程序的形式將是一大難點.

        (3) CUDA 函數(shù)的處理. 在異構(gòu)計算平臺上, CPU用于控制計算過程和實施存儲策略, 而具體的計算過程則由GPU 進行[12]. 而在GPU 上不能直接訪問CPU端的內(nèi)存, 因此GPU 計算所需的數(shù)據(jù)以及計算得到的結(jié)果必須顯式地與CPU 進行數(shù)據(jù)的傳輸. 除此之外,在GPU 端執(zhí)行的代碼需針對并行做相應(yīng)的修改. 因此針對計算過程中的大量變量, 如何自動生成函數(shù)實現(xiàn)內(nèi)存的分配、拷貝以及釋放將是轉(zhuǎn)換過程中的一大挑戰(zhàn).

        針對以上難點, 本文進行了相關(guān)算法設(shè)計和實現(xiàn).

        3.2 算法設(shè)計與實現(xiàn)

        3.2.1 Fortran 語言到C 語言的轉(zhuǎn)換

        在Fortran 到C 語言的轉(zhuǎn)換過程中, 一方面需要完成相關(guān)語法、關(guān)鍵字等內(nèi)容的轉(zhuǎn)換, 另一方面對于每個變量, 需要從本文件或外部模塊中檢索來建立一張變量信息表, 表中需包含文件名稱、變量名稱、變量類型、維度等轉(zhuǎn)換中必要的信息. 轉(zhuǎn)換流程如圖1 所示.

        圖1 Fortran 到C 的轉(zhuǎn)換流程圖

        (1) 規(guī)范代碼行

        處理源文件中的注釋與跨行. 為保證程序的可讀性, 源程序的注釋不刪除, 將“!”所注釋的內(nèi)容均修改為C 的“//”形式; 刪除跨行符“&”并合并多行為一行, 刪除文件中的空行.

        (2) 提取變量信息

        1) 數(shù)組型變量

        首先檢索源文件中的變量定義語句, 獲取所有的內(nèi)部數(shù)組變量名稱. 根據(jù)下標運算符“( )”提取所有可能的數(shù)組變量, 去除關(guān)鍵字、函數(shù)名以及內(nèi)部數(shù)組變量,得到所有外部數(shù)組類型的變量名稱.

        通過對源文件進行檢索, 截取數(shù)組變量的定義或allocate 語句的相關(guān)信息, 建立內(nèi)部數(shù)組變量與相應(yīng)維度、類型的映射關(guān)系. 同理, 根據(jù)use 語句提取源文件引用的module 名稱, 在相應(yīng)文件中查找并建立外部數(shù)組變量信息表.

        2) 基本數(shù)據(jù)類型變量

        由于直接從源文件篩選可能的變量名不僅效率低下, 而且準確率較低, 因此從引用的模塊中提取出所有的變量定義(不含數(shù)組類型變量), 依次進行過濾, 僅保留在源文件中使用的子集, 從而建立外部變量與相應(yīng)類型的映射關(guān)系.

        (3) 引用模塊轉(zhuǎn)換

        對源文件中引用的每一個模塊, 生成相應(yīng)的頭文件, 該文件中對使用到的相關(guān)變量進行聲明. 對于非數(shù)組型變量直接將其轉(zhuǎn)換為C 語言的形式. 對于數(shù)組型變量, 由于C 程序數(shù)組的處理與Fortran 存在較大差異, 如C 程序數(shù)組維度僅能通過常量定義, 起始下標為0, 不支持自定義下標等, 因此將其聲明為同類型的指針變量, 通過一維數(shù)組的方式進行訪問.

        (4) 結(jié)構(gòu)轉(zhuǎn)換

        1) 關(guān)鍵字以及語句的轉(zhuǎn)換

        將Fortran 中的關(guān)鍵字與語句均轉(zhuǎn)換為C 語言中對應(yīng)的形式, 包括但不限于表1 的內(nèi)容. 注釋在C 程序中無需使用的語句, 最大限度保證程序的可讀性.

        表1 Fortran 與C 關(guān)鍵字及語句對照表

        2) 數(shù)組下標索引的轉(zhuǎn)換

        對于Fortran 中定義的數(shù)組, 如real(8) dimension(index1) :: A, 則數(shù)組A 的維度大小為index1, 由于C 語言中起始下標默認為0, 當通過下標來進行元素的訪問時需要減去相應(yīng)的偏移量, 如A(i) 應(yīng)轉(zhuǎn)換為A[i-1]. 更一般的, 對于Fortran 中自定義下標的數(shù)組,如: real(8) dimension(index1:index2) ::B, 則數(shù)組B 的維度大小為index2-index1+1, 相應(yīng)的訪問B(i)應(yīng)轉(zhuǎn)換為B[i-index1].

        對于多維數(shù)組, 如Fortran 中定義數(shù)組real(8),dimension (dimx, dimy) :: D, 由于行列優(yōu)先的不同, 對應(yīng)C 程序的數(shù)組定義為: double D[dimy][dimx] , 相應(yīng)下標訪問D(i,j)對應(yīng)C 程序中的D[j-1][i-1]; 根據(jù)上文所述, 將多維數(shù)組均視為一維數(shù)組進行相關(guān)運算, 下標訪問需進一步轉(zhuǎn)換為D[(j- 1)*dimy+(i-1)].

        3) 數(shù)組名稱的轉(zhuǎn)換

        代碼的前后處理在CPU 上進行, 一般使用原有代碼, 這就需要實現(xiàn)C 和Fortran 的混合編程, 主要是實現(xiàn)數(shù)組或變量的互相訪問. 以ifort 編譯器下的文件param.F90 中定義的外部數(shù)組變量b 為例, 將數(shù)組名稱b 轉(zhuǎn)換為param_mp_b_, 所有外部數(shù)組變量均要做相應(yīng)的替換.

        Fortran 到C 的整體轉(zhuǎn)換算法如算法1 所示.

        算法1. Fortran 到C 的轉(zhuǎn)換1. 輸入Fortran 文件2. 預(yù)處理3. 匹配call 語句, 提取函數(shù)名稱4. 匹配use 語句, 提取引用模塊名稱5. for file in 源文件, 引用模塊文件6. 匹配變量define 語句、allocate 語句7. 存儲變量名稱、所在文件、類型、維度等信息8. end for 9. for var in 變量信息表10. if var in 引用模塊文件11. //以ifort 編譯器為例12. 替換變量名稱: var → filename_mp_var_13. end if 14. end for 15. 轉(zhuǎn)換Fortran 關(guān)鍵字、語句為C 形式16. 創(chuàng)建頭文件17. 輸出C 文件

        3.2.2 C 語言到CUDA C 的轉(zhuǎn)換

        CUDA C 語法規(guī)定用__global__修飾符所修飾的函數(shù)為核函數(shù), 核函數(shù)運行在設(shè)備端, 而運行在主機端的代碼與C 語言是完全兼容的. 因此從C 語言到CUDA C的轉(zhuǎn)換僅需考慮在設(shè)備端運行的部分代碼即可, 這部分代碼所涉及的數(shù)據(jù)以及得出的結(jié)果均需要在主機與設(shè)備內(nèi)存間顯式的進行復(fù)制[13], 即需要額外生成對應(yīng)的拷貝函數(shù). 除此之外, 由于核函數(shù)只能在主機端調(diào)用,設(shè)備端執(zhí)行[14], 因此主機端需要顯式的進行核函數(shù)的調(diào)用, 并傳遞運行時所需參數(shù), 核函數(shù)的內(nèi)容需結(jié)合多線程并行的特點做出相應(yīng)的調(diào)整. 轉(zhuǎn)換流程如圖2 所示.

        圖2 C 到CUDA C 的轉(zhuǎn)換流程圖

        (1) 內(nèi)存拷貝函數(shù)的生成

        1) 確定拷貝對象

        核函數(shù)中參與運算的所有數(shù)組變量均需要在設(shè)備端顯式的進行內(nèi)存的分配. 通過查找上文建立的變量信息表確定類型與維度, 聲明同類型空指針, 分別利用cudaMalloc、cudaFree 進行GPU 內(nèi)存的分配與釋放.如參與運算的某一數(shù)組定義為real, dimension(m, d):: a,則需生成如下CUDA C 語句:

        其中內(nèi)存分配與釋放語句中的CHECK 為宏定義,用于接收cudaError 并輸出提示信息, 保證程序的健壯性.

        2) 生成拷貝函數(shù)

        以所有外部數(shù)組變量為全集, 檢索源文件中賦值運算符“=”, 出現(xiàn)在運算符左側(cè)的所有變量將其從設(shè)備端拷貝到主機端, 相應(yīng)拷貝函數(shù)封裝為test_DtoH(), 出現(xiàn)在運算符右側(cè)的變量將其從主機端拷貝到設(shè)備端,相應(yīng)拷貝函數(shù)封裝為test_HtoD(). 如對于c(j, i)=c(j,i)+a(j, k)*b(k, i), 則應(yīng)有如下函數(shù)被生成:

        extern "C" void test_DtoH(){//從GPU 到CPU 的內(nèi)存拷貝

        (2) 核函數(shù)的轉(zhuǎn)換

        1) 核函數(shù)范圍的確定

        一般情況下, 建議用戶將核函數(shù)部分放在一個單獨的Fortran 文件中, 使用subroutine 或者function 來將其封裝, 在主程序中通過call 語句來對其進行調(diào)用.

        同時, 我們允許手動指定核函數(shù)的范圍, 在對應(yīng)的代碼塊加入如下指導(dǎo)語句:

        二者之間的代碼被認為是一個完整的名為test 的核函數(shù).

        2) 形參列表的確立

        核函數(shù)中所用到的變量均需要作為形參列表的一部分來進行傳遞, 具體分類如下: ① 對于基本數(shù)據(jù)類型: 整型、實型、字符型、邏輯型, 直接將其轉(zhuǎn)換為C 中所對應(yīng)的類型, 作為參數(shù)來進行傳遞. ② 對于數(shù)組型變量, 將其均視為一維數(shù)組, 相應(yīng)類型的指針作為形參來進行傳遞. ③ parameter 關(guān)鍵字定義的常量采用在頭文件中宏定義的方式進行處理, 無需進行參數(shù)的傳遞.

        3) 函數(shù)體的轉(zhuǎn)換

        GPU 上的計算以kernel 函數(shù)為主, 科學(xué)計算軟件中主要的計算代碼是do 循環(huán)計算(模板計算), 圖3 給出了模板計算的kernel 函數(shù)生成. 首先是插入了線程號計算代碼, 然后根據(jù)線程號將循環(huán)計算任務(wù)分配給相應(yīng)的線程. 由于控制循環(huán)的變量被修改, 因此原先由循環(huán)變量控制的數(shù)組下標也要做出相應(yīng)的修改. 在Fortran 程序中由數(shù)組下標i 控制的循環(huán), 在循環(huán)展開后i 的含義變?yōu)镃UDA C 程序中的線程號. 多維情況與一維情況類似, 不再贅述.

        圖3 循環(huán)展開格式

        C 到CUDA C 的整體轉(zhuǎn)換算法如算法2 所示.

        算法2. C 到CUDA C 的轉(zhuǎn)換1. 輸入C 文件2. for var in 變量信息表3. if var in 引用模塊文件4. 聲明設(shè)備端相應(yīng)變量, 創(chuàng)建cudaMalloc 函數(shù)5. if var match 賦值語句左側(cè)6. 創(chuàng)建GPU 到CPU 的cudaMemcpy 函數(shù)7. else if var match 賦值語句右側(cè)8. 創(chuàng)建CPU 到GPU 的cudaMemcpy 函數(shù)9. end if 10. 創(chuàng)建cudaFree 函數(shù)11. end if 12. end for 13. 確立形參列表, 生成核函數(shù)14. 轉(zhuǎn)換核函數(shù)函數(shù)體15. 輸出CUDA C 文件

        4 實驗驗證

        本文實驗環(huán)境如表2 所示.

        表2 實驗環(huán)境配置

        4.1 矩陣乘法的異構(gòu)代碼自動生成

        稠密矩陣的乘法是典型的計算密集型問題, 且具有較好的并行性. 以計算矩陣乘法:Am×dBd×n=Cm×n為例進行測試, 輸入矩陣A、B均為隨機生成. 編寫名為MatrixMul 的串行矩陣乘法Fortran 程序, 計算用到的數(shù)組以及相關(guān)變量定義于外部模塊(源程序位于param.F90 中). 基于正則表達式與shell 語言實現(xiàn)本文提出的轉(zhuǎn)換算法并進行驗證, 轉(zhuǎn)換所得結(jié)果與原Fortran 程序計算結(jié)果相同, 驗證了轉(zhuǎn)換方法的正確性.

        轉(zhuǎn)換后程序的性能表現(xiàn)如圖4 所示, 其中橫坐標是矩陣規(guī)模(m×d×n), 縱坐標是計算時間. 點狀柱是原MPI 程序在1 個節(jié)點(2 顆CPU)下的計算時間, 條紋柱與灰柱分別對應(yīng)使用本文算法轉(zhuǎn)換完成和手動實現(xiàn)的CUDA C 代碼在同1 張GPU 卡上的計算時間. 從計算結(jié)果中可以得出, 由本文算法自動轉(zhuǎn)換得到的CUDA C矩陣乘法在性能上與人工編寫的CUDA C 矩陣乘法相當, 較原MPI 程序平均加速了1.83 倍. GPU+CPU 的異構(gòu)計算較原CPU 并行能夠取得更好的加速效果.

        圖4 矩陣乘法性能對比

        4.2 海洋環(huán)流模式LICOM 的異構(gòu)代碼生成

        LICOM 是由中科院大氣物理研究所LASG 國家重點實驗室發(fā)展的全球海洋環(huán)流模式. 它是中國科學(xué)院地球系統(tǒng)模式CAS-ESM 的重要組成部分[15].源程序采用Fortran 語言編寫, 核心計算程序普遍引用了多個模塊, 大量的數(shù)組變量通過allocate 語句來分配內(nèi)存, 以readyt 為例, 核函數(shù)部分計算變量累計86 個, 其中數(shù)組型變量共52 個, 引用自14 個模塊中數(shù)組變量共40 個, 采用本文算法進行代碼自動轉(zhuǎn)換, 生成的文件結(jié)構(gòu)及功能如圖5 所示.

        圖5 readyt 轉(zhuǎn)換文件結(jié)構(gòu)及功能說明

        實驗證明: 對于復(fù)雜的大型程序, 本文提出的轉(zhuǎn)換算法仍能夠自動檢索核函數(shù)所需變量, 從對應(yīng)文件中提取變量的相關(guān)信息, 自動生成內(nèi)存分配、拷貝、釋放等一系列的函數(shù), 結(jié)合現(xiàn)有的變量信息表以及轉(zhuǎn)化完成的代碼進行調(diào)試, 手動修改少量代碼后即可編譯通過, 轉(zhuǎn)化代碼的正確率高達80%以上, 初步完成了LICOM3 CUDA C 異構(gòu)代碼的開發(fā).

        進一步分析轉(zhuǎn)換算法的輸出結(jié)果, 探究轉(zhuǎn)換過程中出錯的主要原因: (1)計算中含有冒號的數(shù)組索引未能進行相應(yīng)的展開; (2) readyt 內(nèi)部定義的部分數(shù)組變量在實際的CUDA C 程序中仍需進行額外的處理. 若針對上述兩點預(yù)先對Fortran 程序進行簡單的修改, 則轉(zhuǎn)換正確率可達90%以上.

        經(jīng)過測試, Fortran 程序中readyt 函數(shù)平均耗時72.32 s, 轉(zhuǎn)換完成的CUDA C 程序中該函數(shù)平均耗時1.29 s, 加速了56.06 倍.

        5 結(jié)論展望

        本文在深入分析Fortran 語言以及CUDA C 相關(guān)特征的基礎(chǔ)上, 使用正則表達式和shell 語言, 實現(xiàn)了一套邏輯清晰、功能強大的Fortran 到CUDA C 的轉(zhuǎn)換方法. 經(jīng)過測試, 該方法轉(zhuǎn)換正確率高達80%以上,轉(zhuǎn)換后的代碼性能與人工編寫的CUDA C 代碼相當,能夠有效節(jié)省大型程序的移植時間. 本文的實現(xiàn)存在一定的局限性, 對于較復(fù)雜的軟件代碼得到的轉(zhuǎn)換結(jié)果仍需開發(fā)人員手動進行調(diào)試, 且實現(xiàn)的代碼還需要進行深度優(yōu)化.

        猜你喜歡
        程序語言
        語言是刀
        文苑(2020年4期)2020-05-30 12:35:30
        試論我國未決羈押程序的立法完善
        讓語言描寫搖曳多姿
        失能的信仰——走向衰亡的民事訴訟程序
        “程序猿”的生活什么樣
        多向度交往對語言磨蝕的補正之道
        英國與歐盟正式啟動“離婚”程序程序
        累積動態(tài)分析下的同聲傳譯語言壓縮
        創(chuàng)衛(wèi)暗訪程序有待改進
        我有我語言
        日本熟妇高潮爽视频在线观看| 亚洲av无码潮喷在线观看| 国产偷窥熟女精品视频| 午夜亚洲国产精品福利| 男生自撸视频在线观看| 国产欧美日韩一区二区加勒比| 最近中文字幕视频完整版在线看| 一级片麻豆| 中文字幕一区二区三区四区久久| 久久久久成人精品免费播放网站| 亚洲av日韩av天堂久久不卡| 亚洲av中文无码乱人伦在线观看| 无码国产精品一区二区免费模式| 亚洲视频99| 国产丝袜在线福利观看| 日韩av在线不卡一区二区| 又大又粗欧美黑人aaaaa片| 欧美 国产 日产 韩国 在线| 国产成人自产拍免费视频| 国产毛片视频一区二区三区在线 | 国产夫妻精品自拍视频| a级毛片免费观看在线播放| 国产内射性高湖| 超级少妇一区二区三区| 一区二区三区四区在线观看日本| 国产在线 | 中文| 国产亚洲欧美在线观看的| 国产猛男猛女超爽免费av| 日本xxxx色视频在线观看| 亚洲欧美日韩精品久久亚洲区 | 久久视频在线| 亚洲a∨天堂男人无码| 日韩熟女精品一区二区三区视频 | 亚洲精品视频免费在线| 中出人妻希奇杰卡西av| 国产美女久久精品香蕉69| 午夜影院91| 中文av字幕一区二区三区| 丰满爆乳在线播放| 最新国产精品亚洲二区| 日韩精品一区二区av在线|