2011年5月7日 星期六

電腦壞了!

因 Shady 的電腦掛點,
而 Cell 的相關程式及資料都未備份出來,
又因工作關係,
人現在住在台北,
所以 Blog 的更新非常的不頻繁,
等 Shady 購入下台能安裝 Linux 的 PS3 時,
才有可能會頻繁些。

2011年2月28日 星期一

Shady 錯了!TOC 沒比立刻開起一個堆疊快。

之前 Shady 的有關 TOC 之文章中有說 TOC 較堆疊快,
但實際是直接使用堆疊會較快。

Shady 之所以會說 TOC 會較快的原因為:
IBM 的 PowerPC 組合語言相關文章中有提到 TOC 通常是在 Cache 中。

但經 Shady 以更改 setzero 組語函式實際測試後又再想想,
64KB 的 TOC 根本不可能一開始就在 Cache 中,
就算它有在 Cache 中,
那也是 L2 Cache 而不是 DCache。

害 Shady 以為就算直接馬上開個堆疊,
也不會比直接載入在 DCache 中的 TOC 還快,
但經 Shady 證明後並非如此。

所以馬上開個堆疊,
這堆疊會在 Dcache 中,
而 TOC 還是從記憶體載入到 L2 Cache、DCache 中,
故 TOC 只有在第一次載入後,
於第二次載入時才會對整體效能有所幫助。

雖說 TOC 沒比馬上開啟堆疊快,
但會比資料在記憶體或者是更新出 Cache 中的堆疊快就是了。

在 IBM 的一些有關組語的文章中,
其組語的例子中都會有類似以下的 " TOC 宣告 " (紅字):
_start:
       .quad ._start, .TOC.@tocbase, 0

如果你不使用 TOC 請省略這 " TOC 宣告 ",
因為就算沒使用 " TOC 內容 " 或 " 設置 TOC 內容 ",
只要有 " TOC 宣告 " 就會造成函式被呼叫時,
函式和其 TOC 會同時被載入到 Cache 中。

目前最快的 setzero.s

其 tick 值於 4KB 下的測試結果為:13 ~ 16 ticks。(注1)

當中無使用 TOC 也沒有 " TOC 宣告 ",
亦無 Altivec ABI 的指令,
但若做了 " TOC 宣告 " 則會使 tick 值增加到 25 ticks 以上 (注2),
所以不要隨便做 " TOC 宣告 "。

注1:呼叫函式前有使用 dcbt 預先載入函式,
    若無使用 dcbt 則成績為 17 ~ 25 ticks。
注2:呼叫函式前有使用 dcbt 預先載入函式。

2011年2月10日 星期四

再提"雙發射"。

Shady 曾在「撰寫 Cell 的 PPU 之組語所需的知識及技巧。」此文章中提過 "雙發射",
當中有提到「這也是 "雙執行緒" 用來加強 IPC 的機制。」
這裡的 "雙執行緒" 打錯了,應該為 "單執行緒"。

現在來談談如何最佳化 "雙發射" 所該有的注意事項,
由於 PPU 的指令長度都是 32bits ( 4Bytes ),
且指令的預先提取為四個指令、解碼為二個指令同時解碼,
所以預先提取與指令解碼的指令之位址將會是連續的,
以下列程式為例:
    位址       標籤       指令
0x1000_04F0          vand 3,1,2
0x1000_04F4          beq .Lend
0x1000_04F8          add 5,6,7
0x1000_04FC          add 3,8,9
0x1000_0500  .Lend:  sub 4,5,6
0x1000_0504          ldu 10,16(7)
0x1000_0508          stvx 0,5,4
0x1000_050C          std 4,16(7)

