2.8 KiB
2.8 KiB
tags, aliases, date created, date modified
| tags | aliases | date created | date modified | |
|---|---|---|---|---|
|
星期一, 十一月 24日 2025, 5:50:26 下午 | 星期一, 十一月 24日 2025, 5:50:35 下午 |
1. 宏观原理图:箱子与积木的错位
想象 CPU 是一个强迫症收纳师,他手里有一排固定的收纳盒(缓存行),每个盒子长度固定是 64。
我们要存的数据(点迹) 是长度为 48 的积木条。
请看下面的图,展示了当我们把积木一条接一条紧挨着放进去时,发生了什么:
---
config:
theme: base
flowchart:
curve: linear
---
graph LR
%% 样式定义
classDef box fill:#e6f7ff,stroke:#1890ff,stroke-width:2px,stroke-dasharray: 5 5
classDef block1 fill:#ffccc7,stroke:#f5222d,stroke-width:2px
classDef block2 fill:#d9f7be,stroke:#52c41a,stroke-width:2px
subgraph Memory["内存空间 (连续摆放)"]
direction LR
subgraph Box1["收纳盒 1 (容量 64)"]
direction LR
A1["积木A (48)"]:::block1
B1["积木B 的头 (16)"]:::block2
end
subgraph Box2["收纳盒 2 (容量 64)"]
direction LR
B2["积木B 的身子 (32)"]:::block2
C1["…"]:::white
end
end
%% 解释连接
A1 -- 紧挨着 --> B1
B1 -- "⚠️ 惨遭腰斩 ⚠️" --> B2
2. 细节文字表述:为什么这很糟糕?
场景还原:
-
强迫症规则:CPU 每次读取数据,必须连盒带盖端走整整一个“收纳盒”(64 字节),不能只捏走里面的某一块。
-
读取积木 A(红色):
- CPU 伸手端走 收纳盒 1。
- 积木 A 完整地在盒子里。
- 耗时:1 次搬运。(快)
-
读取积木 B(绿色):
- CPU 端走 收纳盒 1,拿到了积木 B 的头。
- CPU 发现身子没了,只能再去端走 收纳盒 2,拿到积木 B 的身子。
- 然后 CPU 还得在手里把这两段拼起来。
- 耗时:2 次搬运 + 拼接时间。(慢!)
3. 结论与解决方案
- 问题核心:因为数据的尺寸(48)不能被盒子的尺寸(64)整除,导致后续的数据像“跨栏”一样骑在两个盒子的边界上。这叫跨缓存行(Cache Line Split)。
- 我们的方案(填充 Padding):
- 既然 48 放不进 64 很尴尬,我们就在每个积木后面硬塞 16 块没用的泡沫(Padding)。
- 把积木强行撑大到 64。
- 结果:虽然浪费了空间,但现在每个盒子正好放一个积木。CPU 拿任何积木都只需要搬 1 次盒子。
这就是我们为了极致性能所做的妥协:用空间换时间。