想象你是一家大排档的老板,雇了一位手艺极好的大厨。可惜厨房设计很离谱:所有食材都放在十米外的仓库,大厨每炒一道菜都要来回跑好几趟拿番茄、鸡蛋、盐和葱姜蒜。菜本身几十秒就能出锅,但跑腿却让客人等上十分钟。 现代计算机的处境一模一样。CPU 这位“大厨”算数极快,但数据和程序却存放在远离它的内存里。每一次“取数—算完—写回”的数据搬运,都在消耗时间和能量。在很多 AI 芯片和超算系统中,数据搬运的能耗占总能耗的 60% 甚至更高——真正的瓶颈不是“算”,而是“搬”。 这篇文章用“厨房改造”的故事线,带你理解数据搬运优化的核心思想:如何让数据被反复复用、如何分块搬运、如何设计存储层次、如何软硬协同、如何调度数据流。学完之后你会发现,现代 AI 加速器里的种种精巧设计,本质上都是在替大厨“少跑腿”。

岗位关联

对 AI 从业者:无论你做模型训练、推理优化还是芯片架构设计,理解数据搬运优化都能帮你从“算力不够”的迷思中走出来,看到系统真实瓶颈所在。

目录
  1. 可视化演示
  2. 搬运瓶颈现状
  3. 数据复用原理
  4. 分块策略优化
  5. 分层存储设计
  6. 软硬协同优化
  7. 数据流调度
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

交互展示存储墙能耗瓶颈、三种数据流复用模式与存储层次延迟对比,理解AI芯片数据搬运优化核心方法。

本节配有交互动效(进入视口后自动加载,约 3KB)。

存储墙能耗瓶颈 数据搬运能耗远超计算能耗 搬运与距离和层级正相关 E_move >> E_calc 数据搬运是AI芯片能效首要瓶颈

搬运瓶颈现状

深夜的大排档,大厨阿强要炒一盘番茄炒蛋。他每放一样东西都要跑到十米外的仓库:拿番茄、拿鸡蛋、拿盐、拿糖……配料明明就几样,一趟趟跑下来却让客人等上十分钟。他炒菜只要三十秒,剩下九分半都耗在跑腿上了。

现代通用计算机的“冯·诺依曼架构”同样把“厨房”和“仓库”分开了:CPU 负责计算,内存储存数据,两者之间通过总线搬运数据。CPU 的时钟频率在过去二十年提升了几十倍,但内存访问延迟只下降了几倍——这就形成了著名的“存储墙”问题。墙内的人再能算,墙外的数据进不来,一切都是空转。

我们用一个量化视角看:一次 32 位算术运算的能耗约 1 个单位,而一次访问 DRAM 的能耗可达 12 个单位;内存访问延迟通常是流水线计算延迟的 50 到 200 倍。系统总时间由计算时间和搬运时间组成,而搬运时间往往占据大头。

阿强厨艺再精进,也抵不住仓库的遥远。数据搬运就是这种“看不见的跑腿”,它决定了菜品的真实出货速度。要想让系统变快,光升级“厨师”远远不够,必须从减少跑腿次数开始。

Emove≈12×Ecompute,η=TmoveTcompute+Tmove\displaystyle E_{move} \approx 12 \times E_{compute}, \qquad \eta = \frac{T_{move}}{T_{compute} + T_{move}}
这个公式告诉我们:一次数据搬运的能耗大约是一次计算的 12 倍,搬运延迟占比 \(\eta\) 越接近 1,系统越是在“跑腿”而不是“干活”。
  1. 计算能耗基准\(E_{compute}\) 表示执行一次加减乘除等算术运算的能耗,这里把它归一化看作 1 个单位,作为全系统的能耗标尺
    Ecompute≈1E_{compute} \approx 1
  2. 搬运能耗的差距\(E_{move}\) 是数据在内存与 CPU 之间移动一次的能耗,约等于 12 次算术运算的代价
    Emove≈12×EcomputeE_{move} \approx 12 \times E_{compute}
  3. 总时间构成\(T_{total}\) 由计算时间 \(T_{compute}\) 和搬运时间 \(T_{move}\) 相加得到,两者随执行顺序可能部分重叠
    Ttotal=Tcompute+TmoveT_{total} = T_{compute} + T_{move}
  4. 搬运占比\(\eta\) 衡量搬运在总耗时中的比例,数值越接近 1,说明系统把绝大部分时间都花在了取送数据上
    η=TmoveTcompute+Tmove\eta = \frac{T_{move}}{T_{compute} + T_{move}}

