Android手機上Audio DSP頻率低 memory小的應對措施

w39發表於2021-09-09

我在前面的文章()中說過Android手機上有一塊專門用於音訊處理的DSP,它的特點是頻率低(一般幾百MHZ)、內部memory小(通常不超過100k word)。要想讓Audio DSP上放下更多的內容以及能流暢的執行,要有一些應對措施。今天就聊聊這些措施。

 

1,頻率低的應對措施

由於DSP的頻率低,要想軟體能流暢的執行,就得把執行時的load降下來。主要的措施有兩種,定點化和load最佳化。先看定點化。

 

DSP有定點DSP和浮點DSP之分。一般來說,定點DSP具有速度快,功耗低,價格便宜的特點;而浮點DSP則計算精確,動態範圍大,速度快,易於程式設計,功耗大,價格高。音訊處理用的DSP通常都是定點DSP。定點DSP,處理定點資料會相當快,但是處理浮點資料就會非常慢。那在定點DSP上涉及到浮點運算怎麼辦呢?解決方法是浮點運算定點化來節約處理時間,即用定點數表示浮點數。定點數中最高位表示符號位,符號位右邊n位表示整數,剩下的表示小數。這種表示方法叫Q格式。

 

Q格式表示為:Qm.n,表示資料用m位元表示整數部分,n位元表示小數部分,共需要m+n+1位來表示這個資料,多餘的一位用作符合位(不表示出來)。例如Q15表示小數部分有15位,一個short型資料,佔2個位元組,最高位是符號位,後面15位是小數位,表示的範圍是:-1

再看看Q格式的加減乘除基本運算。加減法時須轉換成相同的Q格式才能加減。不同Q格式的資料相乘,相當於Q值相加,要右移n位得到正確的值。不同Q格式的資料相除,相當於Q值相減,要左移n位得到正確的值。這裡舉一個乘的例子:兩個小數相乘,0.333*0.414=0.137862

0.333*2^15=10911=0x2A9F,0.414*2^15=13565=0x34FD

short a = 0x2A9F;

short b = 0x34FD;

short c = a * b >> 15;  // 兩個Q15格式的資料相乘後為Q30格式資料,因此為了得到Q15的資料結果需要右移15位

這樣c的結果是0x11A4=0001000110100100,這個資料同樣是Q15格式的,它的小數點假設在第15位左邊,即為0.001000110100100=0.1378173828125...和實際結果0.137862差距不大。或者0x11A4 / 2^15 = 0.1378173828125

其他的基本運算就不舉例了,網上講Q格式的有一些文章,感興趣自己去看。

實際應用中,浮點運算大都時候都是既有整數部分,也有小數部分的。所以要選擇一個適當的定標格式才能更好的處理運算。一般用如下兩種方法:一是使用適中的定標,既可以表示一定的整數復位也可以表示小數復位,如對於2812的32位系統,使用Q15格式,可表示-65536.0~65535.999969482區間內的資料。二是全部採用小數,這樣因為小數之間相乘永遠是小數,永遠不會溢位。取一個極限最大值(最好使用2的n次冪),轉換成x/Max的小數(如果Max是取的2的 n次冪,就可以使用移位代替除法)。剛開始用Q格式時會很彆扭,不習慣,用多了就慢慢習慣了。

 

為了降load,我們在軟體開發過程中要做到以下兩點:一是選擇演算法實現時一定要用定點實現。Audio DSP上會執行好多音訊處理演算法,比如各種codec,這些codec的制定者一般會提供兩套reference code,一套定點實現的,一套浮點實現的,我們在選擇時一定要選用定點實現的。對於應用在ARM上的音訊處理演算法,同樣是推薦用定點實現的演算法,雖然ARM頻率高。在ARM上還是要最佳化演算法把load降到儘量低。二是在我們自己的程式碼中如遇到要做浮點運算(比如算百分比),要用Q格式定點化去算,而不是直接用浮點數去運算。

 

