如何让glog性能提高10倍

简介: 优化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),结构如图所示:
screenshot
每个生产者线程都有独自的无锁队列,生产者线程做日志的序列化处理等,整个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等关键指针做引用计数处理,避免大粒度的锁。
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
5月前
|
算法 编译器
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
249 0
|
6月前
|
SQL 测试技术 Apache
多场景下 3-11 倍性能提升,Apache Doris 1.2 新版本性能揭秘!
多场景下 3-11 倍性能提升,Apache Doris 1.2 新版本性能揭秘!
73 0
|
10月前
|
缓存 编译器 C++
C/C++性能提升之cache分析
C/C++性能提升之cache分析
241 0
|
12月前
|
SQL 存储 缓存
原来count(*)就是我们系统的接口性能变差100倍的真凶…
原来count(*)就是我们系统的接口性能变差100倍的真凶…
|
存储 固态存储 算法
如何快速debug定位SSD延迟问题?
IO延迟分析是一项复杂而有趣的工程,需要带着好奇深挖每一个信息,总会有不同的风景。
EMQ
|
缓存 运维 Kubernetes
5.0 版本持续优化:ExProto 吞吐性能提升
九月,EMQX 5.0保持稳定更新,目前已发布5.0.8版本,企业版4.3&4.4发布最新维护版本。云服务方面,EMQX Cloud新增1000连接规格的专业版部署。
EMQ
221 0
5.0 版本持续优化:ExProto 吞吐性能提升
|
缓存 前端开发 Java
是什么让一段20行代码的性能提升了10倍
性能优化显而易见的好处是能够节约机器资源。如果一个有2000台服务器的应用,整体性能提升了10%,理论上来说,就相当于节省了200台的机器。除了节省机器资源外,性能好的应用相对于性能差的应用,在应对流量突增时更不容易达到机器的性能瓶颈,在同样流量场景下进行机器扩容时,也只需要更少的机器,从而能够更快的完成扩容、应急操作。所以,性能好的应用相对于性能差的应用在稳定性方面也更胜一筹。
是什么让一段20行代码的性能提升了10倍
|
存储 缓存 Java
项目中用了 Disruptor 之后,性能提升了2.5倍
存储设备往往是速度越快价格越昂贵,速度越快价格越低廉。在计算机中,CPU 的速度远高于主存的速度,而主存的速度又远高于磁盘的速度。为了解决不同存储部件的速度不对等问题,让高速设备充分发挥性能,引入了多级缓存机制。
项目中用了 Disruptor 之后,性能提升了2.5倍
|
存储 消息中间件 机器学习/深度学习
硬件成本降低90%,性能提升20倍,异构内存show给你看!
硬件成本降低90%,性能提升20倍,异构内存show给你看!
357 0
硬件成本降低90%,性能提升20倍,异构内存show给你看!
|
存储 缓存 Java
自从项目用了 Disruptor,性能提升了 2.5 倍!
自从项目用了 Disruptor,性能提升了 2.5 倍!
226 0
自从项目用了 Disruptor,性能提升了 2.5 倍!