数据复用原理

老师傅阿强有个习惯:每天开工前先把葱姜蒜切好,分装在小碟子里。炒三盘菜,用的都是同一碟葱花。外人以为他动作快,其实真正秘诀是“一份食材多次用”。

程序访问数据也有类似的规律,叫作“局部性原理”。时间局部性指同一个数据在短时间内被反复访问,就像同一碟葱花连续出现在三盘菜里;空间局部性指一段时间内访问的地址彼此邻近,就像做菜时盐和糖总放在一起、每次都会同时用到。CPU 的缓存正是利用这两点,把常用数据留在身边。还有一个概念叫复用距离:同一数据两次访问之间隔了多少次其他访问,距离越小,数据越可能还留在缓存里。

衡量数据复用效果的关键指标是复用因子 \(R\)。假设程序总共要访问 100 次数据,如果缓存命中后每次都在缓存里读,而真正从内存搬运只有 10 次,那么 \(R = 10\)。编译器通过循环变换、数据重排等技术,尽量让 \(R\) 变大。

同一碟葱花,在三道菜中反复出现,就是时间局部性在厨房里的体现。数据复用做得越好,从仓库取货的次数越少,大厨能专注炒菜的时间就越多。

R=NaccessNfetch,R≥1\displaystyle R = \frac{N_{access}}{N_{fetch}}, \qquad R \ge 1
复用因子 \(R\) 是“总访问次数”与“实际搬运次数”的比值。\(R\) 越大,说明每次搬运服务了越多次计算,系统的搬运效率越高。
  1. 总访存次数\(N_{access}\) 表示程序运行期间对数据发出的所有读写请求总数,这是程序天然需求的体现
    Naccess=读请求数+写请求数N_{access} = \text{读请求数} + \text{写请求数}
  2. 真正搬运次数\(N_{fetch}\) 表示数据块从慢速存储真正搬到快速存储(如缓存)的次数,也就是缓存未命中次数
    Nfetch=缓存未命中次数N_{fetch} = \text{缓存未命中次数}
  3. 复用因子\(R\) 是二者的比值,等于一次搬运平均被后续访问利用了多少次,它是优化数据搬运的核心指标
    R=NaccessNfetchR = \frac{N_{access}}{N_{fetch}}

分块策略优化

然而,食材碟子再多,灶台前的台面也有限。阿强的对策是“按菜单分装”:把一周的食材按每道菜的分量装进小袋,每袋恰好塞进台面的小抽屉。每次做菜只抽一袋,用完整理好再换下一袋。

在循环计算中,数据总量通常远大于缓存容量,于是缓存不断驱逐旧数据、加载新数据。分块策略(Blocking/Tiling)把大循环切成小块:让每一块数据在被移出缓存之前完成尽可能多的计算,从而降低搬运总量。实现时通过重排内外层循环顺序,即访存重排,让块内访问尽量集中,提高缓存命中。

假设原始搬运量为 \(N \times M\),其中 \(N\) 为计算步数,\(M\) 为每步需要的数据字节数。分块后,每块数据被复用 \(B\) 次,搬运总量降到原来的 \(1/B\)。但块大小必须满足 \(B \le C\)(\(C\) 为缓存容量),否则块装不进缓存,搬运次数反而增加。实际工程中还会预留若干缓存路给其他数据,块大小常取 \(C\) 的一半甚至更小。

小袋食材刚好塞进抽屉,一次取一袋、一袋做三道菜——这就是分块的精髓:既不多占台面,也不反复跑仓库。

