在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 汽車電子 > 設計應用 > DSP應用優(yōu)化技術――第二部分

            DSP應用優(yōu)化技術――第二部分

            ——
            作者:TI/Rob Oshana 時間:2006-09-29 來源:電子產(chǎn)品世界 收藏

            簡介

              數(shù)字信號處理 () 是一種采用增強功能處理信號與數(shù)據(jù),以及修改這些信號的方法。數(shù)字信號處理技術還可用于分析信號,以確定特定的信息內(nèi)容。 主要涉及真實信號的處理。這些信號根據(jù)序號進行轉(zhuǎn)換與表示。然后采用數(shù)學方法處理這些信號,以便從信號提取特定信息或者以某種方式轉(zhuǎn)換信號。

               一般駐留在實時,其中計算的及時性與其正確性同等重要。DSP 在這些環(huán)境中很普通,因為對它們經(jīng)過精心設計后,可非常迅速地執(zhí)行常見的信號處理運算。DSP 的可編程性使應用能夠隨時間而改變和演進,從而為應用供應商提供眾多優(yōu)勢。對 DSP 編程需要了解應用、DSP 硬件架構(gòu)、以及用于生成可滿足系統(tǒng)緊迫期限要求的高效實時軟件代碼生成工具。

              本文是系列文章的第二部分,總結(jié)在實踐中從高性能 DSP 獲得數(shù)量級速度提高所采用的某些技術。

            優(yōu)化的首要原則----切勿盲動!

              在開始進行任何優(yōu)化之前,您必須了解從何處著手。就性能方面來看,并非所有軟件生來相同!您必須首先了解瓶頸在何處。一旦分析了應用,就可以開始調(diào)整代碼。應用程序分析意味著權衡需在每部分代碼所花費的時間(或者所占用的內(nèi)存、所消耗的功率)。軟件的某些部分可能只執(zhí)行一次(初始化)或者只執(zhí)行少數(shù)幾次。如果費盡心思優(yōu)化此部分代碼并非明智之舉,因為獲得的整體節(jié)省效果會是微乎其微。更可能的情況是,會有幾部分軟件執(zhí)行很多次,盡管代碼自身可能會很短,但代碼執(zhí)行頻繁意味著花費在該代碼的總循環(huán)會很多。即使如果您在這些代碼中可以節(jié)省一、兩個循環(huán),所獲得的節(jié)省也會很可觀。這就是在調(diào)整和優(yōu)化過程中您應該多費些時間的地方。 

            并行是關鍵所在

              在編程超量標及 VLIM 期間時所要遵循的標準原則是"保持流水線充滿"(Keep the pipelines full!)。充滿的流水線意味著有效的代碼。為了確定流水線充滿的程度,您需要費些時間檢查程序生成的語言代碼。通??梢愿鶕?jù)代碼中NOP 的冗余性檢查低效的代碼。

              為了證明在基于 VLIW 的超量標機器中并行性的優(yōu)勢,讓我們首先從圖 1 所示的簡單循環(huán)程序入手。如果我們準備編寫此程序的串行語言實施,其代碼會與圖 2 中的類似。此循環(huán)采用超量標機器的兩個可用側(cè)之一。通過指令與 NOP 計數(shù),它需要 26 個循環(huán)來執(zhí)行循環(huán)的每個迭代。但是,我們可以做得更好。

              在這個例子中,需要注意兩件事情。許多執(zhí)行單元并未使用,而是處于空閑狀態(tài)。這是對處理器硬件的浪費。其次,在匯編程序的此部分存在眾多延遲間隙(準確說是 20 個),在其中 CPU 需要停滯下來等待加載或保存數(shù)據(jù)。在 CPU 停滯時,不會進行任何操作。在試圖處理大量數(shù)據(jù)時,這對處理器是很糟糕的事情。

              有眾多方法可以在等待數(shù)據(jù)期間保持 CPU 運轉(zhuǎn)。我們可以執(zhí)行不依賴正在等待的數(shù)據(jù)的其他運算。我們還可以使用超量標結(jié)構(gòu)的兩側(cè)來幫助加載和保存其他數(shù)據(jù)值。圖 3 中的代碼就是一種對串行版本的改進。我們已經(jīng)將 NOP 的數(shù)量從 20 個降至了 5 個。我們還可以并行執(zhí)行某些步驟。第 4 行和第 5 行同時執(zhí)行向器件的兩個單元(D1 和 D2)的加載。此代碼還執(zhí)行循環(huán)前的分支運算,然后充分利用與該運算相關的延遲來完成當前循環(huán)的運算。請注意,在該代碼中存在新的一列,它可指定您希望將哪次執(zhí)行單元用于特定運算。這種指定執(zhí)行單元的靈活性使您能夠更好地對運算進行控制。

            1 void example1(float *out, float *input1, float *input2)
            2 {
            3   int i;
            4
            5   for(i = 0; i < 100; i++)
            6    {
            7       out[i] = input1[i] * input2[i];
            8    }
            9 }

            圖1:簡單的C循環(huán)

            1 ;
            2 ;    serial implementation of loop (26 cycles per iteration)
            3 ;
            4 L1:  LDW  *B++,B5 ;load B[i] into B5
            5   NOP  4  ; wait for load to complete
            6
            7   LDW  *A++,A4 ; load A[i] into A4
            8   NOP  4  ; wait for load to complete

            10   MPYSP       B5,A4,A4 ; A4 = A4 * B5
            11   NOP  3  ; wait for mult to complete
            12 
            13   STW  A4,*C++ ; store A4 in C[i]
            14   NOP  4  ; wait got store to complete
            15
            16   SUB  i,1,i  ; decrement i
            17  [i] B  L1  ; if i != 0, goto L1
            18   NOP  5  ; delay for branch

            圖2:C循環(huán)的串行匯編語言實施

            1 ;  using delay slots and duplicate execution units of the device
            2 ;  10 cycles per iteration
            3
            4 L1:  LDW  .D2 *B++,B5 ;load B[i] into B5
            5 ||  LDW  .D1 *A++,A4 ;load A[i] into A4
            6
            7   NOP   2  ; wait load to complete
            8   SUB  .L2 i,1,i  ;decrement i
            9  [i] B  .S1 L1  ; if i != 0, goto L1
            10
            11   MPYSP       .M1X B5,A4,A4 ; A4 = A4 * B5
            12   NOP  3   ; wait mpy to complete
            13
            14   STW  .D1 A4,*C++ ;store A4 into C[i]

            圖3:C循環(huán)更具并行性的實施

            循環(huán)展開

              循環(huán)展開是一種用于提高在循環(huán)分支邏輯之間執(zhí)行的、指令數(shù)量的技術。它可以降低執(zhí)行循環(huán)分支邏輯的次數(shù)。由于循環(huán)分支邏輯是額外開銷,降低其次數(shù)可以減少開銷,而且可以使循環(huán)主體-結(jié)構(gòu)的重要部分運行更快。通過復制循環(huán)主體一定次數(shù)、然后修改終端邏輯來理解循環(huán)主體的多個迭代,可以展開一個循環(huán)。循環(huán)展開的缺點是使用更多的片上寄存器。每個迭代需要使用不同的寄存器。一旦使用了可用的寄存器,處理器就會開始訪問堆棧保存所需要的數(shù)據(jù)。訪問片外堆棧需要很高的代價,并且有可能消除由展開循環(huán)而實現(xiàn)的增益。只有在循環(huán)的單個迭代循環(huán)展開中的運算沒有使用處理器結(jié)構(gòu)的全部可用資源時,才可以使用循環(huán)展開。如果您對此不很確定,請檢查匯編語言輸出。另一個缺點是增加代碼長度。展開后的循環(huán)需要更多指令,進而需要更多內(nèi)存。

            {{分頁}}

            軟件流水線化

              最佳優(yōu)化策略之一是編寫能夠由匯編程序有效流水線化的代碼。軟件流水線化是一種有效調(diào)度循環(huán)和功能單元的優(yōu)化策略。比如在 TMS320C62x™ 生成中,如果匯編程序了解如何操作,則存在 8 個可以同時使用的功能單元。有時只是 C 代碼結(jié)構(gòu)的細微更改,其就有可能產(chǎn)生大為不同的結(jié)果。在軟件流水線化中,會調(diào)度循環(huán)的多個迭代來并行執(zhí)行。循環(huán)會被重新組織,其結(jié)果是流水線化后的代碼中的每個迭代都是由從原始循環(huán)中不同迭代選擇的指令序列構(gòu)成。在圖 4 所示的例子中說明了一個帶 3 個迭代的 5 步驟循環(huán)。在管線被 "primed" 或者最初加載運算時,存在一個稱為 prolog 的初始階段(循環(huán) n 與 n+1)。循環(huán) n+2~n+4 是實際流水線化后的代碼部分。處理器就是在此部分中針對三個不同循環(huán)(1、2、3)執(zhí)行不同運算(C、B、A)。這里存在一個 epilog 部分,其中,退出循環(huán)之前執(zhí)行剩余的指令。只是一套充分利用的流水線,其可生成速度最快、效率最高的代碼。

              如圖 5 所示,軟件流水線化比循環(huán)展開速度更快,因為盡管建立流水線的開銷更為復雜,但是只執(zhí)行一次,而在展開的循環(huán)中卻要執(zhí)行多次,在標準循環(huán)建立過程中會執(zhí)行許多次。


                            
            圖 4:軟件管道化的五步驟管道


            圖 5:標準循環(huán)開銷與循環(huán)展開及軟件流水線化的對比

              圖 6 說明同樣簡單的部分 C 代碼以及相應的匯編語言輸出。在此例中,要求匯編程序嘗試流水線化代碼。匯編語言輸出中的流水線化后的循環(huán) prolog 和循環(huán)核心部分就是證明。在這個例子中,流水線化的代碼并未達到其最佳效果。您可以通過查看代碼流水線化后的循環(huán)核心中存在多少 NOP 來檢查低效的代碼。此例中,流水線化后的循環(huán)核心總共有 5 個 NOP 循環(huán),第 16 行中 2 個,第 20 行中 3 個。此循環(huán)共需要執(zhí)行 10 個循環(huán)。NOP 是能夠?qū)崿F(xiàn)更有效循環(huán)的首要證據(jù)。但是,這個循環(huán)到底能夠有多短呢?預計最小循環(huán)長度的一個方法是確定哪個執(zhí)行單元使用次數(shù)最多。在該例中,單元 D 比其他任何單元使用得都更頻繁,總共使用 3 次(第14、15 與 21 行)。超量標器件存在兩側(cè),從而在最低 2 個時鐘的循環(huán)中每個時鐘可以使用每個單元兩次(D1 和 D2),即一個時鐘 2 次 D 運算,第二個時鐘中 1 個 D 單元。匯編程序具有足夠的智能在流水線的兩側(cè)使用 D 單元(第 14 行和第 15 行),從而使它能夠并行化指令并且只使用 1 個時鐘。在等待完成加載的同時可以執(zhí)行其他指令,而不是坐等 NOP 延遲浪費時間。

            {{分頁}}

            1 void example1(float *out, float *input1, float *input2)
            2 {
            3   int i;
            4
            5   for(i = 0; i < 100; i++)
            6    {
            7       out[i] = input1[i] * input2[i];
            8    }
            9 }

            1 _example1:
            2 ;** ---------------------------------------------------------*
            3            MVK        .S2    0x64,B0
            4
            5            MVC        .S2    CSR,B6
            6 ||         MV         .L1X      B4,A3
            7 ||         MV         .L2X      A6,B5

            8            AND        .L1X      -2,B6,A0
            9            MVC     .S2X      A0,CSR
            10 ;** ---------------------------------------------------------*
            11 L11:        ; PIPED LOOP PROLOG
            12 ;** ---------------------------------------------------------*
            13 L12:        ; PIPED LOOP KERNEL

            14            LDW        .D2       *B5++,B4      ;
            15 ||         LDW        .D1       *A3++,A0     ;

            16            NOP                2
            17 [ B0]      SUB        .L2       B0,1,B0       ;
            18    [ B0]      B         

            .S2       L12              ;
            19            MPYSP      .M1X      B4,A0,A0     ;
            20            NOP                  3
            21            STW        .D1       A0,*A4++    ;
            22 ;** --------------------------------------------------------*
            23            MVC        .S2       B6,CSR
            24            B          .S2       B3
            25            NOP                  5
            26            ; BRANCH OCCURS

            圖 6:C 語言例子以及相應流水線化的匯編語言輸出

              在循環(huán)的簡例中,很明顯輸入獨立于輸出。換句話說,并不存在相關性。但是,匯編程序?qū)@點一無所知。匯編程序一般來說是有點悲觀的小家伙。在形勢不明朗的情況下,它一般并不進行任何優(yōu)化。匯編語言比較保守,會假定輸入每次通過循環(huán)時會依賴此前的輸出。如果了解了輸入與輸出無關,我們就可以通過把 input1 和input2 斷言為 "const" 來提示匯編程序,向它說明這些字段不會改變。這是進行軟件流水線化和節(jié)省吞吐量的切入點。圖 7 說明了這種 C 代碼及其相應匯編語言。

            1 void example2(float *out, const float *input1, const float *input2)
            2 {
            3   int i;
            4
            5   for(i = 0; i < 100; i++)
            6    {
            7       out[i] = input1[i] * input2[i];
            8    }
            9 }


            1 _example2:
            2 ;** ---------------------------------------------------------------*
            3            MVK      .S2             0x64,B0
            4
            5            MVC           .S2             CSR,B6


            6 ||         MV            .L1X            B4,A3
            7 ||         MV            .L2X            A6,B5
            8

            9            AND           .L1X           -2,B6,A0
            10
            11            MVC           .S2X           A0,CSR
            12 ||         SUB           .L2            B0,4,B0
            13
            14 ;** --------------------------------------------------------------*
            15 L8:        ; PIPED LOOP PROLOG
            16
            17            LDW           .D2            *B5++,B4     ;
            18 ||         LDW           .D1            *A3++,A0     ;
            19
            20            NOP                          1
            21
            22            LDW           .D2            *B5++,B4     ;@
            23 ||         LDW           .D1            *A3++,A0     ;@
            24
            25    [ B0]   SUB           .L2            B0,1,B0      ;
            26
            27    [ B0]   B             .S2            L9           ;

            28 ||         LDW           .D2            *B5++,B4     ;@@
            29 ||         LDW           .D1            *A3++,A0     ;@@
            30

            31            MPYSP         .M1X           B4,A0,A5      ;
            32 || [ B0]   SUB           .L2            B0,1,B0       ;@
            33
            34    [ B0]   B             .S2            L9            ;@
            35 ||         LDW           .D2            *B5++,B4      ;@@@
            36 ||         LDW           .D1            *A3++,A0      ;@@@
            37
            38            MPYSP         .M1X           B4,A0,A5      ;@
            39 || [ B0]   SUB           .L2            B0,1,B0       ;@@
            40
            41 ;** --------------------------------------------------------------*
            42 L9:        ; PIPED LOOP KERNEL
            43
            44    [ B0]   B             .S2            L9           ;@@
            45 ||         LDW           .D2            *B5++,B4     ;@@@@
            46 ||         LDW           .D1            *A3++,A0     ;@@@@
            47 

            48            STW           .D1            A5,*A4++     ;
            49 ||         MPYSP         .M1X           B4,A0,A5     ;@@

            50 || [ B0]   SUB           .L2            B0,1,B0      ;@@@
            51
            52 ;** --------------------------------------------------------------*
            53 L10:        ; PIPED LOOP EPILOG
            54            NOP                          1
            55
            56            STW           .D1            A5,*A4++     ;@
            57 ||         MPYSP         .M1X           B4,A0,A5     ;@@@
            58
            59            NOP                          1
            60
            61            STW           .D1            A5,*A4++     ;@@
            62 ||         MPYSP         .M1X           B4,A0,A5     ;@@@@

            64            NOP                         1
            65            STW           .D1           A5,*A4++      ;@@@
            66            NOP                         1


            67            STW           .D1           A5,*A4++      ;@@@@
            68 ;** --------------------------------------------------------------*
            69             MVC         
            .S2            B6,CSR
            70             B            .S2            B3
            71             NOP                         5
            72            ; BRANCH OCCURS

            圖 7:相應流水線化后的匯編語言輸出

              在查看此匯編語言時需要留意幾點。首先,流水線化后的循環(huán)核心已經(jīng)縮小了。事實上,現(xiàn)在循環(huán)只有 2 個循環(huán)長。第 44-47 行在循環(huán)的第一個循環(huán)執(zhí)行(并行指令由 || 符號表示),第 48-50 行在第二個循環(huán)執(zhí)行。利用我們通過 "const" 斷言提供的附加相關性信息,匯編程序已經(jīng)能夠充分利用這些單元中的并行優(yōu)勢來高效地調(diào)用循環(huán)的內(nèi)部部分。但是,這點需要一定代價。代碼的 prolog 和 epilog 部分現(xiàn)在已經(jīng)變得大得多了。更緊密的流水線化核心將需要更多啟動 (priming) 運算來根據(jù)各種指令和分支延遲協(xié)調(diào)所有的執(zhí)行。但是,一旦啟動 (primed),核心循環(huán)就能夠以極其快的速度運行,在循環(huán)的各個迭代上執(zhí)行運算。正如我們在前面所說明,軟件流水線化的目標是提高經(jīng)常性事件速度。 核心在此例中就是經(jīng)常性事件,而且我們已經(jīng)大大提高了它的速度。對于循環(huán)計數(shù)比較小的循環(huán),可能不值得進行代碼流水線化。但是,對于那些要執(zhí)行數(shù)千次的循環(huán)計數(shù)較大的循環(huán)來說,軟件流水線化是唯一的出路。

            {{分頁}}

              在流水線化后的核心要執(zhí)行的 2 個循環(huán)中,會有許多事情發(fā)生。在匯編語言列表中的右列說明每條指令執(zhí)行哪個迭代。每個 "@" 符號表示迭代計數(shù)。因此,在這個核心中,第 44 行為迭代 n+2 執(zhí)行分支,第 45 和 46 行為迭代 n+4 執(zhí)行加載,第 48 行保存迭代的結(jié)果,第 49 行為迭代 n+2 執(zhí)行乘法,而第 50 行為迭代 n+3 執(zhí)行減法,這一切全在 2 個循環(huán)中完成。一旦流水線化后的核心停止執(zhí)行,epilog 就會完成運算。匯編程序能夠使該循環(huán)變成 2 個循環(huán)長,這正是我們通過檢查低效代碼版本所期望達到的結(jié)果。

              流水線化的功能的代碼長度有所增加,看看所生成的代碼就一目了然。這是編程人員為了追求速度而不得不面對的折中之一。

              在使用堆棧的程序中,還存在必須要解決的其他問題。匯編程序必須確定在堆棧(需要更多時間訪問)中放置哪些變量以及要在快速片上寄存器中放置哪些變量。匯編程序有時并不能確定變量的去處。它會干脆不費神地嘗試去流水線化包含太多變量的循環(huán)。這種情況下,明智的做法是將大循環(huán)分成較小的循環(huán),并使匯編程序能夠?qū)χM行流水線化(圖 8)。

              我們可以不采用:

            for (expression)
            {
             Do A
             Do B
             Do C
             Do D
            }

                   而是采用:

            for (expression)
             Do A

            for (expression)
             Do B

            for (expression)
             Do C

            for (expression)
             Do D

            圖 8:將大循環(huán)分成較小的循環(huán)能夠更有效地進行流水線化

              如果不仔細分析并結(jié)構(gòu)化代碼,就不存在所謂的軟件流水線化。例如,不要流水線化沒有足夠處理功能的循環(huán)。另一方面,也不能流水線化具有太多處理功能的循環(huán),因為循環(huán)主體會耗盡可用的寄存器。此外,不能流水線化循環(huán)內(nèi)的函數(shù)調(diào)用。相反,如果您希望獲得流水線化后的循環(huán),需要利用函數(shù)的內(nèi)聯(lián)擴展來代替函數(shù)調(diào)用。

              流水線化的缺陷之一是禁用中斷。完全 primed 管道中間的中斷會破壞指令執(zhí)行中的協(xié)同配合 (synergy)。匯編程序會在進入流水線化的部分之前禁用中斷來保護軟件流水線化運算,并在退出時啟用中斷(圖 9)。這意味著獲得軟件流水線化效率的代價是不可搶先的代碼部分。編程人員必須能夠確定不可搶先代碼的部分對現(xiàn)實性能的影響。


                                                 
             圖 9:在代碼的軟件流水線化期間禁用的中斷

            最后手段――匯編語言

              經(jīng)??梢酝ㄟ^稍微修改 C 語言代碼來緩解這種狀況,但是獲得最佳(或接近最佳)解決方案需要時間和多個迭代。圖 10 說明了采用這種方法優(yōu)化代碼的過程。最后的方法是采用匯編語言對算法進行編碼。匯編語言難以編寫、理解和維護。正在開發(fā)相關工具使匯編語言編程人員更易于為超量標與 VLIW 處理器編寫有效的代碼。例如,匯編語言優(yōu)化器就能使編程人員編寫串行匯編語言,然后自動將其優(yōu)化到軟件流水線化后的循環(huán)中。


              &



            評論


            相關推薦

            技術專區(qū)

            關閉