iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0

本日內容

  • Ninja!
  • 改用 Ninja
  • 預告

Day 26 - Colab

我們今天會沿用 Day 25 的設定, 來看看改用 Ninja 能夠節省多少開發的時間!

Ninja!

Ninja 是一個 build tool, 但是主打 快速
這邊需要聲明一下, 這裡指的 "快速" 是指 從執行 build tool 指令開始到真的開始 build 的時間

由於 build tool 在開始 build targets 之前, 還需要做幾件事情

  1. Dynamic loader 執行 build tool
  2. 爬 build scripts (沒錯, 就是 generator 產生的那些檔案)
    • Make: Makefile
    • Ninja: build.ninja
  3. 建立 dependency graph (通常是 DAG)
    • 用來表示 targets 之間的依賴關係, 決定哪些 targets 需要先 build
  4. Resolve dependency
    • 如果 dependency 改變了, build tool 會知道並幫我們重 build, 就不需要全部再 build 一遍
  5. 產生 dependency file (or depfile)
  6. Start building

所以上面所指的 快速 是從 1 到 5 所需要的時間

道理我都懂, 但是, 為什麼 Ninja 說自己很快呢?

輕量化的 Make

根據 作者所說 (2011), 在他們將 Chrome 從 Windows porting 到 Linux 時, 用 Scons 需要 40s 才會真的開始 build code, 當時的 scale 是 1 個 executable, 30,000 個 source files (想嘗試的話可以用 Day 25 提供的 scripts 模擬~)
然後改動一個檔案後, 在 Windows re-built 就花了 8 分鐘😱

所以作者在分析後決定朝兩個方向優化

  1. 減少兩次 make 需要的時間
  2. 減少從設定到開始 build code 的時間

作者在 strace 後決定把 make 的一些功能拔掉, 加上用比較快的 linker 和 compiler, 最終成功從 8 分鐘壓到剩下 6 秒!

所以可以將 Ninja 看做是輕量版的 Make

壓縮 Dependency Files

除此之外, Ninja 會將 dependency files (deps) 壓縮後存在自己的 db (builddir/.ninja_deps) 中, 加快讀取速度

改用 Ninja

我們直接將 generator 改成 Ninja, 並看看效能有多少提升

cmake -S . -B build -G "Ninja"

可以看到確實比 Make 快了不少, 我們來用 strace 比較一下兩者用了哪些 system call

我們一樣繼續用 Day 25 的設定, 來看看 build debug 版本和 release 版本的效能差異

real user system
make 0m10.708s 0m6.479s 0m3.810s
ninja 0m8.173s 0m7.831s 0m3.901s

加速了 23.6%, 感覺是在誤差範圍內, 怎麼和想象中的效果不一樣呢?

我們再 build 一次試試

real user system
make 0m0.203s 0m0.114s 0m0.087s
ninja 0m0.075s 0m0.053s 0m0.017s

可以發現, 第二次 build 的結果中, Ninja 快了 63%❗❗❗
這是相當顯著的提升了

我們來 strace 一下

第一次

可以看到第一次因為要 parsing build scripts, 建立 dependency graph 等等, wait4 花費的時間相差不大

Make

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.97    4.154351     1038587         4           wait4
  0.01    0.000268           5        47        29 newfstatat
  0.01    0.000216          54         4           clone3
  0.00    0.000140           5        25        20 openat
  0.00    0.000080           6        13           mmap
  0.00    0.000065          13         5           munmap
  0.00    0.000057           6         9           rt_sigaction
  0.00    0.000052          26         2           readlink
  0.00    0.000047           2        21           rt_sigprocmask
  0.00    0.000036           9         4           read
  0.00    0.000035          11         3           mprotect
  0.00    0.000034           6         5         1 access
  0.00    0.000029           4         6           close
  0.00    0.000023          11         2           getdents64
  0.00    0.000018           3         6           ioctl
  0.00    0.000014           2         7           fcntl
  0.00    0.000012           3         4           brk
  0.00    0.000009           2         4           pread64
  0.00    0.000008           2         4           geteuid
  0.00    0.000007           1         4           getuid
  0.00    0.000007           1         4           getegid
  0.00    0.000006           1         4           getgid
  0.00    0.000005           5         1           chdir
  0.00    0.000004           4         1           getcwd
  0.00    0.000004           4         1           getrandom
  0.00    0.000003           1         2         1 arch_prctl
  0.00    0.000002           2         1           set_tid_address
  0.00    0.000002           2         1           set_robust_list
  0.00    0.000002           2         1           prlimit64
  0.00    0.000002           2         1           rseq
  0.00    0.000000           0         1           execve
------ ----------- ----------- --------- --------- ----------------
100.00    4.155538       21094       197        51 total

