Linux 上不需要 VS,但要多装两个开发库。 make -j 的收益有上限,原因不在 CPU。
12.1 在 Windows 上编,这一节换 Linux。 12.3 开始读它自带的三份示例代码。
一、Linux 上少一样、多两样
少的是 VS:Linux 用系统的 g++,不需要额外装 IDE。
多的是两个开发库:libssl-dev 与 libsrtp2-dev。
完整流程是这样:
apt install -y cmake g++ make libssl-dev libsrtp2-dev
git clone --depth 1 https://github.com/ZLMediaKit/ZLMediaKit.git
cd ZLMediaKit && git submodule update --init
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4
-DCMAKE_BUILD_TYPE=Release 这一条别省:
不写就是无优化构建,编出来的服务器性能差一大截,
而且体积更大。它不影响功能对不对,只影响跑得快不快。
二、git submodule 那条最容易跳
ZLMediaKit 依赖 ZLToolKit,它是作为 git 子模块引的。
--depth 1 克隆只拿主仓库,子模块目录是空的。
所以必须补一条 git submodule update --init。
跳过的现象很典型:cmake 那一关能过,make 编到一半突然报
「找不到 ZLToolKit/Util/logger.h」。
看到这个报错,九成是子模块没拉,而不是头文件路径配错了。
为什么它报得这么晚? 因为 cmake 只检查「有没有 CMakeLists」,不检查里面的源文件在不在。 源文件缺失要到真正编译那个目标时才暴露。 这也是为什么「cmake 成功」不能当成「配置没问题」的证据。
三、make -j 的收益上限
make -j 填几,一般填核数。但很多人发现:
从 -j4 加到 -j8,时间掉得很少;再往上加甚至变慢。
原因不在 CPU,在「串行部分」:
cmake 配置只能一个人干,最后链接也只能一个人干,
这两段不管你开多少核都不会缩。
这就是 Amdahl 定律:加速比的上限由串行占比决定。
按这份模型算:串行 1.5 秒、并行 7.5 秒,
8 个工人只快 3.7 倍,核数无限多也只到 6.1 倍。
所以「加核就能线性变快」在编译这件事上从来不成立。
真机上还要加一条:每个 g++ 吃三五百兆内存,
内存不够时 -j8 会因为换页反而比 -j4 慢。
四、动手 Demo:Linux 编 ZLMediaKit——make -j 的收益上限
先查核数与工具,再做一个「串行加并行」的编译模型, 对比实测加速比与 Amdahl 上限。
def compile_all(durs, nworker, before=800, after=700):
"""先串行配置,再并行编译,最后串行链接。"""
time.sleep(before / 1000.0) # cmake 配置,只能一个人干
left = list(durs)
lock = threading.Lock()
def worker():
while True:
with lock:
if not left:
return
d = left.pop()
time.sleep(d / 1000.0) # 编一个源文件
ths = [threading.Thread(target=worker) for _ in range(nworker)]
for t in ths: t.start()
for t in ths: t.join()
time.sleep(after / 1000.0) # 链接,也只能一个人干
durs = [task_len(i) for i in range(200)] # 8~68 毫秒不等
for nw in (1, 2, 4, 8):
print(nw, compile_all(durs, nw))
实跑输出:
--------------------------------------------------------------------
Linux 编 ZLMediaKit——`make -j` 的收益上限
--------------------------------------------------------------------
**① 依赖比 Windows 少一样,多两样**
**Linux 上不需要 VS,但要多装几个开发库:**
apt install -y cmake g++ make
apt install -y libssl-dev
apt install -y libsrtp2-dev
git clone --depth 1 https://github.com/ZLMediaKit/ZLMediaKit.git
cd ZLMediaKit && git submodule update --init
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4
**`git submodule update --init` 这一条最容易被跳过:**
**ZLMediaKit 依赖 ZLToolKit,它是作为子模块引的,不初始化就编不过。**
**报错长这样:找不到 `ZLToolKit/Util/logger.h`。**
**② 真查本机的核数与工具**
**`make -j` 填几,先看核数。**
项目 实测值 说明
------------------------------------------------
逻辑核数 8 make -j 一般填这个数
cmake 没有 得先装
g++ 没有 得先装
make 没有 得先装
**这台机器 8 个逻辑核,但编译器一个都没有,所以下面只能做模型实验。**
**真机上还有一条经验:每个 g++ 进程大概吃三百到五百兆内存,**
**8 核并行就要三四个 G,内存不够时 `-j8` 反而比 `-j4` 慢。**
**③ 真测:并行编译为什么到不了 8 倍**
**编译不是「一堆一样长的任务」,而是「长短不一的几百个任务」。**
**这里造 200 个 8 到 68 毫秒不等的编译单元,用 1/2/4/8 个工人去抢。**
**但光有并行部分不够贴近真实:`cmake` 配置和最后链接都只能一个人干。**
**所以模型里再串行加上前 800 毫秒与后 700 毫秒两段。**
工人数 墙钟毫秒 相对 1 个 Amdahl 上限
-----------------------------------------------------
1 9194 1.00 倍 1.00 倍
2 5357 1.72 倍 1.72 倍
4 3443 2.67 倍 2.69 倍
8 2488 3.70 倍 3.73 倍
**单线程要 9194 毫秒,8 个工人做到 2488 毫秒,只快 3.7 倍。**
**而它离「理想 8 倍」差得远,原因就写在这一列 Amdahl 上限里:**
**串行那 1500 毫秒谁也帮不上忙,它把天花板压住了。**
**这就是 Amdahl 定律的实物版:**
**加速比的上限等于 1 除以「串行占比」。**
**这里串行占 16%,所以核数无限多也只能到 6.1 倍。**
**实测值与这一列几乎完全吻合,说明这份模型的成分是对的:**
**并行部分接近线性,被串行部分一刀切住。**
**所以 `make -j8` 的收益不是「8 倍」,而是「理想 8 倍再打个折」。**
**这也解释了两个常见现象:**
**① 从 `-j4` 加到 `-j8`,时间掉得很少**——**串行那段不会缩。**
**② 再往上加甚至变慢**——**多出来的进程只是在抢锁、抢内存带宽。**
**所以 `-j` 填到核数就够了,再往上没有收益。**
单线程 9206 毫秒,8 个工人 2489 毫秒,只快 3.7 倍。
Amdahl 上限那一列与实测几乎完全重合,说明模型成分对了。
串行占 16%,所以核数无限多也只能到 6.1 倍。
-j 填到核数就够,再往上只是抢锁、抢内存带宽。
30 秒实验
- 把串行那 1.5 秒去掉,猜加速比会变多少——接近 8 倍,说明瓶颈就在那一段。
- 把 200 个单元改成 20 个,猜会怎样——任务太粗,并行收益更差,收尾的尾巴更长。
- 把
CMAKE_BUILD_TYPE改成 Debug,猜编得更快还是更慢——更快,但跑得更慢,优化是拿编译时间换运行时间。
常见误区
- 「cmake 成功就说明配置没问题」——源文件缺失要到编译才暴露。
- 「
-j越大越快」——受串行部分与内存限制,超过核数就没收益。 - 「Debug 版更慢所以没用」——它有调试符号,排障时必需。
自测三问(点开对答案)
1. 为什么必须 `git submodule update --init`? —— **ZLToolKit 是子模块**,`--depth 1` 克隆后目录是空的。 2. `make -j8` 为什么不到 8 倍? —— **cmake 配置与最后链接都是串行的**,Amdahl 定律压住了上限。 3. `-DCMAKE_BUILD_TYPE=Release` 影响什么? —— **只影响优化级别**,不影响功能对不对,但影响跑得快不快。- ☐ (本节完,下一节 12.3.1 test_httpApi.cpp 文件)