摘要: 优化glog源码,性能提高10倍
背景
最近在给glog做性能优化, 使用c++版本 glog-0.3.4做压测,测试数据总量为1.5g, 起12个线程循环写133个字节的日志条目,测试结果耗时175s,每秒大约8-9MB的吞吐量。
在此测试基础上,我对glog进行了一系列的性能优化,优化后耗时16s,性能为glog原生版本的10倍。
优化过程
去localtime函数调用
查看glog源码,在获取日期的时候使用了localtime, localtime_r这两个函数,而这两个函数调用了__tz_convert, __tz_convert有tzset_lock全局锁,每次获取时间都会使用到kernel级别的futex锁,所以优化第一步是去掉glibc的localtime函数,使用getimeofday获取秒数和时区,用纯耗cpu的方式算出日期,稍微复杂一点的计算就是闰年闰月的转换。将这段函数替换后,耗时从175s减少成46s,性能瞬间提高4-5倍。
减少锁粒度
再翻看glog的源码,glog是一个多线程同步写的操作,简化代码就是 lock();dosomething();fwrite();unlock(); fwrite本身就是线程安全的,缩小锁粒度需要改成lock();dosomething();unlock();fwrite(); 其他变量都比较好处理,比如文件名之类的,不好处理的是轮转的时候会更改fd, fwrite()会使用到fd。我使用了指针托管和引用计数的办法,当轮转文件时,将current_fd_ 赋值给old_fd_, 不直接delete或fclose, 简化代码等于:lock();dosomething();if(true) old_fd_ = current_fd_; currnt_fd_.incr();unlock();fwrite();currnt_fd_.decr(); 当old_fd_ = 0时,才会真正delete 和fclose 这个fd指针。优化后压测耗时30s。
引入无锁队列异步IO化
从第二次优化来看。锁热点已经很少了,性能也有不少提升,已经能满足OCS的需求,但是这种多线程同步堵塞写io的模式,一旦出现io hang住的情况,所有worker线程都会堵住。可以看下__IO_fwrite 这个函数,在写之前会进行__IO_acquire_lock() 锁住,写完后解锁。
为了避免所有线程卡住的情况,需要将多线程同步堵塞转换成单线程异步的io操作,同时避免引入新的锁消耗性能,所以引入无锁队列,算法复杂度为O(1),结构如图所示:
每个生产者线程都有独自的无锁队列,生产者线程做日志的序列化处理等,整个glog有一个单线程的消费线程,消费线程只处理真正的io请求,无锁队列使用环形数组实现,引入tcmalloc做内存管理。消费线程也会有hang住的可能,因为无锁队列使用CAS,当队列满了的时候并不会无限增长内存,而是会重试几次后放弃本次操作,避免内存暴涨。改造后耗时33s。
小细节优化
glog在linux系统下缺省使用的是pthread_rw_lock,在第二步减少锁粒度的基础上,现已不需要内核态的读写锁,所以将rwlock替换成用户态的spinlock。另外__GI_fwrite的热点还是有一些,采用合并队列的方法减少一些写操作,再加上超时机制,防止缓存的日志不及时落地。总结起来的优化就是:
- 向前合并队列写
- glog缺省使用的读写锁和mutex锁,换成spinlock
- 单条message buffer大小调整
- fwrite设置file buffer
这些优化完成后耗时时间为16s。
使用场景
优化后的glog版本适合使用在需要高日志吞吐量的产品, 比如OCS这种分布式高并发高吞吐量的系统。
高性能日志系统总结
从以上优化可以总结出高性能的日志系统的特性:
- 使用异步IO实现高并发的日志吞吐量,日志线程与worker线程解耦,worker线程只做序列化之类的工作,日志线程只做io,避免当磁盘满了等异常情况发生时主路径阻塞导致服务完全不可用,这在任何一个高并发的系统中都需要注意的。
- 其他细节点特性:
- 不使用localtime取日期,单测localtime和getimeofday 获取时间, gettimeofday 速度比localtime快20倍
- 选用无锁队列可重试放弃操作,避免内存暴涨。
- 使用内存池管理,比如tcmalloc
- 对fd等关键指针做引用计数处理,避免大粒度的锁。
- 展开全文
相关推荐
使用GLOG不仅可以提高代码的可读性和可维护性,还能为调试和问题排查提供有力的支持。 了解和掌握GLOG的使用方法,对于C++开发者来说,是提升开发效率和软件质量的重要一步。在项目中引入GLOG,可以使得日志管理...
7. **性能优化**:GLOG在设计时考虑了效率,只有达到预设的严重性级别时才会真正写入日志,避免了不必要的性能开销。 8. **线程安全**:GLOG保证了多线程环境下的日志记录是线程安全的,无需用户额外处理同步问题。...
《glog:Linux环境下C++...总之,glog为C++开发者提供了一种强大、易用的日志解决方案,无论是在小型项目还是大型系统中,都能显著提高开发和维护的效率。了解并熟练运用glog,对于提升C++编程的生产力有着积极的作用。
例如,DEBUG级别的信息通常只在开发阶段使用,而在生产环境中可能会被屏蔽,以提高性能。 2. **堆栈回溯**:当发生FATAL级别的错误时,glog会自动打印出调用堆栈,这对于定位致命错误的来源非常有帮助。 3. **日志...
通过glog,ceres得以优雅地处理日志信息,提高了调试和问题排查的效率。了解并掌握glog的使用,对于任何使用ceres或类似工具的开发者来说都是极其重要的,这不仅可以提升工作效率,也有助于构建更健壮、更可维护的...
此外,glog还支持异步日志写入,通过后台线程将日志信息异步写入文件,避免了日志输出对主线程性能的影响。 在实际应用中,glog常被用于服务器端开发、系统监控和大规模分布式系统的日志管理。例如,在大型互联网...
为了简化日志管理,可以修改glog配置,让所有级别的日志都写入同一个文件。这可以通过设置`FLAGS_logtostderr = false`和`FLAGS_alsologtostderr = false`,以及自定义`LogSink`实现来完成。 5. **按日期创建新日志...
首先,让我们了解一下glog的基本概念。glog库的核心功能是提供方便、高效的日志输出,它允许开发者根据严重程度(如INFO, WARNING, ERROR, FATAL)记录日志,同时还提供了堆栈跟踪、时间戳、线程ID等附加信息。FATAL...
在IT行业中,日志记录是软件开发中的一个重要环节,它帮助开发者追踪程序运行状态、调试错误和优化性能。...通过包含glog的头文件,开发者可以轻松地集成glog日志库,提高软件的可维护性和问题排查效率。
这些级别允许开发者根据需要过滤日志信息,提高性能,同时确保关键错误能够被记录。 3. **崩溃处理** 当触发FATAL级别的日志时,Glog 不仅会记录错误信息,还会执行清理工作并终止程序,防止程序在错误状态下继续...
对于Apollo项目来说,glog提供的强大日志功能能够帮助开发人员更好地监控系统状态,快速定位和解决问题,从而提高自动驾驶系统的可靠性和稳定性。通过熟练掌握glog的使用,可以显著提升开发效率和软件质量。 总结来...
glog相对于其他日志库(如log4cpp、spdlog等)的优势在于其稳定性、性能以及与Google其他开源项目的兼容性。然而,对于一些小型项目或者不需要复杂日志处理的场景,其他轻量级日志库可能更合适,因为它们通常具有更...
3. **日志缓冲**:为了提高性能,可以采用日志缓冲机制,将多个日志条目暂存起来,批量写入磁盘或网络,减少I/O操作。 4. **日志格式化**:提供灵活的格式化选项,让用户可以定制日志的输出格式,包括时间戳、线程...
除了基本的日志记录,glog还提供了其他高级特性,如日志速率限制、延迟记录(批量发送日志以提高效率)、以及异步日志处理等。这些特性使得glog在处理大量日志数据时仍能保持高效和稳定。 总结来说,glog是Windows...
谷歌glog是一个广泛使用的日志库,特别是在C++编程中,它为开发者提供了一套强大的、灵活的日志处理工具。glog-0.3.5是该库的一个特定版本,...在实际项目中,理解并熟练使用glog,可以极大地提高开发效率和软件质量。
总的来说,日志库是软件开发中不可或缺的一部分,通过研究和使用Logger和glog,我们可以更好地理解和掌握如何在C++环境中有效地记录和管理日志,这对于提高软件质量和维护性至关重要。在深入源码的过程中,你还可以...
2. **日志级别**:Glog 支持多个日志级别,包括 `DEBUG`、`INFO`、`WARNING`、`ERROR` 和 `FATAL`,开发者可以根据需要选择不同级别的日志输出,方便调试和性能优化。 3. **助手宏**:Glog 提供了一系列助手宏,如 ...
2. 异步日志:启用异步日志记录可以提高性能,避免日志输出阻塞主线程。通过`google::EnableLog鼓泡();`和`google::FlushLogFiles(google::GLOG_INFO);`等函数可以控制异步日志的行为。 3. 日志文件配置:glog允许...