從此例中可發現指令位址的尾數都是以 0、4、8、C 為循環,
而 IB (註1) 會提取前四行指令,
而第一、二行指令會先同時解碼,
在解碼的同時亦會檢查 "相依性" 與是否可以 "雙發射",
所以此例的 "雙發射" 結果為:
第一、二行指令可 "雙發射",原因為指令執行單元是 VSU1 與 BRU。
第三、四行指令不可 "雙發射",原因為指令執行單元是 FXU 與 FXU。
第五、六行指令不可 "雙發射",原因為指令執行單元是 FXU 與 LSU、FXU。
第七、八行指令可 "雙發射",原因為指令執行單元是 VSU2 與 LSU。

從結果可看出執行單元的不同就能 "雙發射",
要注意第七、八行的 VSU2 單元一定要在 LSU 之前,
如此順序才能讓 VSU2 與 LSU 達成 "雙發射"。

還有如 ldu、stdu 等指令會同時使用 LSU、FXU 等二個以上的執行單元,
有些指令甚至會使用到三個以上的執行單元。

如果在無相依的狀況下,
但執行單元卻是相同的話,
就會如第五、六行指令一樣不能 "雙發射",
而且第六行指令會在第五行指令的下個 CPU 週期發射。
若第五行指令的執行單元為 LSU 和 FXU 以外的執行單元,
這樣就能讓第五、六行達成 "雙發射" 之條件。
第三、四之行情形如同第五、六行之情形。

所以無相依性、不同單元可一個 CPU 週期發射二個指令,
而無相依性、相同單元只能二個 CPU 週期發射二個指令,
但有相依性、不論相不相同單元都會是二個以上 CPU 週期發射二個指令。

另外,分支指令最好放置在位址尾數為 0x4 和 0x8,
這樣能對分支預測及程式執行有益處。
然後分支目標最好座落於位址尾數為 0x0 和 0x8,
因為解碼都是 0x0 配 0x4,
而 0x8 配 0xC。

最後就是盡量不要使用會 stall 的指令、非管線化的指令和微碼指令,
因為它們會讓 "雙發射" 失效。
其餘該注意的事項之嚴重性都較以上小,
若有興趣者請多參考 IBM 所放出來的文件。

註1:Instruction Buffer 的簡寫。

之前提過的" Altivec ABI "與現在要提的" TOC "。

之前有略提 Altivec ABI,
而這次 Shady 也不會深入講解,
因為 Linux 的預設下,
vrsave 暫存器為 0xFFFF_FFFF 的數值,
也就是所有的向量暫存器 vr0 ~ vr31 都可以使用,
所以之前的程式中若有 Altivec ABI 的相關指令可以刪掉了。

至於 TOC,
Shady 就直接提供網址給大家參考,
因為 Shady 懶得打了 XD。

Linux on POWER 中的 GNU C/C++ 工具鏈。
用於 Power 體系結構的彙編語言,第 2 部分。

以上網頁會有 TOC 的說明和 TOC 的使用簡介。

老實說 TOC 比堆疊還快...。

之前提過的" Cache 堵塞"。

之前 Shady 曾經說過在 16KB 的測試下於沒有執行 dcbf(); 時,
函式測試之結果都會變慢,
其原因被 Shady 稱作" Cache 堵塞",
而正確的稱呼為" Cache 飽和"。

因為 L2 Cache 為 write-back 設計,
又因配置記憶體的相關函式,
它可能於記憶體配置完成時也將 L1 Data Cache (DCache) 塞滿了,
所以當 DCache、L2 Cache 要將資料丟回記憶體時,
就會造成大量資料堵塞住 Cache 和記憶體之間的 Bus,
而且當資料量愈大時,
就會有部分資料在 DCache、L2 Cache中,
而另一部分資料還在記憶體中,
如此又會造成從記憶體區的資料要搬進 Cache 時,
會遇到舊的資料還在 write-back 中,
因此又造成等待時間加長。

在這邊 Shady 要列出一些 DCache 相關組語指令:
dcbt:將資料從記憶體搬入 DCache,
      預備給 load 相關動作使用,
      但若資料不在 L2 Cache 中,
      此指令只會將資料搬到 L2 Cache。
