🎬 第十二章 ZLMediaKit搭建直播平台
📄 一句话概括

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. 把串行那 1.5 秒去掉,猜加速比会变多少——接近 8 倍,说明瓶颈就在那一段。
  2. 把 200 个单元改成 20 个,猜会怎样——任务太粗,并行收益更差,收尾的尾巴更长。
  3. 把 CMAKE_BUILD_TYPE 改成 Debug,猜编得更快还是更慢——更快,但跑得更慢,优化是拿编译时间换运行时间。

常见误区

  1. 「cmake 成功就说明配置没问题」——源文件缺失要到编译才暴露。
  2. 「-j 越大越快」——受串行部分与内存限制,超过核数就没收益。
  3. 「Debug 版更慢所以没用」——它有调试符号,排障时必需。
自测三问(点开对答案) 1. 为什么必须 `git submodule update --init`? —— **ZLToolKit 是子模块**,`--depth 1` 克隆后目录是空的。 2. `make -j8` 为什么不到 8 倍? —— **cmake 配置与最后链接都是串行的**,Amdahl 定律压住了上限。 3. `-DCMAKE_BUILD_TYPE=Release` 影响什么? —— **只影响优化级别**,不影响功能对不对,但影响跑得快不快。

相关笔记