【性能优化点滴】odygrd/quill 中一个简单的标记位作用--降低 IO 次数

在 StreamSink 类中,成员变量 _write_occurred 的作用是 跟踪自上次刷新(Flush)以来是否有写入操作发生 ,其核心目的是 优化 I/O 性能。以下是详细解析:


_write_occurred 的作用

1. 避免不必要的刷新(Flush)
  • 机制 :
    当 write_log 方法(日志写入)被调用时,_write_occurred 会被设为 true;
    当 flush_sink 方法(主动刷新)被调用时,仅在 _write_occurred 为 true 时执行实际的 fflush 操作,随后将其重置为 false。
  • 优化意义 :
    避免在无新日志写入时频繁调用 fflush(系统调用),减少 I/O 开销。
2. 状态跟踪
  • 写入标记 :
    记录自上次刷新后是否有新数据写入缓冲区。若未写入,跳过刷新操作。
  • 示例场景 :
    若连续多次调用 flush_sink 但无新日志写入,只有第一次会真正执行 fflush。

代码流程解析

写入日志时
cpp 复制代码
void write_log(..., std::string_view log_statement) override {
  // ... 写入日志到文件
  safe_fwrite(...); // 实际写入操作
  _write_occurred = true; // 标记有写入发生
}
  • 写入后必标记 :无论日志内容是否为空,只要调用 write_log 即标记为 true。
主动刷新时
cpp 复制代码
void flush_sink() override {
  if (!_write_occurred || !_file) { 
    return; // 无写入或文件未打开时直接返回
  }
  flush(); // 执行实际刷新
}

void flush() {
  _write_occurred = false; // 重置标记
  fflush(_file); // 系统调用刷新缓冲区
}
  • 条件刷新 :仅在确实有写入时调用 fflush,避免无意义刷新。

设计必要性

性能优化
场景 无 _write_occurred 有 _write_occurred
高频写入 每次 flush 触发 fflush,I/O 压力大 仅在必要时触发 fflush,减少系统调用
低频写入 多余的 fflush 浪费 CPU 周期 无写入时跳过 fflush,节省资源
资源保护
  • 文件句柄有效性 :
    若文件被意外关闭(如 _file 变为 nullptr),flush_sink 会直接返回,避免操作无效指针。

典型应用场景

1. 日志批量写入
cpp 复制代码
// 连续写入多条日志
sink.write_log(...); // _write_occurred = true
sink.write_log(...); // _write_occurred = true
sink.flush_sink();   // 实际刷新,重置 _write_occurred = false
sink.flush_sink();   // 直接返回(无新写入)
2. 定时刷新策略
cpp 复制代码
// 定时器每 1 秒触发刷新
void on_timer() {
  sink.flush_sink(); // 仅当 1 秒内有新写入时刷新
}

扩展:fflush 的成本

  • 系统调用开销 :
    fflush 会触发用户态到内核态的上下文切换,频繁调用可能成为性能瓶颈。
  • 磁盘 I/O 延迟 :
    强制将缓冲区数据写入磁盘,涉及物理写操作,延迟较高。
  • 优化平衡 :
    通过 _write_occurred 减少 fflush 次数,在 数据安全性 (及时持久化)和 性能 之间取得平衡。

总结

_write_occurred 是一个轻量级的状态标志,通过简单的布尔值跟踪写入状态,实现了:

  1. 减少冗余 I/O 操作:避免无数据时的无效刷新。
  2. 降低系统调用开销:提升高吞吐场景下的性能。
  3. 保护资源:防止操作无效文件指针。

它是高性能日志库中常见的优化手段之一,尤其适用于需要平衡实时性和吞吐量的场景。

感触:对于一些好的三方库,还是有很多值得我们学习借鉴的知识点的,需要我们多想多思考多问一个为什么


【技术人的鼓励】❤️ 如果这篇文章对您有帮助,欢迎点击打赏按钮支持博主!您的鼓励是我持续输出优质技术内容的动力,哪怕只是1元也足以让我感受到这份珍贵的认可。💰

相关推荐
Flynt1 小时前
我把微信运动的排行榜和定点推送复刻了一遍:100万次步数更新只要4秒,坑在别处
redis·后端·性能优化
wuyk5552 小时前
防逆向基础:在量产固件中关闭调试接口并设置读保护(RDP)的正确姿势
stm32·单片机·嵌入式硬件·性能优化·重构
打工仔折腾 AI3 小时前
在 Ubuntu ARM64 部署 Q1 V3.0 MuJoCo 模型:加载、单步推理与执行器响应解析
linux·运维·人工智能·python·深度学习·ubuntu·性能优化
choumin3 小时前
C++中万能引用与std::forward的作用和用法
c++·万能引用·std forward
下一站春风里4 小时前
位运算完整学习笔记:按位与(&)+ 异或(^)
开发语言·c++·算法
努力努力再努力wz7 小时前
【C++高阶系列】C++20 协程底层原理:函数为什么能暂停?从状态机到 Task,彻底串起挂起、恢复与协程调度
开发语言·c++·c++20·1024程序员节
划破黑暗的第一缕曙光7 小时前
[C++]:4.模板
c++·template·模板·函数模版·类模版
十五年专注C++开发7 小时前
pybind11:一个跨C++和Python平台的编程库
开发语言·c++·python·pybind11
CodeYp20197 小时前
第11讲:stack 和 queue
开发语言·c++·queue·stack
沫璃染墨8 小时前
《Qt从零入门系列(十五):Qt线程安全与锁机制详解——从QMutex到RAII与读写锁实战》
开发语言·c++·qt·安全·交互·线程·qt6.3