eglibc是 Debian/Ubuntu 提供C語言程式動態連結時使用的函式庫,系統中使用C語言所撰寫的程式,本篇文章將討論如何分析 eglibc 的執行狀況與瓶頸,選出有優化潛力的函式,並如何去優化這些函式,最後再展示我們優化後的數據成果。
分析數據
這裡是用 libc-bench 跑 -pg 之後的結果。
使用的是 eglibc 的 profiling 版本,為 libc6-prof_2.17-0ubuntu5_armhf.deb
實驗所使用的程式碼與編譯方式在 eglibc/libc-bench/gprof_src 底下
原始數據在 gprof_src/analysis.txt 和 gprof_src/output.png
從實驗數據來看,依序應該 tune 這些 function
name | Self seconds | % time | cumulative seconds | calls | self calls | total s/calls | lang |
read | 0.35 | 6.68 | 0.35 | 21708 | 0.00 | 0.00 | system call |
_IO_vfscanf_internal | 0.33 | 6.30 | 0.68 | 1635489 | 0.00 | 0.00 | |
putc | 0.31 | 5.92 | 0.99 | 5000000 | 0.00 | 0.00 | |
clone | 0.31 | 5.92 | 1.30 | 7499 | 0.00 | 0.00 | asm |
gconv_transform_utf8_internal | 0.27 | 5.15 | 1.57 | 3175867 | 0.00 | 0.00 | |
gnu_mcount_nc | 0.26 | 4.96 | 1.83 | 0 | 0.00 | 0.00 | |
mbrtowc | 0.24 | 4.58 | 2.07 | 3175817 | 0.00 | 0.00 | |
getc | 0.23 | 4.39 | 2.30 | 5000000 | 0.00 | 0.00 | |
strstr | 0.20 | 3.82 | 2.50 | 68414 | 0.00 | 0.00 | |
b_stdio_putcgetc | 0.20 | 3.82 | 2.70 | 1 | 0.20 | 0.78 | |
b_stdio_putcgetc_unlocked | 0.19 | 3.63 | 2.89 | 1 | 0.19 | 0.29 | |
memchr | 0.14 | 2.67 | 3.03 | 2269541 | 0.00 | 0.00 | asm |
libc_do_syscall | 0.13 | 2.48 | 3.16 | 0 | 0.00 | 0.00 | asm |
munmap | 0.12 | 2.29 | 3.28 | 2286 | 0.00 | 0.00 | system call |
re_search_internal | 0.12 | 2.29 | 3.40 | 2 | 0.06 | 0.08 | |
pthread_mutex_lock | 0.11 | 2.10 | 3.51 | 1381473 | 0.00 | 0.00 | |
index | 0.11 | 2.10 | 3.62 | 91302 | 0.00 | 0.00 | builtin |
_int_malloc | 0.10 | 1.91 | 3.72 | 230096 | 0.00 | 0.00 | |
_IO_old_init | 0.09 | 1.72 | 3.81 | 1635521 | 0.00 | 0.00 | |
pthread_create | 0.08 | 1.53 | 3.89 | 7502 | 0.00 | 0.00 | |
mprotect | 0.08 | 1.53 | 3.97 | 4753 | 0.00 | 0.00 | system call |
Laligned | 0.08 | 1.53 | 4.05 | 0 | 0.00 | 0.00 | |
rawmemchr | 0.07 | 1.34 | 4.12 | 1635499 | 0.00 | 0.00 | |
_IO_str_init_static_internal | 0.07 | 1.34 | 4.19 | 1635491 | 0.00 | 0.00 | |
isoc99_vsscanf | 0.07 | 1.34 | 4.26 | 1635489 | 0.00 | 0.00 | |
write | 0.07 | 1.34 | 4.33 | 2496 | 0.00 | 0.00 | system call |
_dl_mcount_wrapper_check | 0.06 | 1.15 | 4.39 | 3175867 | 0.00 | 0.00 | |
memcpy | 0.06 | 1.15 | 4.45 | 758019 | 0.00 | 0.00 | |
b_utf8_onebyone | 0.06 | 1.15 | 4.51 | 1 | 0.06 | 0.63 | |
_IO_sputbackc | 0.05 | 0.95 | 4.56 | 3022065 | 0.00 | 0.00 | |
_IO_setb | 0.05 | 0.95 | 4.61 | 1635552 | 0.00 | 0.00 |
實驗數據
可靠度測試
實驗環境
- Odroid-U2
名詞定義
- Baseline
baseline 是用 dpkg-buildpackage 編出來的 libc6-dev_2.17-0ubuntu5_armhf.deb,裡面才有我們所需要的 libc6.a
dpkg -i ./libc6-dev_2.17-0ubuntu5_armhf.deb 安裝 package -
Native
native 是利用 apt-get update 取得最新的 libc6-dev
-
musl
musl 係利用 musl-1.0.3 (http://www.musl-libc.org/releases/musl-1.0.3.tar.gz) 所取得
數據與分析
Baseline vs. Native
最大的差距是 b_malloc_bubble 有 8.51%,第二大是 b_stdio_putcgetc_unlocked 有 3.58%,第三大是 b_pthread_create_serial1 有 2.45%
平均差距為 0.56%,只有千分之五強。
Baseline vs. musl
最大的差距是 b_pthread_uselesslock 有 4.86%,第二大是 b_malloc_big2 有 2.34%,第三大是 b_malloc_big1 有 2.24%
平均差距為 1.04%。
優化方法
修改eglibc部分演算法
實驗環境
比較使用native的libc和改過strstr的eglibc來跑libc-bench的結果。因為只改strstr的結果,故僅挑出string相關的benchmark來做比較。之前實驗取樣 500 次的結果相當穩定,平均標準差百分比都在1%以下,故本實驗只取樣 10 次。
名詞定義
- Native
native 是利用 apt-get update 取得最新的 libc6-dev
-
工人智慧
人工修改eglibc部分演算法後的版本
可靠度分析
Native
在誤差方面相當小,實驗的可靠度高
- 最大的差距是 b_string_memset 有 4.34%
- 第二大是 b_string_strchr 有 0.69%
- 第三大是 b_string_strstr(“abcdefghijklmnopqrstuvwxyz”) 有 0.51%
- 平均差距為 0.89%。
工人智慧
在誤差方面相當小,實驗的可靠度高
- 最大的差距是 b_string_strstr(“abcdefghijklmnopqrstuvwxyz”) 有 1.41%
- 第二大是 b_string_strstr(“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac”) 有 1.16%
- 第三大是 b_string_strstr(“aaaaaaaaaaaaaaaaaaaaaaaaac”) 有 0.67%
- 平均差距為 0.55%。
優化改善比較
可以看到優化後,有關strstr的都變好,在b_string_strstr(“aaaaaaaaaaaaaacccccccccccc”)的例子中甚至有2倍多的改善, b_string_strstr(“abcdefghijklmnopqrstuvwxyz”)和b_string_strstr(“azbycxdwevfugthsirjqkplomn”)的例子也有1倍多的改善。
觀察到的另一點是,把musl的strstr實做直接搬進eglibc當中所得到的結果,都比本來直接換musl-gcc可以得到更好的改善。
在最長字串的部分,本來直接用musl和直接用baseline的狀況時,musl的表現是比較差的,但是可能是因為eglibc編譯選項有調教過的關係,也有9%的改善。
Linker Optimization Option
- 開啟下列三個 option,看是否可以增強效能
- –sort-common
- –sort-sections=alignment
- –reduce-memory-overheads
實驗結果
- –sort-common 無效,因為 glibc 沒有 common symbols
- –sort-aligment 無效,segment 與 section 順序都沒有改變,alignment 還是亂跳
- –reduce-memory-overheads 有點用,可以觀察到的結果如下
- header 變小
- debug 和 string table 變小
Code Size
對造組 | 實驗組 | 縮小 | Improvement |
9017580 | 9017052 | 528 bytes | 0.005855 % |
單位是 byte,在 code size 上有改善,但是改善幅度幾乎看不見