Qblock=N×MB,B≤C\displaystyle Q_{block} = \frac{N \times M}{B}, \qquad B \le C
分块的核心是“以小博大”:块越小越容易装进缓存,但复用次数 \(B\) 越大搬运量越小。块大小必须与缓存容量匹配,才能让搬运量下降 \(B\) 倍。
  1. 朴素搬运量\(N \times M\) 表示不分块时,每次计算所需的全部数据都被当成新数据读取,搬运总量等于计算步数乘以每步数据量
    Qnaive=N×MQ_{naive} = N \times M
  2. 分块复用倍数\(B\) 表示一块数据被装入缓存后,能在块内支撑多少次计算,它是分块收益的直接来源
    B=块内复用次数B = \text{块内复用次数}
  3. 容量约束块大小 \(B\) 必须小于等于缓存容量 \(C\),否则块还没用完就被挤出缓存,复用无从谈起
    B≤CB \le C
  4. 优化后的搬运量\(Q_{block}\) 表示分块之后的真实搬运总量,它比朴素方案缩小了 \(B\) 倍
    Qblock=N×MBQ_{block} = \frac{N \times M}{B}

分层存储设计

阿强最终请来设计师翻新厨房:最常用的盐、油、铲子挂在灶台上方;每周要用的干货放中岛柜;只有每月才用的大汤锅放进储物间。离灶台越近的东西,取用越快。

计算机的存储也按“离 CPU 的远近”排列成层次结构:寄存器、L1/L2/L3 缓存、主存、磁盘。越靠近 CPU 的层次越快、越贵、容量越小。程序运行时,数据在各层之间流动,系统感受到的平均访问时间取决于命中率——也就是数据在快速层被找到的概率。

平均访问时间由命中率 \(H\)、快速层延迟和慢速层延迟共同决定。\(H\) 越接近 1,系统越像快速层一样迅速。近数据计算是“把厨师派到仓库门口做菜”,在内存附近放置计算单元,减少原始数据的长途搬运;存算一体更进一步,直接在存储阵列内部完成计算,让数据根本不用离开“仓库”。

厨房布局决定了做菜体验,存储层次决定了程序性能上限。哪怕算法再完美,把食材放在“够不着”的地方,一切都白搭。

Tavg=H×Tfast+(1−H)×Tslow\displaystyle T_{avg} = H \times T_{fast} + (1 - H) \times T_{slow}
平均访问时间是命中与未命中两种情况的加权平均。命中率越高,系统越少受慢速层拖累,整体表现越接近缓存的速度。
  1. 命中率\(H\) 表示数据访问命中快速存储层(如缓存)的概率,命中率越高,等待越短
    H=快速层命中次数总访问次数H = \frac{\text{快速层命中次数}}{\text{总访问次数}}
  2. 快速层延迟\(T_{fast}\) 是数据已在缓存中时的访问开销,通常只有几个时钟周期
    Tfast=缓存命中访问时间T_{fast} = \text{缓存命中访问时间}
  3. 慢速层延迟\(T_{slow}\) 是数据在主存或更远层次时的访问开销,包含总线传输与存储阵列等待
    Tslow=主存访问时间+总线传输时间T_{slow} = \text{主存访问时间} + \text{总线传输时间}
  4. 平均访问时间\(T_{avg}\) 以命中率为权重,对快慢两种延迟做加权平均,是评估存储层次设计的关键指标
    Tavg=H×Tfast+(1−H)×TslowT_{avg} = H \times T_{fast} + (1-H) \times T_{slow}

软硬协同优化

光有厨房布局还不够。阿强请了一位帮厨小芳:她提前看菜单,把每道菜需要的配料切好、腌好、按顺序摆在小碗里。阿强只管下锅翻炒,两人配合得天衣无缝。

软件和硬件也可以这样“打配合”。软件侧,编译器和算法工程师负责优化数据布局和循环结构:把一起使用的数据放到相邻地址,通过循环变换让访问顺序更规则,减少缓存失配;硬件侧,缓存预取器根据历史访问模式预测未来的数据请求,提前把数据搬到缓存中。访存调度则决定多个读写请求的排队顺序,让总线不会被单个请求堵死。

当搬运与计算足够重叠时,整体耗时从“相加”变为“取最大值”。这就是为什么预取和流水线能显著提升性能——总工作量没变,但时间被“藏”到了计算背后。