Ninja

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ------------------
 96.81    3.817308       12195       313           wait4
  0.46    0.018043          10      1665           write
  0.35    0.013789          44       313           clone3
  0.34    0.013244          43       307           unlink
  0.31    0.012116           5      2192           prlimit64
  0.31    0.012109           8      1391       321 stat
  0.20    0.007690           8       938           read
  0.18    0.007202           7       940           close
  0.16    0.006128          19       311           ppoll
  0.14    0.005615          16       338        22 openat
  0.11    0.004463          14       315           munmap
  0.10    0.004069           6       628           ioctl
  0.10    0.003855          12       313           pipe2
  0.10    0.003780           6       630           fcntl
  0.09    0.003626           5       628           rt_sigprocmask
  0.08    0.002960           8       336           mmap
  0.07    0.002627           7       339        20 newfstatat
  0.07    0.002576           8       309           fstat
  0.04    0.001726           5       311           rt_sigpending
  0.00    0.000035           3         9           brk
  0.00    0.000012           3         4           lseek
  0.00    0.000011           1         6           rt_sigaction
  0.00    0.000000           0         7           mprotect
  0.00    0.000000           0         4           pread64
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         2         1 arch_prctl
  0.00    0.000000           0         1           sched_getaffinity
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
  0.00    0.000000           0         1           getrandom
  0.00    0.000000           0         1           rseq
------ ----------- ----------- --------- --------- ------------------
100.00    3.942984         314     12557       365 total

我們改動 main.cpp 的實作, 多呼叫一個 foo49_1() 的 function 後再 build 一次看看

第二次

這次差距就相當明顯了, Ninja wait4 所花費的時間是 0.033s
而 Make 則是 0.082s, 提升了 60% 的速度❗

可見 Ninja 作為輕量化的 Make 確實能處理的更快速

Make

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.082306       20576         4           wait4
  0.00    0.000000           0         4           read
  0.00    0.000000           0         6           close
  0.00    0.000000           0        13           mmap
  0.00    0.000000           0         3           mprotect
  0.00    0.000000           0         5           munmap
  0.00    0.000000           0         4           brk
  0.00    0.000000           0         9           rt_sigaction
  0.00    0.000000           0        21           rt_sigprocmask
  0.00    0.000000           0         6           ioctl
  0.00    0.000000           0         4           pread64
  0.00    0.000000           0         5         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         7           fcntl
  0.00    0.000000           0         1           getcwd
  0.00    0.000000           0         1           chdir
  0.00    0.000000           0         2           readlink
  0.00    0.000000           0         4           getuid
  0.00    0.000000           0         4           getgid
  0.00    0.000000           0         4           geteuid
  0.00    0.000000           0         4           getegid
  0.00    0.000000           0         2         1 arch_prctl
  0.00    0.000000           0         2           getdents64
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0        25        20 openat
  0.00    0.000000           0        47        29 newfstatat
  0.00    0.000000           0         1           set_robust_list
  0.00    0.000000           0         1           prlimit64
  0.00    0.000000           0         1           getrandom
  0.00    0.000000           0         1           rseq
  0.00    0.000000           0         4           clone3
------ ----------- ----------- --------- --------- ----------------
100.00    0.082306         417       197        51 total

Ninja

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ------------------
 87.61    0.032997       16498         2           wait4
  6.27    0.002360           4       515         8 stat
  2.27    0.000856           1       728           lseek
  0.60    0.000227           7        32        20 openat
  0.59    0.000221           7        28           read
  0.50    0.000190           7        25           mmap
  0.31    0.000118          59         2           clone3
  0.30    0.000112           3        35        20 newfstatat
  0.23    0.000086          10         8           write
  0.23    0.000085           6        14           close
  0.14    0.000051           7         7           mprotect
  0.13    0.000049           3        15           prlimit64
  0.11    0.000043          10         4           munmap
  0.10    0.000038          19         2           ppoll
  0.08    0.000032          32         1           unlink
  0.08    0.000032          16         2           pipe2
  0.07    0.000026           4         6           ioctl
  0.06    0.000024           4         6           rt_sigprocmask
  0.06    0.000022           2         9           brk
  0.06    0.000021           2         8           fcntl
  0.05    0.000017           2         6           rt_sigaction
  0.04    0.000014           4         3           fstat
  0.03    0.000011           2         4           pread64
  0.02    0.000009           4         2           rt_sigpending
  0.02    0.000008           8         1         1 access
  0.01    0.000003           3         1           getrandom
  0.01    0.000002           2         1           getpid
  0.01    0.000002           2         1           sched_getaffinity
  0.01    0.000002           2         1           set_tid_address
  0.01    0.000002           2         1           set_robust_list
  0.01    0.000002           2         1           rseq
  0.00    0.000001           0         2         1 arch_prctl
  0.00    0.000000           0         1           execve
------ ----------- ----------- --------- --------- ------------------
100.00    0.037663          25      1474        50 total

可以看到, 我們除了在 configure 階段將 generator 設定為 Ninja 以外, 沒有做任何改動
然而, 單單這樣就提升了 60% 的速度!

預告

Ninja 的介紹就到這裡, 下一篇要來準備 cross compile Windows 的環境啦!


上一篇
[Day 25] 你需要再快一點! Build Performance (二)
下一篇
[Day 27] Cross Compile 行前準備
系列文
30 天 CMake 跨平台之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言