2010年6月18日 星期五

在NUC501上測定程式效能

前次修改了ROM虛擬記憶體的演算法之後,雖然可以感覺到程式執行的速度有所提昇,但目測可能會隨著時間、身心狀態的不同,而使得結果誤差很大,所以著實需要能客觀測定程式改進幅度的機制,以免陷入程式愈改愈遭的命運!

使用ICE的人,可能早就使用profile取得程式的效率報告了,不過在開發環境是台拼裝車,加上程式執行的方式被改到亂糟糟的情況下,即使有ICE恐怕也無法正確取得所要的效能資料,另外我們也需要一個能讓程式delay正確的時間,而不受處理器速度影響的方法。

以前使用8-bit微處理器的時候,因為timer精度的關係,這類的需求通常是撰寫timer中斷,設定想要的中斷時間間隔,然後在每次中斷時將計數值加一,雖然可用,但當需要測定的時間精度很高時,恐怕沒有多少處理器可以承受這麼高速的中斷。

在NUC501上,timer是屬於AIC的管轄範圍,這部份因個人因素(哈),暫時還不想去處理,也不希望因使用中斷,讓目前還不知道效能問題出在哪的情況雪上加霜。另外,目前的開發流程主要是先在PC上進行,等階段功能完成之後,才移植到NUC501上去測試,使用中斷會使程式的移植性降低。

在PC上,可以透過SDL系統的SDL_GetTicks()函式(對應於Windows的GetTickCount())取得32-bit的系統Ticks值,時間精度為1ms,最長可以測到49.71天,所以如果程式跑得很慢,49.71天也真夠測得了(笑)。而在NUC501上,也裝備二組32-bit的timer可以提供類似的功能,但時間精度更高,如果使用「NUC501 IP Programming Guide」文件內的建議值,可以得到1us的Tick值,不過這麼一來最長測定時間只有4294.97秒(約71.58分鐘)。

不過為了與電腦的互換性,還是得忍痛降低精確度,方法是將Tick值除以1000就可以了。但因為ARM7TDMI沒有除法指令,使用除法會使得系統呼叫專門處理除法的函式庫,反而拖慢執行速度,所以希望可以簡單用shift的方式來達成。

以目前系統所使用的12MHz Crystal來看,利用TCSRx暫存器的PRESCALE,可以將Crystal除頻到接近64000Hz上,這樣既可以用shift處理又可延長量測時間,雖然有一點偏差,但還可接受,最長的量測時間就爆增到18.64小時了呢!

有了時間測定的功能之後,就可以測程式的效能了,由於RAM虛擬記憶體的效能不佳,所以先來測試SD卡的讀取效率吧!

測試的條件如下:

  1. 測試的時間為1秒。
  2. 以ROM虛擬記憶體的方式執行,在測試時間內重複呼叫DrvSDCard_Read函式,測試時每次讀取1 sector,每讀取一次將計數器加一。
  3. 時間到時將計數器的值除以2即可得到每秒讀取速度。

測試完畢後,得到DrvSDCard_Read函式至少有516K/sec的傳輸速率。雖然一次讀取多個sector可能會得到更好的結果,但為了節省記憶體,APSLib內的快取只有一個sector的長度,所以單測一個sector才能知道底層運作的效能如何,不過有這樣的速率應該不會拖慢RAM虛擬記憶體太多呀~~接下來就來測試ASPLib模擬的fread,同樣測試條件如下:

  1. 測試的時間為1秒。
  2. 以ROM虛擬記憶體的方式執行,在測試時間內重複呼叫fread函式,測試時每次讀取512 bytes,每讀取一次將計數器加一。
  3. 時間到時將計數器的值除以2即可得到每秒讀取速度。
最後得到令人難堪的12K/sec!天啊!難怪RAM虛擬記憶體的效能如此低落!看來問題有得找了。


後記:

為了得知效能增進的幅度,另外也重做了第一項測試,但改為不使用ROM虛擬記憶體,而將程式完全放在SpiROM下執行,看看虛擬記憶體這東西到底有沒有效果!值得欣慰的是,得到了38K/sec的執行結果,幸好這大半年東搞西搞的還是有代價的(泣),不然就要被我自己毆飛了。

2010年6月2日 星期三

ROM虛擬記憶體改進篇

開始新版GPS軌跡記錄器專案也快半年了,由於該專案並不是最高優先,所以時常有其他專案插隊,再加上大金一直沒有交出試作電路板,簡直快變成閒暇之餘的興趣案了,也因為這樣,才有機會在這上面嘗試新的想法,像之前實作的ROM/RAM虛擬記憶體便是很好的例子。

在專案沈寂許久之後,最近大金突然塞給我他弄好的試作電路板,要我測試看看,剛好可以測試之前寫的繪圖程式庫是否能動。在沒有試作板以前,都是在PC上以假想硬體的方式來寫繪圖程式,要驗證繪圖結果的時候,就用SDL寫的小程式來模擬LCD的顯示。一來可以讓PC與裝置的基本程式碼相同,能很快的將程式在二個平台移植來移植去,二來也可以透過PC端進行程式除錯,減低裝置端除錯的困難。