帮厨提前备菜、大厨连续炒菜,总工作量没变,客人等待的时间却大幅缩短。软硬协同,本质上是让一份工作“有人干的时候,另一份工作也在干”。

Tpipeline=max⁡(Tcompute,Tmove)\displaystyle T_{pipeline} = \max(T_{compute}, T_{move})
软硬协同的核心收益:当计算与搬运并行发生时,总时间不再是一加一,而是由较慢的一方决定。就像帮厨备菜与主厨炒菜同时进行,出菜时间取决于更慢的那个环节。
  1. 计算时间\(T_{compute}\) 表示所有算术逻辑动作完成所需的时间,它由指令数和处理器吞吐能力共同决定
    Tcompute=指令数吞吐率T_{compute} = \frac{\text{指令数}}{\text{吞吐率}}
  2. 搬运时间\(T_{move}\) 表示所有数据搬运动作完成所需的时间,它由搬运数据量和总线带宽决定
    Tmove=搬运字节数带宽T_{move} = \frac{\text{搬运字节数}}{\text{带宽}}
  3. 重叠后的总时间当预取或流水线让搬运与计算并行时,总时间取两者中的较大者,而不是简单相加
    Tpipeline=max⁡(Tcompute,Tmove)T_{pipeline} = \max(T_{compute}, T_{move})

数据流调度

周末流水席,阿强把厨房改成三条工作线:小芳负责切菜,阿强负责炒,小李负责装盘。三个人各干各的,只要每人稳定完成自己的工序,整条流水线就能源源不断出菜。

数据流调度研究的就是:在给定硬件资源下,如何安排读写请求与计算任务的先后顺序,让不同硬件单元尽量并行工作。比如先把下一块要算的数据提前读出(预取式调度),或把不依赖当前结果的任务插入搬运等待的空档,避免“人等菜”或“菜等人”的空闲。

把“取数据—计算—写回”看成流水线的三级。当任务量大时,第一个任务需要一段启动时间才能流出流水线,之后每个任务只需等待最慢一级的耗时 \(\tau\)。任务数越多,启动开销被摊薄得越干净,系统整体吞吐率就越接近 \(1/\tau\)。

流水席的成功不靠某一位师傅的手速,而靠整条流水线的节奏。数据流调度优化的正是这种“节奏感”,让搬运、计算、写回像流水线一样无缝衔接。

TP=NTstartup+(N−1)×τ≈1τ\displaystyle TP = \frac{N}{T_{startup} + (N-1) \times \tau} \approx \frac{1}{\tau}
吞吐率公式揭示了一个“节奏”规律:第一个任务要经历流水线排空时间,后面每个任务都按最慢一级的节拍 \(\tau\) 出队。任务无穷多时,吞吐率逼近 \(1/\tau\)。
  1. 任务总数\(N\) 表示进入流水线的数据块或任务数量,数量越大,启动开销越容易被摊薄
    N=任务总数N = \text{任务总数}
  2. 启动开销\(T_{startup}\) 是流水线从空到满所需的时间,它等于流水线级数 \(k\) 乘以级间节拍
    Tstartup=k×τT_{startup} = k \times \tau
  3. 级间节拍\(\tau\) 是流水线最慢一级的耗时,它决定了整个流水线的最大节奏
    τ=max⁡(t1,t2,…,tk)\tau = \max(t_1, t_2, \dots, t_k)
  4. 总耗时第一个任务在 \(k\tau\) 后完成,之后每 \(\tau\) 时间完成一个新任务
    Ttotal=k×τ+(N−1)×τT_{total} = k \times \tau + (N-1) \times \tau
  5. 吞吐率极限当 \(N\) 足够大时,启动开销被分摊,单位时间完成的任务数趋近于 \(\tau\) 的倒数
    TP=NTtotal≈1τTP = \frac{N}{T_{total}} \approx \frac{1}{\tau}

AI 应用场景

AI 推理引擎中的算子分块

阶段:模型部署与推理优化

