qinghuan
← 回到知识探索

计算机趣闻 / 动手实验

CPU 核心翻倍,程序耗时为什么不一定减半?

给同一份工作分出能一起做与必须按顺序做的部分,再看看加核心究竟能省掉哪一段时间。

展开本文目录
  1. 先动手看看
  2. 先固定工作量,再决定哪一段可以一起做
  3. 加速的只是可分工部分
  4. 用程序算假设,不把算式装成跑分
  5. 真程序里,还要找到等待和资源限制
  6. 记住这三点
  7. 别把例子用过头
  8. 自己试一小步
  9. 查原始资料
  10. 找一份资料继续练
开始前,会这些就够了
  • 理解比例和简单除法;可选代码使用 Python 3.10 或更新版本
遇到不熟悉的词,先看这 1 个解释
串行比例
同一份工作在一个核心上执行时,有多少耗时属于必须按顺序完成的部分。剩下的工作若能均分给多个核心,才有机会一起做。
打开完整术语小抄 ↗

四个人搬书,八个人接着搬,听上去应该只用一半时间。但如果每次开库房、核对唯一的清单都得按顺序完成,加人只能分担搬运,不能把所有步骤同时缩短。

程序也可能有这样的分工。我们先不比较任何真实 CPU,只给一份固定任务画时间账:单核原本用一百秒,其中哪些时间可以分给更多核心?

多找几个帮手,哪部分仍得排队做?

固定同一份工作:一个核心原本用 100 秒。假设各核心能力相同,可并行部分能均分给它们,暂时不计通信、调度和等待开销。串行比例按原来的 100 秒计算。

理想用时:40.00 秒,是原来的 2.50 倍速度。

20 + 80 ÷ 4 = 40.00 秒

继续增加核心时,这份模型的加速比上限约为 5.00 倍;这是上限,并不表示当前已经达到。

每条横条都用原来的 100 秒作同一把尺:蓝色是必须顺序完成的工作,绿色是分摊后的可并行工作。下面也列出数字,不必只靠颜色判断。

  1. 1 个核心100.00 秒

    串行 20.00 秒 + 并行 80.00 秒

  2. 2 个核心60.00 秒

    串行 20.00 秒 + 并行 40.00 秒

  3. 4 个核心 · 当前40.00 秒

    串行 20.00 秒 + 并行 20.00 秒

  4. 8 个核心30.00 秒

    串行 20.00 秒 + 并行 10.00 秒

  5. 16 个核心25.00 秒

    串行 20.00 秒 + 并行 5.00 秒

  6. 32 个核心22.50 秒

    串行 20.00 秒 + 并行 2.50 秒

这里只算固定工作量的理想加速上界,没有测过任何 CPU。任务规模改变、内存带宽不足或通信开销增加,都可能让真实结果与这里不同;核心数也不等于可以直接照搬的线程数设置。

先固定工作量,再决定哪一段可以一起做

假设这份工作在一个核心上,必须按顺序完成的部分用二十秒,其余八十秒可以平均分给多个核心。二十秒占原来总耗时的 20%,这个比例叫串行比例。它是时间占比,不是“20% 的代码行”。

这里还做了很强的假设:每个核心能力相同,分工完全均匀,没有安排任务、传递数据或互相等待的额外时间。核心数 N 指能实际参与这份工作的相同计算资源,不能直接把屏幕上显示的逻辑线程数量当成同等新增能力。

加速的只是可分工部分

四个核心下,八十秒的可分工部分理想地缩为二十秒,原来的二十秒串行工作仍在,总共四十秒。八个核心下,两部分合起来是三十秒。所以从四核加到八核,这次并没有把四十秒减为二十秒。

把单核时间记作 T1,串行比例记作 s,就得到 T(N) = T1 × [s + (1-s)/N]。加速倍数是 T1/T(N),比较的始终是同一份工作。这个简单的时间分账就是 Amdahl 定律的常见用法。

用程序算假设,不把算式装成跑分

代码只计算上面的公式,不启动多个工作线程,也不测机器耗时。Fraction 用分数保存 1/5,最后打印时才保留三位小数。cores 是模型输入,改成十六不会让电脑真的使用十六个核心。

