2016年5月5日 星期四

ZRAM 與 Flash 置換空間的效能比較

還記得在剛到公司上班不久,就要處理產品的一個Bug。

這個問題是這樣的,從系統的日誌(Log)上看起來是因為應用程式的動態函式庫在執行期間因為無效的記憶體存取而造成Segment fault,最後從使用者這邊啟動的應用程式就發生閃退了。當時一度懷疑是 Houdini 的問題(這是經驗法則...= =),但是在開發初期到問題發生的中間 Houdini 並沒有版本更新,而且在前者程式卻沒有發生閃退。

所幸,最後是神人同事發現到開發過程中間有引進ZRAM的功能,閃退的原因是因為 ZRAM 的 alignment 問題造成程式對記憶體的超界存取。

問題解決後,資深老鳥要菜逼八本魯解釋什麼是 ZRAM....。

ZRAM是什麼?

ZRAM 是一種置換空間(Swap Space)的機制,但是相較於傳統的 Swap 是將從記憶體將分頁置換到硬碟中,ZRAM 則是將分頁置換到記憶體。但是這下又有問題了....

置換會發生不就是因為實體記憶體空間已經不夠用了,所以才要將分頁搬移到硬碟,那把分頁置換到記憶體不是在耍白痴嗎?

正是因為如此,所以要被置換出的分頁必須要經過LZOLZ4演算法壓縮,才可以置換到記憶體。

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 的速度整體還是比較快。

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 
make install 
完成後可以看到當前目錄下出現 ct-ng 的執行檔

2. 挑選工具鏈版本:

./ct-ng list-samples <- 用來查詢可以選擇的工具鏈版本
 因為 Beaglebone black 上的 SoC (AM3358) 是 arm cortex A8 系列,可以下指令

./ct-ng show-arm-cortex_a8-linux-gnueabi 

顯示工具鏈的詳細資訊


選定好要建置的工具鏈後(arm-cortex A8),輸入

./ct-ng arm-cortex_a8-linux-gnueabi

完成後可以看到當前目錄下出現 .config 隱藏檔,檔案中主要描述工具鏈的詳細功能設定。

3.  開啟硬體浮點功能:

因為 arm-cortex_a8-linux-gnueabi 版本的工具鏈並沒有將硬體浮點運算的功能打開,因此我們要先修改 menuconfig 開啟硬體浮點加速支援,


並修改 .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 倍。

所以當在開發的程式需要進行大量的浮點運算的話,這時如果運行平台上又有支援浮點硬體加速(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