還記得在剛到公司上班不久,就要處理產品的一個Bug。
這個問題是這樣的,從系統的日誌(Log)上看起來是因為應用程式的動態函式庫在執行期間因為無效的記憶體存取而造成Segment fault,最後從使用者這邊啟動的應用程式就發生閃退了。當時一度懷疑是 Houdini 的問題(這是經驗法則...= =),但是在開發初期到問題發生的中間 Houdini 並沒有版本更新,而且在前者程式卻沒有發生閃退。
所幸,最後是神人同事發現到開發過程中間有引進ZRAM的功能,閃退的原因是因為 ZRAM 的 alignment 問題造成程式對記憶體的超界存取。
問題解決後,資深老鳥要菜逼八本魯解釋什麼是 ZRAM....。
ZRAM是什麼?
ZRAM 是一種置換空間(Swap Space)的機制,但是相較於傳統的 Swap 是將從記憶體將分頁置換到硬碟中,ZRAM 則是將分頁置換到記憶體。但是這下又有問題了....
置換會發生不就是因為實體記憶體空間已經不夠用了,所以才要將分頁搬移到硬碟,那把分頁置換到記憶體不是在耍白痴嗎?
正是因為如此,所以要被置換出的分頁必須要經過LZO或LZ4演算法壓縮,才可以置換到記憶體。
ZRAM 於 Linux Kernel 3.14版本正式合併到主線,於3.15版加入LZ4壓縮演算法。
為什麼要ZRAM?
相較於PC, 我們的手機裝置上的記憶體容量多數是比較小的,為了要讓程式有更多的記憶體空間可以使用所以我們需要置換空間。PC可以用硬碟作置換空間,那手機呢?
當然聰明的人一定會想到,用手機的 eMMC 或是 SD 卡不就行了。
但是不幸的是,eMMC 和 SD卡 因為它們的物理特性,容許寫入的次數非常少。一旦讓 eMMC 和 SD 卡承受頻繁的寫入,那麼他們很快就會壞掉了。
所以,在早期的手機(Android KitKat 以前)是沒有置換空間的。也正因為如此,我們無法在手機上使用會大量消耗記憶體的應用程式,或是只能讓記憶體中存在有限的行程。
為了解決這項問題 ZRAM 便應運而生。
ZRAM與 Disk swap 相較的優缺點
優:
1. ZRAM 置換出的存儲媒體是 RAM 且 RAM 寫入的速度遠大於 Disk 或是快閃記憶體的寫入速度
2. 可以避免快閃記憶體的磨耗
缺:
寫入 RAM 之前必須經過壓縮,不像 Disk 或快閃記憶體一樣直接寫入即可
ZRAM與 快閃記憶體 swap 效能比較
在 beaglebone black 上寫一個無限迴圈,迴圈中的每一步都會 malloc 1MB 的記憶體,
一直執行到系統的 memory killer 被叫起來為止。並用 vmstat 每秒紀錄一筆資料。
flash:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 0 479180 1812 3136 0 0 107 4 58 112 0 6 93 1
0 0 0 479180 1812 3156 0 0 0 0 7 8 0 0 100 0
0 0 0 479180 1812 3156 0 0 0 0 39 54 0 1 99 0
0 0 0 479180 1812 3156 0 0 0 0 9 8 0 0 100 0
0 0 0 479180 1812 3156 0 0 0 0 7 10 0 0 100 0
1 0 0 169776 1816 3688 0 0 536 0 276 133 26 71 0 3
3 0 129228 3324 76 680 72 139600 1036 139692 913 869 5 36 0 59
0 2 144788 2628 76 984 24 5232 188 5236 116 123 2 12 0 86
1 1 185984 2752 76 900 0 41196 0 41196 161 162 1 35 0 64
0 2 198552 3360 76 888 0 12568 0 12568 108 132 0 9 0 91
0 2 198552 3496 76 888 0 0 0 0 53 61 0 2 0 98
0 2 198552 4092 76 888 0 0 0 0 89 70 0 5 0 95
0 3 198552 4600 76 888 0 0 0 0 83 73 0 6 0 94
0 3 239436 4620 92 868 0 40884 4 40912 738 965 3 19 0 79
1 2 247716 2648 96 880 0 8280 0 8288 101 113 1 17 0 82
1 1 259520 3476 96 880 0 11800 0 11800 128 140 2 29 0 69
3 0 262136 44508 76 412 0 2632 1148 2632 204 211 0 31 0 69
計算得到平均Swap out 速度每秒為 23830 bytes
ZRAM:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
1 0 0 472580 2364 7732 0 0 31 4 139 46 54 2 44 0
0 0 0 472596 2364 7736 0 0 0 0 9 12 0 0 100 0
0 0 0 472596 2364 7736 0 0 0 0 34 48 0 0 100 0
1 0 0 320532 2368 8268 0 0 536 0 200 132 18 41 40 2
3 0 2184 3212 84 940 132 4020 872 12 285 197 24 76 0 0
2 0 60992 2688 84 1208 0 57072 112 4 232 860 4 96 0 0
2 0 119288 2820 92 1112 0 58300 0 76 256 860 4 96 0 0
2 0 177652 2820 92 1112 0 58364 0 4 207 824 3 97 0 0
2 0 236628 2656 92 1108 0 58972 0 0 201 803 2 98 0 0
計算得到平均Swap out 速度每秒為 47325 bytes
結論:
雖然 ZRAM 要 Swap out 會因為壓縮使用掉一些 CPU 時間,但是因為 RAM 的寫入速度比快閃記憶體要快很多。所以可以看到 ZRAM swap out 的速度整體還是比較快。
魯蛇GG筆記
醒醒吧肥宅,你沒有妹妹!!!
2016年5月5日 星期四
2016年4月28日 星期四
用 Beaglebone black 學 Linux 驅動程式 (一) -- 工具鏈
本篇文章主要以 crosstool-ng 來建置 Beaglebone black 的工具鏈並且挑選接下來實驗所要使用的版本:
1. 下載並建置基本環境:
wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.20.0.tar.bz2
cd crosstool-ng-1.20.0
./configure --enable-local
make
完成後可以看到當前目錄下出現 ct-ng 的執行檔
![](data:image/png;base64,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)
2. 挑選工具鏈版本:
./ct-ng list-samples <- 用來查詢可以選擇的工具鏈版本
./ct-ng show-arm-cortex_a8-linux-gnueabi
顯示工具鏈的詳細資訊
選定好要建置的工具鏈後(arm-cortex A8),輸入
因為 arm-cortex_a8-linux-gnueabi 版本的工具鏈並沒有將硬體浮點運算的功能打開,因此我們要先修改 menuconfig 開啟硬體浮點加速支援,
並修改 .config 來對工具鏈名稱添加hf後綴字串,修改後的版本如下:
4. 建置工具鏈
之後便可以用以下指令開始建置工具鏈
並修改 .config 來對工具鏈名稱添加hf後綴字串,修改後的版本如下:
4. 建置工具鏈
之後便可以用以下指令開始建置工具鏈
./ct-ng build
建置過程大約需要 50 分鐘左右。完成後,可以在家目錄下看到 x-tools 資料夾,就是工具鏈所在的位置。
之後可以使用指令
export PATH=~/x-tools/arm-cortex_a8-linux-gnueabihf/bin:$PATH
把工具鏈所在執行檔位置加入環境變數中,就可以在任意目錄下執行工具鏈了。
浮點運算的 soft softfp 與 hard 差異探討
要解答這個問題就必須要從gcc編譯指令跟一些組合語言談起了。
現在有一個浮點運算的程式 (test.c),程式碼如下:
#include <stdio.h>
float f_add(float a, float b)
{
return a + b;
}
float f_sub(float a, float b)
{
return a - b;
}
float f_mul(float a, float b)
{
return a * b;
}
float f_div(float a, float b)
{
return a / b;
}
int main(int argc, char* argv[])
{
float a = 1.0;
float res;
int run_count, i, j;
if (argc != 2) {
printf("How many loop you would like to run?\n");
return 1;
}
run_count = atoi(argv[1]);
for (i = 0; i < run_count; i++) {
for (j = 0; j < 1000; j++) {
res = f_add(a, 2.5);
res = f_sub(res, 1.5);
res = f_mul(res, 2.5);
res = f_div(res, 5.0);
}
}
return 0;
}
我們用剛做好的交叉編譯器(工具鏈)編譯出三個不一樣的執行檔:
arm-cortex_a8-linux-gnueabi-gcc -static test.c -o soft (沒有浮點加速)
arm-cortex_a8-linux-gnueabi-gcc -static -mfloat-abi=softfp -mfpu=vfp test.c -o softfp
(softfp 型浮點加速)
arm-cortex_a8-linux-gnueabihf-gcc -static -mfloat-abi=hard -mfpu=vfp test.c -o hard
(hard 型浮點加速)
*註:由於 soft, softfp 在當開啟hardware FPU選項後便無法使用,因此本次實驗建置了關閉hardware FPU (arm-cortex_a8-linux-gnueabi) 以及開啟 hardware FPU (arm-cortex_a8-linux-gnueabihf) 的兩種工具鏈。
於是我們將分別將不同的執行檔反組譯
arm-cortex_a8-linux-gnueabi-objdump -S soft > soft.S
arm-cortex_a8-linux-gnueabi-objdump -S softfp > softfp.S
arm-cortex_a8-linux-gnueabi-objdump -S hard > hard.S
1. soft 版本與 softfp 比較
左邊的部份是 f_add 函式在 soft 版本的組合語言,可以看到在這版本中浮點加法是透過__addsf3 函式實作出來,__addsf3 裡面是利用定點指令來完成浮點加法,因此函式中也包含了許多指令。右邊的部份則是 softfp 版本的組合語言,可以看到浮點加法是透過 vadd.f32的指令完成,因此可以預期 softfp 版本會有比較好的效能。
2. softfp 版本與 hard 比較
左邊的部份是 f_add 函式在 soft 版本的組合語言,右邊是 hard 版本的組合語言。
softfp 版本與 hard 版本最大的差異在於當 f_add 函式回傳時,hard 可以利用浮點暫存器 s0回傳函式執行結果,而 softfp 在函式回傳前必須將結果存到暫存器 r0,因此相較於 softfp 版本 hard 版本可以少掉一個 mov 指令。
在 Vexpress-A9 模擬器實驗
可以看到使用支援硬體浮點加速(綠線&藍線)的部份,在 10 萬個迴圈下程式執行的效率快了將近 1.6 倍。
2. softfp 版本與 hard 比較
左邊的部份是 f_add 函式在 soft 版本的組合語言,右邊是 hard 版本的組合語言。
softfp 版本與 hard 版本最大的差異在於當 f_add 函式回傳時,hard 可以利用浮點暫存器 s0回傳函式執行結果,而 softfp 在函式回傳前必須將結果存到暫存器 r0,因此相較於 softfp 版本 hard 版本可以少掉一個 mov 指令。
在 Vexpress-A9 模擬器實驗
可以看到使用支援硬體浮點加速(綠線&藍線)的部份,在 10 萬個迴圈下程式執行的效率快了將近 1.6 倍。
所以當在開發的程式需要進行大量的浮點運算的話,這時如果運行平台上又有支援浮點硬體加速(FPU),可以在編譯程式的時候稍微留意一下,往往會有不錯的效果。
使用硬體浮點就一定會加速?
先前預期在使用硬體浮點的狀況下,執行效率會提昇的理由是基於程式的指令數比較少。但事實上這樣的假設是有問題的,因為每個單一指令的週期數不一定相同。所以一但有某個平台硬體浮點運算的指令週期大於使用定點指令作浮點運算的指令集合,那麼我們並沒有辦法在使用硬體浮點處理的狀況下達到效能提昇。很不幸的,這樣的狀況正好發生在 beaglebone black 的實驗平台上,同樣的實驗結果如下:
可以看到在 beaglebone black 上 hard 版本跟 softfp 版本的執行效能反而比 soft 版本還要差。盡信書不如無書,硬體浮點指令是否一定等於高效率在這兩次的實驗上剛好得到了解答。
因此,之後的實驗我們都會使用 arm-cortex_a8-linux-gnueabi 的工具鏈編譯程式。
參考資料:
1. 关于armhf (hard-float ABI for ARM)
http://www.claudxiao.net/2012/02/armhf/
2. Mastering Embedded Linux Programming, ISBN: 978-1-78439-253-6, p.13-29
https://it-ebooks24.com/ebook/mastering-embedded-linux-programming
訂閱:
文章 (Atom)