解决的问题:Transformer 模型推理时,权重矩阵和激活值频繁进出显存,访存带宽成为吞吐瓶颈。传统的逐元素计算没有充分利用缓存,导致大量重复搬运。通过“分块策略优化”,AI 编译器把大矩阵切成能装进共享内存的小块,让数据在片上被反复复用。

具体案例:在 NVIDIA GPU 上用 Triton 编写矩阵乘法算子,将 4096×4096 的矩阵按 64×64 分块,块数据在 Tensor Core 上复用数百次,使算力利用率从约 20% 提升到 80% 以上。

数据仓库的列式存储与分区设计

阶段:大数据分析与特征工程

解决的问题:数据仓库在做全表扫描时,冷热数据混杂会导致大量无效磁盘 IO。数据布局优化把同一查询常用的字段排在一起,层次化存储设计让热数据驻留内存、冷数据落盘,减少了约 70% 的搬运量。

具体案例:某广告推荐系统的特征表从行存改为列存,并按用户 ID 分区后,单轮训练前的特征查询耗时降低约 4 倍,GPU 集群的空闲等待明显减少。

端侧 AI 芯片的存算一体设计

阶段:AI 芯片架构设计

解决的问题:智能手机上跑人脸识别时,数据传输能耗远超计算能耗。近数据计算和存算一体把卷积乘累加电路嵌入 SRAM 存储阵列内部,让计算直接在“仓库”里完成,避免了大规模数据搬运。

具体案例:某存算一体 AI 芯片在 SRAM 内完成卷积运算,能效比相较传统"CPU+DRAM"方案提升约 10 倍,同时把时延从毫秒级降到微秒级。

Python 演示

python63 行
class MiniCache:    def __init__(self, cap):        self.cap = cap        self.items = []        self.miss = 0     def access(self, addr):        if addr in self.items:            self.items.remove(addr)            self.items.append(addr)            return True        self.miss += 1        if len(self.items) >= self.cap:            self.items.pop(0)        self.items.append(addr)        return False  def build_window(n, tile):    seq = []    if tile == 0:        cells = [(r, c) for r in range(n) for c in range(n)]    else:        cells = []        for rb in range(0, n, tile):            for cb in range(0, n, tile):                for r in range(rb, min(rb + tile, n)):                    for c in range(cb, min(cb + tile, n)):                        cells.append((r, c))    for r, c in cells:        for dr in range(-1, 2):            for dc in range(-1, 2):                rr = r + dr                cc = c + dc                if 0 <= rr < n and 0 <= cc < n:                    seq.append(rr * n + cc)    return seq  def main():    n = 64    cap = 128    tile = 8    naive_cache = MiniCache(cap)    tile_cache = MiniCache(cap)    naive_seq = build_window(n, 0)    tile_seq = build_window(n, tile)    for addr in naive_seq:        naive_cache.access(addr)    for addr in tile_seq:        tile_cache.access(addr)    naive_miss = naive_cache.miss    tile_miss = tile_cache.miss    save = (1 - tile_miss / naive_miss) * 100    print("图像尺寸: %dx%d" % (n, n))    print("缓存容量: %d  分块: %dx%d" % (cap, tile, tile))    print("朴素按行扫描搬运次数: %d" % naive_miss)    print("分块扫描搬运次数:   %d" % tile_miss)    print("搬运量下降: %.1f%%" % save)  if __name__ == "__main__":    main()
目的:用一个小型 LRU 缓存模拟器对比“朴素按行扫描”和“分块扫描”两种访问顺序,直观展示分块策略如何让数据在缓存中复用、从而减少搬运次数。
  1. MiniCache(cap)模拟一个容量为 cap 的缓存,缓存满时淘汰最久未使用的数据,对应正文的缓存层次。
  2. build_window(n, 0)生成按行扫描 3×3 邻域的访问序列,代表不做任何优化的朴素顺序。
  3. build_window(n, tile)生成按 tile×tile 分块扫描的访问序列,对应分块策略优化。
  4. if addr in self.items命中缓存时把该数据挪到最近使用位置,表示无需从内存搬运。
  5. self.miss += 1每次未命中都代表一次真实的数据搬运,这是我们要优化的核心指标。
  6. naive_cache.access(addr)让朴素访问序列依次经过缓存,统计它的搬运次数作为基线。
  7. save = (1 - tile_miss / naive_miss) * 100算出分块策略省下了多少搬运量,量化优化效果。

