随手记——使用内存池遇到的性能下降问题

Posted 穿越临界点

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了随手记——使用内存池遇到的性能下降问题相关的知识,希望对你有一定的参考价值。

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。


使用内存池为的就是提升性能,怎么还会导致性能下降呢?是题主自己用法有问题吧?——好吧,确实是。

下面,我们一起来看看是什么问题。

1 问题引入

问题是从业务侧发现的,发现业务处理不过来,因为每隔500us都要处理一次。后来通过时间打点发现是驱动侧的问题,有一个内存申请的函数运行时间偶现会比较长。驱动侧的线程(Ta)功能就是每接收到一个500us中断,就开辟一段内存(用来存储消息)给业务侧的线程(Tx)发送消息。

2 问题分析定位

继续跟踪该内存申请函数。该函数是从内存池中申请指定大小的内存。由于内存池中维护了两个链表:两个链表:一个链表将使用中的内存块串起来;另一个链表将使用释放后(空闲中)的内存块串起来(详见我之前写的另外一篇文章:内存池耗尽问题分析和定位)。所以,在申请和释放隶属于某个内存池的内存块时,需要给这两个链表加锁。

由于使用了锁,那么性能可能就会受到影响。所以,我们的思路自然而然是怀疑申请内存时使用了锁导致的性能偶现下降。

为了坐实这个猜想,我们抓了一下任务切换的情况,如下图所示。

图中每一根水平线代表一个线程,水平轴是时间轴,黄色线的水平移动代表时间流动,垂直跳变代表任务切换。

我们可以看到图中用红框框住的部分,线程Ta运行了一段时间后切换到了Tc上,然后过了一段时间又切换回了Ta。但是在我们的优先级设置中Ta的优先级是高于Tc的,由此我们可以推断出Ta是由于等待锁而被阻塞了,从而造成了运行时间延长。

如何避免这个问题呢?

3 问题解决

3.1 使用专属内存池

上述问题的矛盾点就是在使用同一内存池时造成的竞态问题。虽说影响性也就是几个微秒,但是对于强实时性来说还是不能容忍的。因此,解决资源竞争问题的最好办法就是 专属 。也就是给Ta线程分配专用的内存池,没人和他抢,自然就没有竞态发生。

这是一种 以空间换时间 的思维。

3.2 只申请不释放/使用静态内存

使用内存池时只申请不释放就相当于在使用静态内存了,这样,速度当然就更快了。

但是还要有一个权衡。如果接收Ta线程消息的Tx线程是固定数目的,也就是说生命周期固定,那就代表需要开辟的内存是固定数量和大小的,可以选取使用静态内存方式来替代动态申请方式;或者只申请一次,后续就不再释放了。因为一直要用的,频繁占用的内存干嘛还总是申请释放,申请释放的,这不是自己找不痛快么?

Tips:对于只申请不释放的方式使用内存需要十分注意的一点是——只能申请一次,否则就很容易造成内存泄露问题。

4 复盘

  1. 对性能要求极高的线程,可以给其分配专属内存池,不要和其他线程共用同一个内存池。
  2. 对于数目和大小固定的内存使用,可以选取只申请一次,后续不再释放的方式来提升性能。

恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

以上是关于随手记——使用内存池遇到的性能下降问题的主要内容,如果未能解决你的问题,请参考以下文章

随手记——使用GDB定位内存越界问题

0523- 人员定位系统问题随手记

随手记开发中遇到的问题与解决方案,持续更新...

随手记一下,VS2015卡顿问题解决。

随手记——静态Flag变量使用不规范导致的重大流程错误

2019-3-19记随手记面试