一、背景
在一个实际项目中(PDF/打印绘制类输出),大量使用如下代码:
printf("xxx\n");
printf("yyy\n");
printf("%s %d\n", str, num);
这些输出逻辑在循环中频繁执行,导致整体性能较差。
二、问题分析
❗ 为什么 printf 慢?
printf 本质上是一个格式化 + IO操作的组合:
-
格式解析(开销较大)
-
内部缓冲处理
-
最终调用 write / fwrite
👉 更致命的是:
每调用一次 printf ≈ 一次系统调用(或接近)
如果有:
1000 次 printf → 1000 次 write
👉 性能直接炸掉
三、优化方案演进
🥇 ①:printf → write / fwrite
改造方式
printf("hello\n");
改为:
write(1, "hello\n", 6);
优点
-
避免 printf 格式解析
-
减少函数调用层级
实测效果
| 版本 | 时间 |
|---|---|
| 修改前 | 22秒 |
| 修改后 | 16秒 |
👉 提升约 27%
🥈 ②:snprintf + write
用于带变量的情况:
printf("%d %s\n", a, b);
改为:
char buf[256];
int len = snprintf(buf, sizeof(buf), "%d %s\n", a, b);
write(1, buf, len);
优点
-
格式化仍保留
-
IO统一走 write
🥉 ③:批量缓冲输出(核心优化)
👉 真正的性能关键点
思路
把多个输出:
write(...)
write(...)
write(...)
改成:
snprintf 拼接 → 一次 write
示例优化
❌ 原始写法
write(1, "abc ", 4);
write(1, "def ", 4);
write(1, "ghi\n", 4);
✅ 优化后
char buf[256];
int len = 0;
len += snprintf(buf + len, sizeof(buf) - len, "abc ");
len += snprintf(buf + len, sizeof(buf) - len, "def ");
len += snprintf(buf + len, sizeof(buf) - len, "ghi\n");
write(1, buf, len);
效果
write次数:3次 → 1次
👉 系统调用减少 = 性能提升核心
⚠️ 关键坑:不能盲目合并
在实际代码中,有这种结构:
write(1, "(", 1);
PrintCharacter(...); // ⚠️ 内部也 write
write(1, ")\n", 2);
❌ 错误优化(不能这么干)
snprintf(buf, "(%s)\n", outputData); // ❌
原因:
-
PrintCharacter不是简单字符串 -
内部有:
-
转义(\ ( ))
-
UTF-8处理
-
👉 会破坏逻辑
✅ 正确原则
遇到"直接输出函数"必须断开 buffer
🧨 真正性能杀手:逐字符 write
原代码:
write(1, &c, 1);
在循环中:
1000字符 → 1000次 write
🚀 终极优化:函数内部缓冲
改造 PrintCharacter
❌ 原始
write(1, &strings[count], 1);
✅ 优化后
char outbuf[1024];
int outlen = 0;
outbuf[outlen++] = c;
if (满了) {
write(1, outbuf, outlen);
outlen = 0;
}
完整思路
循环处理字符 → 写入 outbuf → 最后一次 write
效果对比
| 场景 | write次数 |
|---|---|
| 原始 | 1000次 |
| 优化后 | 1~3次 |
👉 性能提升数量级级别
🧠 最终优化原则总结
✅ 1. 能不用 printf 就不用
printf → write / snprintf + write
✅ 2. 合并连续输出
多次 write → 一次 write
❗ 3. 遇到"输出函数"必须断开
PrintCharacter / fwrite / write
✅ 4. 循环内绝对不能逐字符 write
必须使用 buffer
✅ 5. 最终目标
整个逻辑 = 最少 write 次数
📊 最终效果总结
| 阶段 | 优化内容 | 效果 |
|---|---|---|
| 初始 | printf | 22秒 |
| ① | write替换 | 16秒 |
| ③ | 批量输出 | 预计进一步下降 |
🎯 结语
这次优化的本质不是"换函数",而是:
减少系统调用次数
如果再进一步,可以做到:
整个页面输出 = 1次 write
👉 这就是 IO 性能优化的终极形态。