当串行比例大于零,增加核心只能让可分工部分越来越接近零,总时间仍被串行部分挡住。在本模型中,核心数趋于无限时,加速倍数趋于 1/s;有限核心一般还达不到这个极限。

小实验 · python
from fractions import Fraction

single_core_seconds = Fraction(100)
serial_fraction = Fraction(1, 5)

def model_seconds(total, serial, cores):
    if total <= 0 or not 0 <= serial <= 1:
        raise ValueError("positive total and serial fraction in [0, 1] required")
    if type(cores) is not int or cores < 1:
        raise ValueError("cores must be a positive integer")
    return total * (serial + (1 - serial) / cores)

for cores in [1, 2, 4, 8, 16]:
    seconds = model_seconds(single_core_seconds, serial_fraction, cores)
    speedup = single_core_seconds / seconds
    print(f"cores={cores:2d} time={float(seconds):7.3f} s speedup={float(speedup):.3f}x")

if serial_fraction > 0:
    print(f"infinite-core limit={float(1 / serial_fraction):.3f}x")
else:
    print("no finite serial-part limit in this model")

怎样运行:保存为 amdahl_model.py,运行 python amdahl_model.py。使用 Python 3.10 或更新版本,仅用标准库;它计算虚构时间,不是 CPU 性能测试。

运行后对照结果
cores= 1 time=100.000 s speedup=1.000x
cores= 2 time= 60.000 s speedup=1.667x
cores= 4 time= 40.000 s speedup=2.500x
cores= 8 time= 30.000 s speedup=3.333x
cores=16 time= 25.000 s speedup=4.000x
infinite-core limit=5.000x

真程序里,还要找到等待和资源限制

真实程序可能要创建任务、合并结果,还会争用内存或等待别的线程。更多核心可能让分工更快,也可能增加这些开销。评估时先用相同输入、相同结果要求测各段时间,再说明硬件和运行条件;不要用代码行数猜串行比例。

公式的上界依赖这里的假设,不是跨所有机器与算法的永恒天花板。换算法、改每核速度或增大工作量,都改变了问题。若目标是同样时间处理更多数据,要另外说明扩大的任务;若想让原任务更快,也可以先寻找串行部分有没有办法减少。

记住这三点

  • 串行比例按原始单核耗时计算,不按代码行数计算。
  • 固定工作量下,只有可并行部分能在理想模型中除以核心数。
  • Amdahl 上界依赖同等核心、固定分工等假设,真实开销和性能仍需测量。

别把例子用过头

  • 本例没有并行执行任务,输出是公式计算,不是这台电脑的性能成绩。
  • 串行比例为零时,有限 N 的理想加速仍只有 N 倍;不存在由非零串行部分给出的固定极限。改变工作量或算法后,也不能原样沿用旧比例。

自己试一小步

怎样给“换更多核心就会快很多”这句话补上可检查的条件?

  1. 自选一份固定工作,画出必须先后完成与能分工处理的两段时间。
  2. 分别试两个串行比例和两组翻倍核心数,先预测趋势再运行模型。
  3. 另列至少两项真实程序可能增加的开销,写出你准备怎样单独观察它们。

做到这些,就算完成

  • 是否用同一份工作、同一份单核时间作比较?
  • 是否把每次新增核心的收益与相对单核的总加速分开?
  • 是否标明公式没有测量哪些真实开销,而非把模型数字当作购买建议?

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

  • NVIDIA:Strong Scaling and Amdahl’s Law (新标签页)↗NVIDIA · 英文

    给出固定问题规模下的 Amdahl 公式、单核耗时中可并行部分的含义,以及核心数增长后的上界。

    已阅读 4.1.3.1 与相邻的强/弱扩展说明;文档讨论 CUDA,但本文只使用其通用加速模型,没有声称运行 GPU 或 CPU 测试。

  • LLNL:Introduction to Parallel Computing Tutorial (新标签页)↗Lawrence Livermore National Laboratory · 英文

    支持串行与并行时间分账、强扩展固定工作量,以及任务创建、同步、数据通信和资源限制带来的额外开销。

    已阅读 Observed Speedup、Parallel Overhead、Amdahl’s Law、Resource Requirements 和 Scalability;未复制其例表或并行实现。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。