2009年10月15日 星期四

MPEG-4 code SIMD 化 -- 雙魚

=========== 2009.10.15===========

還是拿原本的code來改 , 主要把第一次改的換種方法
以及 找了第二個可以改成SIMD的部分

(1) 第一部分的別種方法:

->本來的方法是圖的上半部 , vector先裝入IDCT_Coeffo [0~3]的數值
讓IDCT_Coeffo 和 V0 相乘結果先暫存在 temp
再把暫存在 temp中的四個數值相加後放進去R0[i]
R0[i] = temp[0] + temp[1] + temp[2] + temp[3] ;
老師有提過 這邊一堆+ 可能會影響速度 , 所以換種方法


-> 後來的方法是圖的下半部 , 先把 IDCT_Coeffo 陣列轉置
轉置的結果IDCT_Coeffo 陣列順序變成 0 4 8 12 ......
讓vector先裝入IDCT_Coeffo [0 4 8 12] 的數值 再和V0相乘
相乘的結果就可以直接放入R0[0~3] 中

下次vector則裝入IDCT_Coeffo [1 5 9 13] 的數值 再和V0相乘
相乘的結果直接和上一次的R0[0~3] 累加 , 使用vec_madd()去累加


(2) 第二部分的改寫 原始code如下 :

for(i = 0; i <= 7; i++) { k = i& 3 ; if(i & 4) { k ^= 3; temp0 = R0[k] - R1[k]; .... } else { temp0 = R0[k] + R1[k]; ... } .....(略) } -> 觀察一下原本的內容 , 發現迴圈跑8次要做的事情就是產生
R0[0] + R1[0] ,R0[1] + R1[1] ,R0[2] + R1[2] ,R0[3] + R1[3] ,
R0[3] - R1[3] ,R0[2] - R1[2] ,R0[1] - R1[1] ,R0[0] - R1[0]

-> R0 R1在迴圈之前已經計算好 , 迴圈裡面只要他們之間相加減的數值
於是想說新開一個長度8的一維陣列 , 直接把相加減的數值給放好
如此一來 迴圈裡面的這個部份就可以抽掉

R0[k]+R1[k] / R0[k]-R0[k] 可用SIMD提供的 add / sub函式來完成計算
R0[k]-R0[k] 放入btmp[8]的順序如圖 , 從k=3放回來
計算時的vector是從k=0~3裝入 , 相減的結果要先交換位置才能放入btmp


(3) 數據結果 程式跑10000次 :

1. 原始程式在PPE環境下: 1.48 sec
改寫的程式在PPE環境下: 1.15 sec

2. 原始程式在SPE環境下: 1.28 sec
改寫的程式在SPE環境下: 0.48 sec

-> PPE和SPE都是一樣的程式 , 只是使用的函式不同
因為兩個環境支援的函式名稱是不一樣的 , 但是數據差距有點大
PPE只比原本的少了0.3 , 但SPE少了快三倍

我有請小新學長看一下程式 , 應該沒漏掉或放錯迴圈次數
我們兩個對於PPE為什麼只少0.3 都感到很奇怪

=========== 2009.10.8 ===========

整理了在PPE和SPE下寫的兩個範例
一個是4*4矩陣 , 另一個陣列比大小
範例的寫法也有附上解釋
會和大鈞學長討論哪裡還需要做修改



=========== 2009.09.24 ===========

SIMD程式分為使用VMX指令的PPE和使用SPU SIMD指令的SPE
當初會想用PPE是因為不知道怎樣把SPE裡面的時間傳出來
後來可以把測的時間傳出來 , 於是可以去測SPE提供的指令時間

下午報告的數據 因為迴圈次數給錯 , 導致vec_madd()的時間會較慢
所以我把數據重測了一次 , 結果如下:

(1) 一維陣列相乘


(2) 4*4矩陣相乘


從(1)(2)數據看來 , PPE的vec_madd()函式會比原本節省大約3倍
SPE的spu_mul()函式會比原本節省大約4倍

因為兩種函式都有大幅度的降低時間 , 但是改寫的code效果沒有很好
可能要更改原本程式 , 讓原本的程式能比較適合用在SIMD上


=========== 2009.09.16 ===========

把改寫成SIMD的部分下去測時間 , 時間比較如下:


會發現改寫成SIMD的程式時間 比原本的程式還慢
主要改寫後的程式如下:
原本 : R0[i] += IDCT_Coeff0[k+0] * V0[i];
SIMD : tmp[q] = vec_madd( id_c0[i] , v0[q] , vzero );
R0[i] = Tmp[i] + Tmp[i+1] + Tmp[i+2] + Tmp[i+3] ;

-> 做法是 , 一次讓IDCT_Coeff0[k+0] * V0[i] 的計算做完
把數值存在tmp陣列裡面 , 接著把陣列數值相加再給R0

老師提到 , 把數值給R0[i]用到很多個+ , 可能是這邊導致速度變慢
因為之前沒有實際測過 SIMD支援的函式 是否真的有加速功能
這個禮拜會寫簡單的例子去測時間 .
小新學長說先寫簡單的一維陣列相乘 , 確定SIMD的函式是否有加快
如果有加快 , 再寫矩陣相乘的範例 , 然後去測時間


