打印性能优化[缓冲模式角度]
Posted BHY_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了打印性能优化[缓冲模式角度]相关的知识,希望对你有一定的参考价值。
问题背景:
在某些场景下,N个进程调用XX接口进行大量字段信息打印,打印信息频繁,极大耗费系统资源。
问题代码:
void functest(std::ostream &os) os << "xxxxx" << std::endl; while(flag) os << "["; os << "xxx" << "yyy" << std::endl; os << "zzz"; os << "]" << std::endl;
os << "xxxxx" << std::endl; |
问题分析角度:
XX接口中使用os有多次输出,其中还有while循环中的os输出(while循环的次数与字段数量正相关)。当os为stdout对象时,其缓冲模式的默认值为_IOLBF,即行缓冲模式,遇到换行符时会将缓冲区内容输出到终端,并清空缓冲区;当os为std::ofstream时(重定向),其缓冲模式默认值通常为_IOFBF,即全缓冲模式,遇到换行不输出。两种情况下区别在于遇到换行是否清理缓冲区。
缓冲模式类型:
- _IOFBF(Fully Buffered):缓冲区已满或显式请求刷新时刷新,典型的缓冲区大小是 4KB。
- _IOLBF(Line Buffered):找到换行符、缓冲区已满或请求刷新时,将刷新缓冲区,典型的缓冲区大小是 1KB。
- _IONBF(Unbuffered):流是无缓冲的,也就是说,一旦可用,就立即发送输出。
cout会对输出的内容进行缓冲,所以输出的内容并不会立即输出到目标设备,而是被存储在缓冲区中,直到缓冲区填满才输出,在缓冲区没有满的时候,手动刷新缓冲区也可以实现输出。手动刷新缓冲区有两种方式,一种是向cout输出一个flush操作符,一种是向cout输出一个endl操作符,flush只刷新缓冲区,endl不仅刷新缓冲区,还加入了一个换行符。
以下情况会引发缓冲区刷新:
- 缓冲区满时;
- 程序结束时;
- 关闭文件,即执行close语句;
- 执行cin语句,cin是和cout绑定的,可以用函数cin.tie(0)来解绑定
- 执行flush语句;
- 执行endl/ends语句
所以在以上代码中,多次std::endl,即有多次的清理缓冲区行为,清理缓冲区对性能的影响测试如下:
打印代码 | 输出到终端 | 输出到文件 |
for (int i = 0; i < 100000; i++) | 6468 | 4705 |
for (int i = 0; i < 100000; i++) | 175658 | 4783 |
for (int i = 0; i < 100000; i++) | 183146 | 152005 |
for (int i = 0; i < 100000; i++) | 160890 | 147487 |
for (int i = 0; i < 50000; i++) | 100196 | 78584 |
for (int i = 0; i < 50000; i++) | 177131 | 6772 |
输出到终端/文件的结果为3次测试的tick差值的平均值。可以看出,当使用flush或者endl/ends刷缓冲区时,输出到终端和文件性能差异并不大,但使用‘\\n’时,仅有换行,不刷缓冲区,输出到文件性能上提升了30多倍。
优化代码:
void functest(std::ostream &os) os << "xxxxx" << "\\n"; while(flag) os << "["; os << "xxx" << "yyy" << "\\n"; os << "zzz"; os << "]" << "\\n";
os << "xxxxx" << endl; // 最后确保函数走完输出到文件或者由调用者flush或者触发自动刷缓冲区条件 |
Trace分析:
打印代码 |
while(1) fout << "1" << endl;
|
13:46:27.638892 write(3, "1\\n", 2) = 2 <0.000012> 13:46:27.638933 write(3, "1\\n", 2) = 2 <0.000012> 13:46:27.638974 write(3, "1\\n", 2) = 2 <0.000013> 13:46:27.639015 write(3, "1\\n", 2) = 2 <0.000013> |
while(1) fout << "1\\n";
|
13:50:25.713263 writev(3, ["1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n"..., 8190, "1\\n", 2], 2) = 8192 <0.000019> 13:50:25.713499 writev(3, ["1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n"..., 8190, "1\\n", 2], 2) = 8192 <0.000019> 13:50:25.713735 writev(3, ["1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n"..., 8190, "1\\n", 2], 2) = 8192 <0.000019> 13:50:25.713971 writev(3, ["1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n1\\n"..., 8190, "1\\n", 2], 2) = 8192 <0.000018> |
优化效果分析:
针对XX情况,一共有YY个字段信息,即最少YY个endl,通过该方式优化后,极大减少了缓冲区刷新次数,性能较大提升。
以上是关于打印性能优化[缓冲模式角度]的主要内容,如果未能解决你的问题,请参考以下文章