🔗 从 Swap 到磁盘:存储性能的最后一环
在第十四章中,我们深入探讨了 Swap 机制------当物理内存不足时,内核将不活跃的匿名页 swap 到磁盘。我们诊断了一个自动驾驶系统的性能崩溃问题,发现每秒 2345KB 的 swap in 和 3456KB 的 swap out 导致 CPU iowait 达到 90%,响应延迟从 50ms 飙升到 5000ms。
这个案例揭示了一个关键问题:磁盘 IO 是现代计算机系统中最慢的环节 。虽然 CPU 的速度以纳秒计,内存以微秒计,但磁盘 IO 的延迟却以毫秒计------相差 6 个数量级(100 万倍)!
在自动驾驶系统中,磁盘 IO 性能直接影响着:
- 传感器数据录制:6 个摄像头 + 2 个激光雷达,每秒产生 ~500MB 数据
- 数据回放:加载历史 rosbag 文件,需要每秒读取 ~1GB 数据
- 高精地图查询:从 100GB 的地图数据库中查询路径信息
- 日志记录:每秒数千条日志写入磁盘
本章关键问题:
- IOPS、吞吐量、延迟三者的关系是什么?
- 为什么顺序 IO 比随机 IO 快 100 倍?
- 如何选择正确的 IO 调度器?
- SSD 性能未达预期,如何诊断?
📊 磁盘 IO 三大性能指标:IOPS、吞吐量、延迟
1. 三大指标的定义
磁盘 IO 性能三角
关系
关系
关系
IOPS
每秒 IO 操作次数
Input/Output Operations Per Second
吞吐量
每秒传输数据量
MB/s 或 GB/s
延迟
单次 IO 操作耗时
ms 或 μs
IOPS (每秒 IO 操作次数):
IOPS = 1 秒内完成的读写操作总数
- 高 IOPS:适合小文件、随机访问(数据库、元数据操作)
- 低 IOPS:大量小 IO 操作会导致 IOPS 饱和
吞吐量 (Throughput):
吞吐量 = 1 秒内传输的数据总量 (MB/s)
- 高吞吐量:适合大文件、顺序访问(视频、日志、备份)
- 关系 :
吞吐量 = IOPS × 平均 IO 大小
延迟 (Latency):
延迟 = 从发起 IO 请求到完成的时间 (ms 或 μs)
- 低延迟:实时系统、交互式应用
- 关系 :
IOPS ≈ 队列深度 / 平均延迟
2. 三者的权衡关系
示例 1:顺序读取大文件(视频回放)
IO 大小:1MB
IOPS:100 次/秒(低)
吞吐量:100 IOPS × 1MB = 100 MB/s(高)
延迟:10ms(中等)
示例 2:随机读取小文件(数据库查询)
IO 大小:4KB
IOPS:10,000 次/秒(高)
吞吐量:10,000 IOPS × 4KB = 40 MB/s(低)
延迟:0.1ms(低)
不可能三角:
- 高 IOPS + 高吞吐量 + 低延迟 → 需要顶级硬件(NVMe SSD)
- 低成本硬件 → 必须在三者间取舍
🔀 顺序 IO vs 随机 IO:100 倍的性能鸿沟
1. 机械硬盘(HDD)的工作原理
顺序 IO
寻道时间
~5ms (一次)
连续读取
1ms/MB
100MB 总耗时: ~105ms
吞吐量: 950 MB/s
随机 IO
寻道时间
~5ms
旋转延迟
~4ms
数据传输
~1ms
总延迟: ~10ms
IOPS: 100
HDD 结构
磁盘盘片
7200 RPM 旋转
磁头
机械臂移动
磁道
HDD 的三个阶段:
- 寻道时间(Seek Time):磁头移动到目标磁道(~5ms)
- 旋转延迟(Rotational Latency):等待数据扇区旋转到磁头下(~4ms,7200RPM)
- 数据传输(Transfer Time):读取数据(~150MB/s)
性能对比:
| IO 模式 | IO 大小 | IOPS | 吞吐量 | 延迟 |
|---|---|---|---|---|
| 随机读取 | 4KB | ~100 | 0.4 MB/s | 10ms |
| 顺序读取 | 1MB | ~100 | 100 MB/s | 10ms |
差异 :顺序读取的吞吐量是随机读取的 250 倍!
2. SSD 的随机 IO 优势
SSD 工作原理:
- 无机械部件,全电子访问
- 寻道时间 = 0
- 旋转延迟 = 0
- 只有闪存读取延迟(~0.1ms)
性能对比(SATA SSD):
| IO 模式 | IO 大小 | IOPS | 吞吐量 | 延迟 |
|---|---|---|---|---|
| 随机读取 | 4KB | ~10,000 | 40 MB/s | 0.1ms |
| 顺序读取 | 1MB | ~500 | 500 MB/s | 2ms |
差异 :顺序读取的吞吐量只是随机读取的 12.5 倍(HDD 是 250 倍)
3. 自动驾驶场景分析
场景 1:传感器数据录制(顺序写入)
bash
# 6 个摄像头 + 2 个激光雷达,每秒 500MB
# 需要的性能:
IOPS: 50-100(大 IO 块)
吞吐量: 500 MB/s
推荐硬件: SATA SSD 或 NVMe SSD
场景 2:点云数据库查询(随机读取)
bash
# 从 100GB 数据库中查询特定区域的点云
# 需要的性能:
IOPS: 5,000-10,000(小查询)
吞吐量: 20-40 MB/s
推荐硬件: NVMe SSD(HDD 会非常慢)
性能测试:
bash
# 测试随机读 IOPS
fio --name=random-read --ioengine=libaio --iodepth=32 --rw=randread \
--bs=4k --direct=1 --size=4G --numjobs=4 --runtime=60 --group_reporting
# 测试顺序读吞吐量
fio --name=seq-read --ioengine=libaio --iodepth=32 --rw=read \
--bs=1M --direct=1 --size=4G --numjobs=1 --runtime=60 --group_reporting
典型结果(NVMe SSD):
随机读 (4KB): IOPS=120,000, BW=468MB/s, lat=0.27ms
顺序读 (1MB): IOPS=3,200, BW=3,200MB/s, lat=10ms
📏 队列深度:并发的力量
1. 队列深度的定义
队列深度(Queue Depth):同时发送给磁盘的 IO 请求数量。
磁盘 IO 队列 应用程序 磁盘 IO 队列 应用程序 队列深度 = 4 IO 请求 1 IO 请求 2 IO 请求 3 IO 请求 4 并发处理 4 个请求 完成 1 完成 2 返回结果 1 返回结果 2 IO 请求 5 IO 请求 6
队列深度 = 1(串行):
请求 1 → 完成 → 请求 2 → 完成 → 请求 3 → ...
延迟:1ms × 100 次 = 100ms
IOPS:100 次 / 0.1s = 1,000
队列深度 = 32(并行):
请求 1-32 → 同时处理 → 完成
延迟:1ms(单次)
IOPS:32 / 0.001s = 32,000
2. 队列深度对性能的影响
测试脚本:
bash
# 测试不同队列深度的性能
for qd in 1 4 8 16 32 64 128; do
echo "Testing QD=$qd"
fio --name=test --ioengine=libaio --iodepth=$qd --rw=randread \
--bs=4k --direct=1 --size=1G --runtime=10 --numjobs=1
done
结果(NVMe SSD):
| 队列深度 | IOPS | 延迟 | 吞吐量 |
|---|---|---|---|
| 1 | 8,500 | 0.12ms | 33 MB/s |
| 4 | 32,000 | 0.12ms | 125 MB/s |
| 8 | 58,000 | 0.14ms | 227 MB/s |
| 16 | 95,000 | 0.17ms | 371 MB/s |
| 32 | 120,000 | 0.27ms | 469 MB/s |
| 64 | 125,000 | 0.51ms | 488 MB/s |
| 128 | 126,000 | 1.02ms | 492 MB/s |
观察:
- QD=1 → QD=32:IOPS 提升 14 倍
- QD=32 → QD=128:IOPS 提升很小(已饱和)
- 最佳队列深度:32-64(平衡性能与延迟)
3. 应用程序如何控制队列深度
直接 IO(绕过 Page Cache):
c
#include <fcntl.h>
#include <libaio.h>
// 打开文件(O_DIRECT 绕过缓存)
int fd = open("/dev/nvme0n1", O_RDONLY | O_DIRECT);
// 初始化 AIO 上下文(队列深度 32)
io_context_t ctx = 0;
io_setup(32, &ctx);
// 准备 32 个 IO 请求
struct iocb *iocbs[32];
for (int i = 0; i < 32; i++) {
iocbs[i] = malloc(sizeof(struct iocb));
io_prep_pread(iocbs[i], fd, buffer[i], 4096, i * 4096);
}
// 提交 32 个请求(队列深度 = 32)
io_submit(ctx, 32, iocbs);
// 等待完成
struct io_event events[32];
io_getevents(ctx, 32, 32, events, NULL);
缓冲 IO(使用 Page Cache):
bash
# 系统会自动调整队列深度
# 查看当前队列深度
cat /sys/block/nvme0n1/queue/nr_requests
# 输出:128(默认)
# 增加队列深度(提升并发)
echo 256 > /sys/block/nvme0n1/queue/nr_requests
⚙️ IO 调度器:内核的"交通警察"
1. IO 调度器的作用
IO 调度器负责决定内核如何将 IO 请求发送给硬件:
- 合并请求:相邻的 IO 请求合并为一个(减少开销)
- 排序请求:按照磁盘位置排序(减少 HDD 寻道时间)
- 公平性:防止某个进程"饿死"其他进程
块设备层
IO 调度器
应用层
进程 A
读请求
进程 B
写请求
进程 C
读请求
调度算法
合并、排序、调度
设备队列
磁盘
2. 传统 IO 调度器(单队列)
noop (No Operation):
- 策略:先进先出(FIFO),不排序不合并
- 适用:SSD、NVMe、虚拟机(底层已优化)
- 优点:CPU 开销最小
- 缺点:无优化
deadline:
- 策略:按截止时间调度,防止请求饿死
- 适用:数据库、实时系统
- 优点:延迟可预测
- 缺点:吞吐量可能较低
cfq (Completely Fair Queuing):
- 策略:按进程公平分配带宽
- 适用:桌面系统、多用户环境
- 优点:公平性好
- 缺点:复杂,CPU 开销大
3. 多队列 IO 调度器(Linux 5.0+)
现代 NVMe SSD 有多个硬件队列(可达 64 个),传统单队列调度器无法充分利用。
none:
- 无调度器(直接发送给硬件)
- 适用:高性能 NVMe
mq-deadline:
- deadline 的多队列版本
- 适用:通用场景
bfq (Budget Fair Queuing):
- 按 IO 带宽预算分配
- 适用:桌面、低延迟场景
kyber:
- 动态调整队列深度
- 适用:云环境、混合负载
4. 查看和设置 IO 调度器
查看当前调度器:
bash
cat /sys/block/nvme0n1/queue/scheduler
# 输出:[none] mq-deadline kyber bfq
# [] 表示当前使用的调度器
临时设置:
bash
echo mq-deadline > /sys/block/nvme0n1/queue/scheduler
永久设置(通过 udev 规则):
bash
# 创建 udev 规则
cat << 'EOF' | sudo tee /etc/udev/rules.d/60-scheduler.rules
# NVMe SSD 使用 none
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"
# SATA SSD 使用 mq-deadline
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD 使用 bfq
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
EOF
# 重新加载 udev 规则
sudo udevadm control --reload-rules
sudo udevadm trigger
5. 调度器选择建议
| 硬件类型 | 负载类型 | 推荐调度器 | 理由 |
|---|---|---|---|
| NVMe SSD | 通用 | none | 硬件已优化,调度器反而增加延迟 |
| SATA SSD | 通用 | mq-deadline | 平衡性能与延迟 |
| HDD | 通用 | bfq | 减少寻道时间,提升公平性 |
| 数据库 | 低延迟 | mq-deadline | 可预测的延迟 |
| 桌面系统 | 交互式 | bfq | 响应性好 |
| 自动驾驶数据采集 | 高吞吐量 | none (NVMe) | 最大化写入带宽 |
🛠️ 实战案例:传感器数据录制性能优化
1. 问题现象
一个自动驾驶数据采集系统,配备了 6 个摄像头和 2 个激光雷达,理论数据流量约 500MB/s。但在实际录制时,系统频繁丢帧,磁盘写入速度只有 150MB/s,远低于 NVMe SSD 的标称性能(3000MB/s)。
初步检查:
bash
iostat -x 1
输出:
Device r/s w/s rMB/s wMB/s rrqm/s wrqm/s %util avgqu-sz await r_await w_await
nvme0n1 0.0 3254 0.00 150.2 0.0 0.0 85.2 2.3 0.71 0.00 0.71
异常点:
- wMB/s = 150MB/s:远低于预期的 500MB/s
- w/s = 3254:每秒 3254 次写操作
- avgqu-sz = 2.3:平均队列深度只有 2.3(太低!)
- %util = 85.2%:设备利用率很高,但吞吐量低
计算平均 IO 大小:
平均 IO 大小 = 150MB/s / 3254 次/s ≈ 47KB
问题定位:IO 大小太小(47KB),队列深度不足(2.3),无法发挥 NVMe 的性能。
2. 深入诊断:blktrace
使用 blktrace 追踪 IO 请求:
bash
# 启动 blktrace(记录 10 秒)
sudo blktrace -d /dev/nvme0n1 -o trace &
sleep 10
sudo killall blktrace
# 分析 IO 模式
blkparse -i trace | head -100
输出示例:
259,0 0 1 0.000000000 12345 Q WS 204800 + 96 [data_recorder]
259,0 0 2 0.000023456 12345 G WS 204800 + 96 [data_recorder]
259,0 0 3 0.000045678 12345 I WS 204800 + 96 [data_recorder]
259,0 0 4 0.000067890 12345 D WS 204800 + 96 [data_recorder]
259,0 0 5 0.000789012 12345 C WS 204800 + 96 [0]
分析:
- WS:写入,同步(sync)
- 204800 + 96:起始扇区 + 扇区数(96 扇区 × 512B = 48KB)
- 时间戳差异:从 Q(队列)到 C(完成)约 0.79ms
发现:应用程序使用 48KB 的同步写入,且队列深度很小。
3. 优化方案
方案 1:增大 IO 块大小
cpp
// 原始代码(小 IO)
void RecordSensorData(const SensorFrame& frame) {
// 每帧单独写入(48KB)
write(fd, &frame, sizeof(frame));
fsync(fd); // ❌ 每次都同步
}
// 优化后(批量写入)
class SensorRecorder {
private:
std::vector<SensorFrame> buffer;
static constexpr size_t BUFFER_SIZE = 100; // 缓冲 100 帧
public:
void RecordSensorData(const SensorFrame& frame) {
buffer.push_back(frame);
// 缓冲区满时批量写入
if (buffer.size() >= BUFFER_SIZE) {
Flush();
}
}
void Flush() {
// 一次写入 4.8MB(100 帧 × 48KB)
write(fd, buffer.data(), buffer.size() * sizeof(SensorFrame));
fsync(fd); // 100 帧同步一次
buffer.clear();
}
};
方案 2:使用异步 IO(io_uring)
cpp
#include <liburing.h>
struct io_uring ring;
void InitRecorder() {
// 初始化 io_uring(队列深度 64)
io_uring_queue_init(64, &ring, 0);
}
void RecordSensorData(const SensorFrame& frame) {
// 获取 SQE(Submission Queue Entry)
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
// 准备写入请求
io_uring_prep_write(sqe, fd, &frame, sizeof(frame), offset);
// 提交请求(非阻塞)
io_uring_submit(&ring);
// 无需等待完成(异步)
}
void WaitCompletion() {
struct io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
io_uring_cqe_seen(&ring, cqe);
}
方案 3:调整内核参数
bash
# 增加队列深度
echo 512 > /sys/block/nvme0n1/queue/nr_requests
# 禁用调度器(NVMe 直接发送)
echo none > /sys/block/nvme0n1/queue/scheduler
# 增加 writeback 缓存
sysctl -w vm.dirty_ratio=40
sysctl -w vm.dirty_background_ratio=10
4. 优化效果
再次运行 iostat:
bash
iostat -x 1
优化后:
Device r/s w/s rMB/s wMB/s rrqm/s wrqm/s %util avgqu-sz await r_await w_await
nvme0n1 0.0 104 0.00 520.3 0.0 0.0 45.2 32.5 0.31 0.00 0.31
改善:
| 指标 | 优化前 | 优化后 | 改善 |
|---|---|---|---|
| 写入带宽 | 150 MB/s | 520 MB/s | ↑ 247% |
| 写入次数 | 3254 次/s | 104 次/s | ↓ 97% |
| 平均 IO 大小 | 47KB | 5MB | ↑ 106 倍 |
| 队列深度 | 2.3 | 32.5 | ↑ 14 倍 |
| 设备利用率 | 85% | 45% | ↓ 47%(更高效) |
| 延迟 | 0.71ms | 0.31ms | ↓ 56% |
丢帧率:从 15% 降至 0%
🔧 IO 性能监控工具
1. iostat - 实时 IO 统计
基础用法:
bash
# 每秒刷新一次
iostat -x 1
# 显示更详细的信息
iostat -xz 1
# -x: 扩展统计
# -z: 跳过无活动的设备
关键字段解释:
r/s, w/s: 每秒读/写次数(IOPS)
rMB/s, wMB/s: 每秒读/写数据量(吞吐量)
rrqm/s: 每秒合并的读请求数
avgqu-sz: 平均队列长度(重要!)
await: 平均等待时间(ms)
%util: 设备利用率(接近 100% 说明饱和)
性能瓶颈判断:
%util = 100% + avgqu-sz > 10 → 设备饱和
await > 10ms (SSD) → 延迟过高
r/s + w/s 接近设备 IOPS 上限 → IOPS 饱和
2. iotop - 进程级 IO 监控
bash
# 显示哪些进程在进行 IO
sudo iotop
# 只显示有 IO 的进程
sudo iotop -o
# 按累计 IO 排序
sudo iotop -a
输出:
Total DISK READ: 45.67 M/s | Total DISK WRITE: 234.56 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
12345 be/4 root 0.00 B/s 234.56 M/s 0.00 % 85.23 % data_recorder
23456 be/4 user 45.67 M/s 0.00 B/s 0.00 % 12.34 % rosbag_player
3. blktrace - IO 请求追踪
bash
# 记录 IO 事件
sudo blktrace -d /dev/nvme0n1 -o trace &
# 运行负载
sleep 10
sudo killall blktrace
# 分析 IO 模式
blkparse -i trace > trace.txt
# 查看 IO 延迟分布
btt -i trace.blktrace.*
IO 请求的生命周期:
Q (Queued) → 请求进入内核队列
G (Get request)→ 分配请求结构体
I (Inserted) → 插入 IO 调度器
D (Dispatched) → 发送给驱动
C (Completed) → 完成
Q2C 延迟 = 总延迟(应用程序视角)
D2C 延迟 = 设备延迟(硬件性能)
4. fio - IO 性能基准测试
测试随机读 IOPS:
bash
fio --name=randread --ioengine=libaio --iodepth=32 \
--rw=randread --bs=4k --direct=1 --size=4G \
--numjobs=4 --runtime=60 --group_reporting \
--filename=/dev/nvme0n1
测试顺序写吞吐量:
bash
fio --name=seqwrite --ioengine=libaio --iodepth=32 \
--rw=write --bs=1M --direct=1 --size=10G \
--numjobs=1 --runtime=60 --group_reporting \
--filename=/dev/nvme0n1
自动驾驶场景测试:
bash
# 模拟传感器数据录制(顺序写,大块)
fio --name=sensor-record \
--ioengine=libaio --iodepth=64 \
--rw=write --bs=256k --direct=1 \
--size=50G --numjobs=1 --runtime=60
# 模拟点云数据库查询(随机读,小块)
fio --name=pointcloud-query \
--ioengine=libaio --iodepth=32 \
--rw=randread --bs=16k --direct=1 \
--size=20G --numjobs=4 --runtime=60
💽 NVMe vs SATA SSD vs HDD:性能天壤之别
1. 三种存储介质对比
| 指标 | HDD (7200 RPM) | SATA SSD | NVMe SSD |
|---|---|---|---|
| 接口带宽 | 6 Gbps (SATA) | 6 Gbps (SATA) | 32 Gbps (PCIe 4.0 x4) |
| 随机读 IOPS | ~100 | ~10,000 | ~500,000 |
| 顺序读吞吐 | ~150 MB/s | ~550 MB/s | ~7,000 MB/s |
| 随机读延迟 | ~10 ms | ~0.1 ms | ~0.02 ms |
| 队列深度 | 1-4 | 32 | 64-256 |
| 价格 ($/TB) | ~$20 | ~$80 | ~$120 |
性能差异可视化:
顺序读吞吐
HDD
150 MB/s
SATA SSD
550 MB/s
3.7x
NVMe SSD
7,000 MB/s
46x
随机读 IOPS
HDD
100 IOPS
SATA SSD
10,000 IOPS
100x
NVMe SSD
500,000 IOPS
5,000x
2. 自动驾驶系统存储方案
方案 1:全 NVMe(高性能)
主存储: 2TB NVMe SSD(PCIe 4.0)
- 传感器数据录制
- 点云数据库
- 操作系统和应用程序
成本: ~$240
优点: 最佳性能,低延迟
缺点: 容量有限,成本高
方案 2:NVMe + SATA SSD(平衡)
热数据: 500GB NVMe SSD
- 当前录制的数据
- 频繁查询的地图数据
冷数据: 4TB SATA SSD
- 历史录制数据
- 归档日志
成本: ~$60 + $320 = $380
优点: 性能与容量兼顾
缺点: 需要数据分层管理
方案 3:NVMe + HDD(经济)
热数据: 500GB NVMe SSD
- 实时处理数据
归档: 8TB HDD
- 长期存储
成本: ~$60 + $160 = $220
优点: 最经济,大容量
缺点: 访问归档数据很慢
3. 验证硬件性能
检查 NVMe 链路速度:
bash
# 查看 PCIe 信息
sudo lspci -vv | grep -A 20 "Non-Volatile"
输出:
LnkCap: Port #0, Speed 16GT/s, Width x4 ⬅️ PCIe 4.0 x4 (最大 ~8GB/s)
LnkSta: Speed 16GT/s, Width x4 ⬅️ 实际运行在最高速度
如果发现:
LnkSta: Speed 8GT/s, Width x4 ⬅️ 只跑 PCIe 3.0(限速)
原因:
- 主板插槽只支持 PCIe 3.0
- NVMe 插在了共享带宽的插槽上
- 解决:更换到 CPU 直连的 M.2 插槽
📝 总结与最佳实践
核心要点
- IOPS、吞吐量、延迟的权衡:无法同时最大化,需根据负载选择
- 顺序 IO 比随机 IO 快 100 倍(HDD)或 12 倍(SSD)
- 队列深度是关键:QD=1 vs QD=32,IOPS 可提升 10-15 倍
- IO 调度器选择:NVMe 用 none,SATA SSD 用 mq-deadline,HDD 用 bfq
- 大 IO 块 + 高队列深度 = 高性能
IO 性能诊断清单
✅ 快速检查
bash
# 查看 IO 统计
iostat -x 1
# 查看进程 IO
sudo iotop -o
# 查看队列深度和调度器
cat /sys/block/nvme0n1/queue/nr_requests
cat /sys/block/nvme0n1/queue/scheduler
✅ 深度分析
bash
# 追踪 IO 请求
sudo blktrace -d /dev/nvme0n1 -o trace
blkparse -i trace
# 性能基准测试
fio --name=test --ioengine=libaio --iodepth=32 \
--rw=randread --bs=4k --runtime=60
✅ 性能优化
bash
# 调整队列深度
echo 512 > /sys/block/nvme0n1/queue/nr_requests
# 设置调度器
echo none > /sys/block/nvme0n1/queue/scheduler
# 调整 writeback 参数
sysctl -w vm.dirty_ratio=40
自动驾驶 IO 优化建议
| 场景 | 负载特征 | 优化方案 |
|---|---|---|
| 传感器录制 | 顺序写,大块,高吞吐 | NVMe + 大 IO 块(1-4MB) + 异步 IO + QD=64 |
| 数据回放 | 顺序读,大块 | madvise(MADV_SEQUENTIAL) + readahead |
| 点云查询 | 随机读,小块,高 IOPS | NVMe + 直接 IO + QD=32 |
| 日志记录 | 小块写,低延迟 | 批量缓冲 + 异步 fsync |
关键代码模式:
cpp
// ✅ 高性能 IO 模式
1. 使用大 IO 块(1-4MB)
2. 异步 IO(io_uring 或 libaio)
3. 直接 IO(O_DIRECT)绕过 Page Cache
4. 高队列深度(32-64)
5. 批量处理(减少系统调用)
// ❌ 低性能 IO 模式
1. 小 IO 块(4KB)
2. 同步 IO(阻塞等待)
3. 缓冲 IO + 频繁 fsync
4. 队列深度 = 1
5. 每次操作都系统调用
🎯 下一章预告
在本章中,我们深入探讨了磁盘 IO 性能的三大指标------IOPS、吞吐量、延迟,理解了顺序 IO 与随机 IO 的巨大性能差异(100 倍),掌握了队列深度对性能的关键影响(10-15 倍提升),学会了选择合适的 IO 调度器,并通过一个传感器数据录制系统的优化案例,将写入带宽从 150MB/s 提升到 520MB/s。
在下一章《文件系统性能优化与分析》中,我们将深入文件系统层面的性能优化:
- Ext4、XFS、Btrfs 的性能特点与适用场景
- 元数据操作对性能的影响(文件创建、删除、stat)
- fsync、fdatasync、sync 的区别与性能开销
- 日志模式(ordered、writeback、journal)的选择
- Direct IO、Buffered IO、mmap 的性能对比
- 文件碎片对性能的影响与优化
- inotify、fanotify 的性能开销
通过真实的自动驾驶日志系统和数据回放场景,我们将揭示文件系统对整体性能的深远影响。敬请期待!🚀