=========== 2009.08.31 ===========

原本cpp檔需要傳入的參數有7個 , 參照小新學長的意見
除了Mode不是等於0 就是 1 , 其他6個都當成64的一維陣列
這7個參數直接在cpp檔設定 , 一維陣列的數值先隨意給 , 設定如下:
int Mode=0 ;
int BlockLT[64] __attribute__((aligned(16)));
int BlockRT[64] __attribute__((aligned(16)));
int BlockLB[64] __attribute__((aligned(16)));
int BlockRB[64] __attribute__((aligned(16)));
int BlockU[64] __attribute__((aligned(16)));
int BlockV[64] __attribute__((aligned(16)));


在compile時遇到幾個問題或錯誤:
(1) compile時指令的改變
之前寫的範例都是 .c檔 , 這次則是 .cpp檔
所以把指令中的 " gcc " 換成 " g++ " , 這樣就可以了

(2) spu_mul()的語法錯誤
一開始是寫 tmp = spu_mul( id_c0[k], v0) ;
tmp 和 v0這兩個vector所乘載的陣列大小只有4
剛好vector一次就是裝四個 , 以為這樣可以 , 但是compile會有錯誤
後來改成 tmp[q] = spu_mul( id_c0[k] , v0[q] ) ; 變數 q=0
雖然 tmp 和 v0大小只有4 , 但還是要告知vector的起始點

另外 要使用spu_mul() , vector宣告的型態要為float
原本程式V0型態為 int
所以寫成 __vector int *v0 = (__vector int *) V0 ;
compile時會顯示錯誤 , 上網找資料看到都是用float
改成 __vector float *v0 = (__vector float *) V0 ;
這樣執行起來就不會出現錯誤了

測試方面使用SPU SIMD指令的方式
寫了PPE+SPE , SPE裡面就放cpp檔內容
打入compile需要的指令 , 目前不會出現錯誤
但是SPE裡面不能用printf , 這樣無法看到算出來的數字對不對

想說把要印的數值傳到PPE裡 , 執行後印出的數字都是0
可能是要印出的數值沒有傳好 , 或者是數值上有算錯
這邊會想辦法去解決


=========== 2009.08.24 ===========

參考aaa學長提出的兩個方法 , 覺得第二種方法比較簡單
利用這種方法 , 程式碼中原本 d0~d11的變數就可以拿掉
但是第二種方法的問題出在下圖這段程式碼

R0[i] += IDCT_Coeff0[k+0] * V0[0];
R0[i] += IDCT_Coeff0[k+1] * V0[1];
R0[i] += IDCT_Coeff0[k+2] * V0[2];
R0[i] += IDCT_Coeff0[k+3] * V0[3];

--> 這等於把 IDCT_Coeff0陣列和V0陣列相乘的數值都放在R0[i]
SIMD語法中好像沒有這種計算的函式 , 所以不能直接這樣寫


所以把程式碼改寫 如下:

Tmp[i] = IDCT_Coeff0[k+0] * V0[0];
Tmp[i+1] = IDCT_Coeff0[k+1] * V0[1];
Tmp[i+2] = IDCT_Coeff0[k+2] * V0[2];
Tmp[i+3] = IDCT_Coeff0[k+3] * V0[3];

R0[i] = Tmp[i] + Tmp[i+1] + Tmp[i+2] + Tmp[i+3] ;

--> 新開一個tmp[4]陣列,用來暫存 IDCT_Coeff0和V0相乘的數值
把 tmp陣列中四個數值相加後 , 再放回R0[i]
這樣一來 IDCT_Coeff0陣列和V0陣列相乘 的部分就可以一次作

測試的方法: 幫 原本的cpp 和 SIMD化的cpp 各寫PPE+SPE
然後給入一樣的參數數值 , 再去看兩者時間的差別有多少
目前在寫這兩個的PPE+SPE , 但傳入的參數還弄不清楚
所以還無法把程式跑起來



=========== 2009.08.12 ===========


從bbb學長給的mpeg-4檔案中 , 找出要做SIMD的檔案
主要就是改 MacroBlock_DCT_IDCT.cpp
下圖是目前要改成SIMD的地方


(1) 上圖第一個框框的部分, 希望可以用vector去裝變數d0~d11
這樣一次就可以assign 四個變數


(2) 同一個迴圈內, A0=B0+C0 , A1=B1+C1, ..... 這種計算可以改寫
但是 A0=B0+C0 , A1=D1+C1 , A2=B0+C3 ,... 這種不行
所以我把第二個框框的迴圈內容想成下面這樣

希望藉著此種想法 , 讓迴圈內的程式可以改成SIMD化

13 則留言:

Zhong-Ho Chen 提到...

這個code是原瑞為了模擬硬體而寫的
所以不是那麼直覺
建議你可以把for(j=0; j<=3; j++)這個迴圈Unroll, 再把TLB0改成 IDCT_Coeff0[k+0],[k+1], [k+2], [k+3] ....
之後再整理一下, 就可以比較容易看出來