繪圖系統除了具備貼圖與遮罩處理以外,還具備多國語言顯示的能力。原先的規劃,是將龐大的字型資料放在SpiROM上,但大金希望程式可以選擇把字型資料放在SD卡或SpiROM,這樣在客戶想降低成本時,只要把字型資料改放在SD卡上,就可以用較小容量的SpiROM壓低價格,所以大金在測試板上的SpiROM只給了8Mbit,將來有機會產品化的話,甚至是4Mbit都在考慮之列。

為了達成這個要求,又不用寫太多程式碼,只好把腦筋放在虛擬記憶體上。首先先把字型資料填入RAM虛擬記憶體的交換檔中,以透過該系統存取字型資料。當RAM虛擬記憶體在收到換頁需求時會判斷來源,以決定要不要進行換頁,再加上RAM換頁之後,程式可以直接在正確的記憶體上取得字型資料,所以當字型資料可以存放在SpiROM時,對字型系統來說,即使加入了虛擬記憶體的操作指令也沒關係,幾乎不用修改就可以達成二對應了!所以將字型系統架在RAM虛擬記憶體之上可以省去很多麻煩。

就在把PC端驗證過的程式碼一股腦移植到NUC501上後,執行結果暴慢!顯示一小段文字竟然要8秒鐘以上!這樣的結果令人不能接受!暴慢的原因,除了RAM虛擬記憶體極度依賴的檔案系統需要改進以外,另一個問題是直接影響執行效率的ROM虛擬記憶體處理方式。由於檔案系統牽涉到很多程式碼的變動,所以先拿ROM虛擬記憶體開刀好了,看效能會不會好些。

原先在處理ROM虛擬記憶體時,採用腦殘的方式,以中斷時的PC值除以2048,用得到的商值來決定要對哪個頁面暫存器做修改。雖然簡單,但可用頁面數量少的時候,常發生目前使用的頁面被新要求的頁面給置換掉,結果消失的頁面之後又必須再進入例外置換回來的慘劇,嚴重點CPU就永遠在處理頁面置換,無法執行程式了!也因此原先Prefetch Abort與Data Abort應共享頁面的情況,被硬生生的拆散成程式頁面+資料頁面來解決這個死結問題。

可是,Prefetch Abort與Data Abort各自為政,還會產生一個潛在的問題,也就是當程式使用ldm指令,發生跨頁面資料讀取時,Data Abort會決定是否將二個頁面同時mapping出來,但只檢查自己轄區內的頁面暫存器是否已經有重複的位址,而沒去檢查Prefetch Abort是否有相同的頁面。所以當Data Abort產生的頁面與Prefetch Abort的頁面重複時,會浪費一個頁面放同樣的東西,這種情況根本不應該發生。

所以解決之道,還是需要一個較佳的演算法,來解決頁面置換的問題,並重新結合程式與資料這二個分離的頁面區。由於ARM7TDMI缺乏記憶體管理旗標,無法得知那個頁面最近被執行或修改過,因此,雖然Google可以找到很多虛擬記憶體演算法,能實作的也只有頁面先進先出的方式比較合邏輯,不過可以加一些改進。

程式預取例外作法如下:

  1. 為每個頁面設立一個衰老計數器。程式啟動時將所有的衰老計數器設為最老。
  2. 進入程式預取例外後,先找出計數器中最老的頁面,將其更新至新的頁面後,重設該頁面的計數器為最年輕的頁面。
  3. 將其他頁面的衰老值加一。

由於程式預取例外發生時一定是該程式碼頁面不存在才會發生,所以不用管是否與資料例外衝突,不過資料例外因為ldm/stm指令的關係,必須考慮頁面重複的問題,其作法如下:

  1. 與程式預取例外相同,先找出最老的頁面出來。
  2. 檢查發生資料例外的程式碼是否是ldm/stm,如果是,則檢查起始位址與結束位址是否在相同的頁面。
  3. 如果頁面相同,則進行步驟5。
  4. 如果不同,則檢查頁面暫存器中是否已有起始位址的頁面,如果有,則將該頁面設為最年輕,並將其他頁面的衰老值加一。如果沒有,則將之前找出來的最老頁面置換成新的頁面,將此頁面設為最年輕,並把其他頁面衰老值加一。
  5. 最後處理結束位址,與處理起始頁面相同,也是將頁面塗上SK-II變年輕,沒用的讓它變衰老,處理完後就可以結束例外處理。若之前找出的最老頁面已經被起始位址給用掉了,則需先重新找出最老頁面,才進行處理。
  6. 其他非ldm/stm的指令因為只會產生一個頁面位址,所以直接以步驟5處理。

不過Data Abort除了告訴我們資料需求的頁面位址以外,還有另一個隱藏的位址,就是堆疊裡存放著中斷前的PC值!那麼我們可以確定,PC所指的位址絕對是待會要執行的部份,不應被換掉!所以我們可以在Data Abort執行時,先幫PC所在的頁面擦上SK-II讓它變年輕,讓其他頁面衰老後,才去處理Data Abort接下來的事務,這樣可降低PC所在頁面可能是最老,而慘遭Data Abort處理換掉的機率。

將Prefetch Abort與Data Abort修改之後,除了確實解決之前產生的死結問題以外,Page Fault的次數也大幅減少,目測結果程式至少能快上二倍左右,甚至更快。但就跟任何一種演算法一樣,以上的方法,也會遇到最差的情況,不過整體而言,已經比之前的腦殘處理方式要有效率的多。

雖然速度已經快很多,但...還是得要加速檔案系統才行呢~~