常见误区

误区缓存容量不够大所以才频繁搬运,我把缓存从 64KB 换成 64MB,数据搬运问题自然就解决了。

正解缓存容量增大确实能降低未命中率,但成本和功耗也会急剧上升。更关键的是提高数据的复用率:分块、循环重排、数据布局优化可以让小块缓存发挥出接近大缓存的命中率。真正的优化是让同样的数据少被搬运,而不是无限扩大存放空间。

为何常见:大家习惯用“容量”理解缓存,却忽略了访问模式对命中率的决定性影响。容量只是硬件参数,访问顺序才是软件可控的变量。

误区分块越大,一次搬进缓存的数据越多,复用次数越多,搬运总量应该越少,所以块越大越好。

正解块大小必须不大于缓存容量,否则块内数据还没算完就被挤出缓存,复用无从谈起。实际中还要考虑缓存路数、替换策略和多程序干扰,块大小常常取缓存容量的一半甚至更小。块大了反而会因为装不下而频繁换入换出,搬运量不降反增。

为何常见:直觉上“一次多搬点”似乎能减少搬运次数,但忽略了缓存容量这个硬约束。就像小抽屉塞不进大袋子,硬塞只会把手卡住。

误区存算一体是革命性新技术,既然能在存储里直接算,那传统分层存储和缓存机制早晚被淘汰。

正解存算一体在卷积、矩阵乘等数据搬运主导的场景有明显优势,但它会带来面积开销、灵活性下降和通用性受限等问题。常规程序仍需要通用计算单元和分层缓存。最优设计往往是混合架构:该就近算的就近算,该通用算的交给 CPU/GPU。

为何常见:技术热点报道常常放大新架构的覆盖面,让人误以为它能对所有场景通吃。实际工程中很少有银弹方案。

误区CPU 自带硬件预取器,会自动把数据提前搬到缓存里,软件做不做数据布局和循环优化都无所谓。

正解预取器依赖可预测的访问模式,遇到链表跳转、稀疏索引等不规则访问时预取几乎失效。软件侧的数据布局和循环变换是预取成功的前提,只有数据访问足够规律,预取器才能精准预测。两者配合,才能把搬运隐藏到计算身后。

为何常见:底层硬件细节不透明,初学者容易高估硬件的自动优化能力,低估编译器与算法设计对性能的决定性影响。

面试题

Q1. 在冯·诺依曼架构中,造成系统总开销最大的通常是什么?

单选题 · easy

  • A.算术运算单元的执行能耗
  • B.内存与计算之间的搬运开销← 正确答案
  • C.指令译码与分支预测的能耗
  • D.寄存器读写所产生的时钟开销

答案:B。冯·诺依曼架构把计算和存储分开,数据每次在内存和 CPU 之间往返都像厨师去仓库跑腿。搬运能耗和延迟都远高于单次算术运算,因此系统开销最大的环节往往是数据搬运。

Q2. “时间局部性”指的是下面哪种现象?

单选题 · easy

  • A.一段时间内反复访问同一个数据← 正确答案
  • B.一段时间内访问相邻的若干个地址
  • C.不同时间片访问完全不同的数据
  • D.访问时间随数据地址周期变化

答案:A。时间局部性对应“同一个数据短时间内被反复访问”,就像同一碟调料在连续几道菜中反复使用。选项 B 描述的是空间局部性,C 和 D 与局部性原理无关。

Q3. 分块优化中,块大小 B 必须满足什么约束?

单选题 · mid

  • A.B 等于程序总数据量
  • B.B 小于等于缓存容量← 正确答案
  • C.B 大于缓存容量的一倍
  • D.B 与缓存容量没有关系

答案:B。分块的前提是块能装进缓存。如果块比缓存还大,数据搬入后还没来得及用完就被挤出,搬运次数不会减少。就像小抽屉放不下大袋子,强行放反而更乱。