Buffett 提到...

但硬體的code可以直接看出那些資料是可以SIMD的,因為在HW裡那些值也是平行計算的

Zhong-Ho Chen 提到...

d0~d11是硬體register, 軟體不care
展開來之後, 排一起是可能能用SIMD的地方
for(i=k=0; i<3; i++, k+=4)
{
R0[i] = 0;
R1[i] = 0;
R2[i] = 0;
R3[i] = 0;

R0[i] += IDCT_Coeff0[k+0] * V0[0];
R1[i] += IDCT_Coeff1[k+0] * V1[0];
R2[i] += IDCT_Coeff0[k+0] * V2[0];
R3[i] += IDCT_Coeff1[k+0] * V3[0];

R0[i] += IDCT_Coeff0[k+1] * V0[1];
R1[i] += IDCT_Coeff1[k+1] * V1[1];
R2[i] += IDCT_Coeff0[k+1] * V2[1];
R3[i] += IDCT_Coeff1[k+1] * V3[1];

R0[i] += IDCT_Coeff0[k+2] * V0[2];
R1[i] += IDCT_Coeff1[k+2] * V1[2];
R2[i] += IDCT_Coeff0[k+2] * V2[2];
R3[i] += IDCT_Coeff1[k+2] * V3[2];

R0[i] += IDCT_Coeff0[k+3] * V0[3];
R1[i] += IDCT_Coeff1[k+3] * V1[3];
R2[i] += IDCT_Coeff0[k+3] * V2[3];
R3[i] += IDCT_Coeff1[k+3] * V3[3];
}

也可以排成下面這樣
for(i=k=0; i<3; i++, k+=4)
{
R0[i] = 0;
R1[i] = 0;
R2[i] = 0;
R3[i] = 0;

R0[i] += IDCT_Coeff0[k+0] * V0[0];
R0[i] += IDCT_Coeff0[k+1] * V0[1];
R0[i] += IDCT_Coeff0[k+2] * V0[2];
R0[i] += IDCT_Coeff0[k+3] * V0[3];

R1[i] += IDCT_Coeff1[k+0] * V1[0];
R1[i] += IDCT_Coeff1[k+1] * V1[1];
R1[i] += IDCT_Coeff1[k+2] * V1[2];
R1[i] += IDCT_Coeff1[k+3] * V1[3];

R2[i] += IDCT_Coeff0[k+0] * V2[0];
R2[i] += IDCT_Coeff0[k+1] * V2[1];
R2[i] += IDCT_Coeff0[k+2] * V2[2];
R2[i] += IDCT_Coeff0[k+3] * V2[3];

R3[i] += IDCT_Coeff1[k+0] * V3[0];
R3[i] += IDCT_Coeff1[k+1] * V3[1];
R3[i] += IDCT_Coeff1[k+2] * V3[2];
R3[i] += IDCT_Coeff1[k+3] * V3[3];
}

不過上面這兩種實作上都有問題
第一種資料要重排
第二種要先暫存乘法的結果, 再加起來
你應該可以想出更好的方法

Buffett 提到...

aaa太奸詐了,把簡單偷懶的方法都寫出來了...
這樣學妹要苦惱了... :p

SCREAMLab 提到...

給兩位學長拍拍手, 這是我一直希望資深學長姐可以幫助後進的地方.

雙魚不必太緊張, 先做一版簡單的出來, 我的目的是要你先會用所有的工具以及學會分析妳改後的結果. 我們先學會走.

在書裡面, 你就是要提供最簡單的做法的.

雙魚 提到...

謝謝學長提供的意見 ^^
我會當作參考 , 也會想想是否有別的方法

SCREAMLab 提到...

傳入參數先不眾要, 算得是對的就好. 原始DCT不過就是Matrix Mult. 測一下速度吧!

smallnew 提到...

mode參數應該是0和1兩種
這部分兩個都需要測試
其他的參數應該是64的一維陣列
這部分填入任意值應該都可

Zhong-Ho Chen 提到...

那個Mode是用來決定
// DCT : Mode = 0
// IDCT : Mode = 1
Decoder只會用到IDCT

SCREAMLab 提到...

PLease update your thread.

We had a few discussion when you presented. Please write it down.

Also show us your effort on SIMD for Matrix Multiplication.

SCREAMLab 提到...

根據你測Matrix 相乘的結果 是否完整的PPE的SIMD有加速呢?

假如是, 那麼請你開始研究Fast DCT的實作方式, 以便用 SIMD來加速MPEG-4 decoder. 請不要再用原程式了, 如不清楚, 請問一下學長.

另外我們的書進度如何了? 請大鈞與阿凡出來講一下, 很久沒進度報告了.

雙魚 提到...

4*4矩陣相乘,用PPE提供的函式會加速
接下來要做的事情我會問小新學長~

SCREAMLab 提到...

還有其他的fast algorithm嗎?

另外aaa說過PPE與SPE的架構不同, 請你記錄並查一下看有無架構圖可以post.