再來看load最佳化。我在前面的文章()中load最佳化的一些通用方法,在DSP上也適用。不過由於DSP頻率低,且DSP上執行的音訊處理演算法多,有些演算法又比較耗load(比如AEC),要想讓音訊軟體流暢的執行,複雜的演算法都是要做彙編最佳化的。做這些不僅專業而且耗時,load最佳化沒有最低只有更低。

 

2,memory低的應對措施

DSP的內部memory分兩種,DTCM(Data Tightly Coupled Memory, 資料緊密耦合儲存器)和PTCM(Program Tightly Coupled Memory, 程式緊密耦合儲存器)。DTCM用於存data,PTCM用於存code。同時還有外部memory(DDR),它也可存data和code。data和code儘量放內部,因為這樣速度快效率高,不到萬不得已才將它們放在外部memory上。即使放在外部的也是一些低頻訪問的data和code,如初始化函式等。今天我們主要講的是應對DTCM(data memory)小的措施。DTCM主要分以下幾個區域:const區、data區、bss區、overlay區。const區顧名思義就是放一些常量的,data區是放已初始化的全域性變數和靜態變數,bss區是放未初始化的全域性變數和靜態變數,overlay區主要是根據場景的互斥做一些memory的複用,這是應對memory小的主要措施。我們先看overlay機制。

 

Overlay機制是指不會同時發生的場景下使用的memory可以複用,在音訊上主要有兩種情況。一是music使用的memory可以和voice使用的memory複用,因為音樂播放和打電話不可能同時發生。通常是音樂播放時來電話,音樂播放就暫停,把音樂播放的context以及還在buffer中未播放的資料複製到外部memory上,把music使用的memory讓給voice用。打電話結束後再把放在外部memory上的音樂相關的context以及未播放的資料拷進內部memory原先的位置上,從而繼續音樂的播放。二是各種codec使用的memory的複用,因為同時只有一種codec在使用。音樂的decoder有MP3、AAC,播放音樂時只可能有一種decoder在用。Voice的codec有AMR-NB、AMR-WB、EVS,打電話時只可能有一種codec在用。它與第一種的區別是不需要儲存上下文。這樣可以畫出DTCM的分佈圖,如下圖:

 圖片描述

上圖中memory地址是由低向高增長,分別是const區、data區、bss區、overlay區。在overlay區music和voice有相同的起始地址,在music中decoder MP3和AAC又有相同的起始地址,在voice中codec AMR-NB、AMR-WB和EVS又有相同的起始地址。

 

對於PTCM,同樣可以用overlay機制。不過除非一些程式碼重寫,一般很難省code size了。 

 

除了overlay機制,其他的就要一點一點的摳來省memory了,主要有以下幾點:

1) 定義資料型別時能用short的就不要用int

2)在overlay區域,buffer的大小都是指定的。指定時要正確算出大小值,不要指定大了,指定大了就浪費了。

3)在data區或者bss區的buffer要看是不是分大了,比如有的buffer分三塊就夠了,也就沒必要分四塊了,分四塊一是浪費了buffer,二是有些場景下增加了時延。

4)  DSP上每個thread/task的棧的大小都是指定的。為了省memory,棧的大小不可能很大,一般不超過1k word。這就要求寫程式碼時不能有大的區域性變數陣列等,遇到時就要透過一些技巧解決。如一個要把雙聲道的資料從interleave變成non-interleave的函式,寫成了如下實現,避免了大的區域性變數陣列。通常的做法是用一個大的區域性變數陣列先存右聲道資料,最後再一起拷到指定位置上。

void interleave_to_noninterleave(int16_t *buf, int32_t frame_cnt)

{

    int i,j;

    short temp;

    for(i = 1; i

        temp = buf[2*i];

        for(j = 0; j

            buf[2*i - j] = buf[2*i - j -1];

        buf[i] = temp;

    }

}

5)程式碼編好後會生成一個各buffer起始地址和大小的檔案。關注那些size較大的buffer,分析有沒有減小的可能。

 

總體而言,DSP由於頻率低和memory小的限制,在上面寫程式碼比在ARM上要求高些,花的時間長些。長時間在ARM上寫程式碼,轉到DSP上會有些不習慣,有個適應過程。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/854/viewspace-2803623/,如需轉載,請註明出處,否則將追究法律責任。

相關文章