dcbtst:將資料從記憶體搬進 DCache 和 L2 Cache,
        預備給 store 相關動作使用。
dcbst:將資料從 DCache 和 L2 Cache 中更新回記憶體,
       但保留此 Cache line 在 DCache 和 L2 Cache 中。
dcbf:將資料從 DCache 和 L2 Cache 中更新回記憶體,
      但不保留此 Cache line 在 DCache 和 L2 Cache 中。
dcbz:將被指定的 Cache line 設置為零。

當中的 Cache line 又稱為 Cache block,
其長度有 128Bytes,
所以資料要盡量對齊 128Byte 位址,
以方便資料搬進 Cache 中。

還有盡量只使用 dcbt 和 dcbz 指令,
因為其它 DCache 相關指令不會比較快,
而 dcbt 需連續拿取六個不同位址且對齊 128Byte 之資料,
也就是六個 Cache line,
這樣才能掩蓋記憶體延遲,
而 dcbz 需連續拿取四個。
另外將資料 load 進或 store 出暫存器會有四個 CPU 週期,
所以當第一行指令為 load、store 相關指令時,
需再通過三行無相依指令才能於第五行以後的指令使用此暫存器。

以下列出 PS3 的階層式記憶體架構之延遲值:
Memory 到 L2 Cache:~ 400 cycles。
L2 Cache 到 DCache、ICache (註1):~ 40 cycles。
DCache 到 Register:4 cycles。
ICache 到 IB (註2):4 cycles。

註1:L1 Instruction Cache 的簡寫。
註2:Instruction Buffer 的簡寫。

2010年12月29日 星期三

找到原因了...,原因有點囧就是了。

那就是 Shady 的測試主程式中的此段程式:

    for(i = 32, j = 0; i > 0; i--, j += 128) {
        __dcbf(d + j);    /* 就是當中的 d+j,Shady 寫成 s+j 了。*/
    }

因為 Shady 的測試程式一開始就是為 memcpy 類型的函式撰寫的,
所以會配置二個記憶體空間,
一個為來源 s、一個為目的 d,
故一直以來 Shady 都如上面程式解說般,
目的記憶體 d 都未從 cache 中消除,
而 memset 類型的函式就一直使用 d 而未更正,
因此 d 一直堵塞住 cache 而 Shady 卻不自知。
現在更正之後,
16KB 記憶體空間的測試就正常了,
但 setmem 的成績還不怎麼理想,
因為它還有至少 30 tick 的空間可以改善,
所以 Shady 下次放出的組語函式有可能是 setmem 修正版或 memcpy 的組語版,
也有可能二者同時釋放,
但不久之後 Shady 會比較忙,
所以釋放的頻率會比現在更低上很多,
因此請大家多體諒。

2010年12月28日 星期二

超過 16KB 的記憶體配置後,造成程式緩慢之因素的猜測。

Shady 之前曾說過,
當配置的記憶體容量超過 16KB 時,
不管是出自 Linux 本身之函式、別人之函式或 Shady 的,
都將有很高的 tick 值出現。
而此情形有可能大於 4KB 記憶體配置就會出現,
只是 Shady 還未有稍大於 4KB 的測試就是了...XD

現在 Shady 最大的懷疑是" cache 堵塞"為主要原因,
因為不管是出自別人之函式或 Shady 的,
都有不當使用過多和未適時使用 lvxl、stvxl、dcbf等指令,
而這些指令都是將 cache line 標記為"最近最少使用"或"無效化",
所以目前 Shady 正在實驗這些指令的特性和它們在程式中的順序,
以更改配置的記憶體容量超過 16KB 時所造成的大失速。

當然主要的原因可能不在此處,
但目前也只能朝這方向猜測,
因為都已經用組語撰寫了,
甚至還避免使用微碼指令。
所以除了這個方向,
Shady 實在是想不出還會有啥其它的原因...囧rz