Q4. 平均访问时间 \(T_{avg}=H \times T_{fast}+(1-H) \times T_{slow}\) 中,命中率 H 升高会使什么发生?

单选题 · mid

  • A.平均访问时间逐渐增大,同时慢速层也变慢
  • B.平均访问时间逐渐减小,性能提升← 正确答案
  • C.快速层延迟随命中率提高而增大
  • D.慢速层延迟随命中率提高而下降

答案:B。H 越接近 1,平均访问时间越接近快速层的低延迟。命中率升高一定会降低平均访问时间;慢速层和快速层本身的延迟是硬件属性,不会因 H 改变。这就像食材放在手边的概率越高,平均取菜时间越短。

Q5. 近数据计算的核心思想是?

单选题 · easy

  • A.把内存搬到 CPU 芯片附近
  • B.把计算搬到数据所在地点← 正确答案
  • C.把数据复制多份并分发
  • D.增加数据搬运的带宽上限

答案:B。近数据计算是把运算单元放到存储附近,例如在内存旁加计算部件,减少原始数据的长途往返。它不改变数据总量,而是让数据少跑路,像把厨师派到仓库门口现场做菜。

Q6. 当搬运和计算完全重叠时,流水线总时间接近哪个值?

单选题 · hard

  • A.搬运时间与计算时间之和
  • B.计算时间与搬运中的较大值← 正确答案
  • C.搬运时间乘以计算时间
  • D.计算时间减去搬运时间

答案:B。搬运与计算重叠时两者并行发生,总时长取决于较慢的一方。像帮厨备菜与主厨炒菜同时进行,出菜时间取决于炒菜或备菜中更长的那段。这就是 \(T=\max(T_{compute}, T_{move})\) 的含义。

Q7. 流水线吞吐率公式 \(TP=N/(T_{startup}+(N-1)\tau)\) 中,当任务数 N 趋于无穷时,吞吐率趋近于?

单选题 · extreme

  • A.逐渐减小为 0
  • B.趋近于流水线的级间节拍
  • C.趋近于级间节拍 τ 的倒数← 正确答案
  • D.随任务数 N 线性增长

答案:C。N 趋于无穷时,启动开销被摊薄,\((N-1)\tau\) 近似等于 \(N\tau\),所以吞吐率趋近 \(1/\tau\)。\(\tau\) 是流水线最慢一级的耗时,也就是说系统吞吐由最慢工序决定。

Q8. 以下哪些手段能有效降低数据搬运量?

多选题 · mid

  • A.提高缓存命中率并减少回读← 正确答案
  • B.增大数据块的复用次数← 正确答案
  • C.提高 CPU 的主频大小
  • D.减少缓存容量以省电

答案:AB。提高命中率、增大复用倍数都直接减少实际搬运次数,属于从源头减负。提高 CPU 主频只让计算更快,若瓶颈在搬运,主频再高也无济于事;减少缓存容量会加剧搬运,方向正好相反。

Q9. 软硬协同优化包含以下哪些措施?

多选题 · hard

  • A.编译器循环分块与重排← 正确答案
  • B.硬件预取器提前取数← 正确答案
  • C.访存调度优化请求顺序← 正确答案
  • D.仅增加内存条的容量

答案:ABC。软硬协同包括编译器做的循环分块与重排、硬件预取器,以及访存调度,三者分别从数据布局、提前搬运和请求排队顺序上优化。单纯加大内存条容量属于硬件扩容,不是协同优化手段。

Q10. 关于存储层次结构的说法,哪些正确?

多选题 · mid

  • A.越底层容量越大延迟越高← 正确答案
  • B.缓存命中率决定平均延迟← 正确答案
  • C.寄存器应制作得尽可能大
  • D.各层之间由硬件自动管理← 正确答案

答案:ABD。存储层次越靠近底层,容量越大、延迟越高;命中率直接决定平均访问时间;各层数据搬移主要由硬件自动完成。寄存器容量极小且成本高昂,不可能做得很大,故 C 错误。这就像厨房的储物间越大越远,灶台上的调料盒虽小但最快够到。