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的次數也大幅減少,目測結果程式至少能快上二倍左右,甚至更快。但就跟任何一種演算法一樣,以上的方法,也會遇到最差的情況,不過整體而言,已經比之前的腦殘處理方式要有效率的多。

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


沒有留言:

張貼留言