被vector动态扩容给坑了!

大家好,我是东北码农。记录一下工作中事。
前几天,运维同事给我反馈了一个问题:
通过监控发现,线上的一个服务,业务线程时不时会出现卡顿,卡顿大约持续几秒。
我们做金融系统后台开发的,对性能要求很严格的,当然要查一查啦。
1、问题调查 1.1、top 日志 top中有内存和cpu信息,可以判断出卡顿时内存使用暴涨,但cpu使用没有下降。
应该是做了非常耗cpu和内存的操作,而不是等待什么。
1.2、代码分析 【被vector动态扩容给坑了!】通过top的分析,结合代码分析,发现业务代码中有vector操作,每收到一个包都会建立索引。伪代码如下

vector idx_; void on_recv(pkg *h) { idx_.push_back(h->seq); }

应该是vector动态扩容时,造成的卡顿。下面来验证一下。
2、vector std::vector is a sequence container that encapsulates dynamic size arrays. vector的底层实现是数组,在使用时采取动态扩容方式。
2.1、vector的size和capacity vector有size和capacity两个属性,size是实际数量,capacity是容器当前容量。下面是cppreference中的解释:
  • size:returns the number of elements。
  • capacity:returns the number of elements that can be held in currently allocated storage。
2.2、扩容 下面写代码,观察一下vector何时会扩容,以及扩容时的开销。伪代码如下:在扩容时打印容量变化,以及耗时。
void test_vector() { vector idxs; uint64_t last_cap = 0; for(int i = 0 ; i < 1200000000; i++) { auto begin = get_ns(); idxs.push_back(i); auto cost = get_ns() - begin; auto cap = idxs.capacity(); if(last_cap != cap || cost > 100*1000*1000) { printf("last_cap=%ju,cap=%ju,cost=%ju\n",last_cap,cap,cost/1000000); last_cap = cap; } } }

输出如下:
last_cap=0,cap=1,cost=0 last_cap=1,cap=2,cost=0 last_cap=2,cap=4,cost=0 last_cap=4,cap=8,cost=0 last_cap=8,cap=16,cost=0 last_cap=16,cap=32,cost=0 last_cap=32,cap=64,cost=0 last_cap=64,cap=128,cost=0 last_cap=128,cap=256,cost=0 last_cap=256,cap=512,cost=0 last_cap=512,cap=1024,cost=0 last_cap=1024,cap=2048,cost=0 last_cap=2048,cap=4096,cost=0 last_cap=4096,cap=8192,cost=0 last_cap=8192,cap=16384,cost=0 last_cap=16384,cap=32768,cost=0 last_cap=32768,cap=65536,cost=0 last_cap=65536,cap=131072,cost=0 last_cap=131072,cap=262144,cost=0 last_cap=262144,cap=524288,cost=1 last_cap=524288,cap=1048576,cost=1 last_cap=1048576,cap=2097152,cost=3 last_cap=2097152,cap=4194304,cost=6 last_cap=4194304,cap=8388608,cost=13 last_cap=8388608,cap=16777216,cost=25 last_cap=16777216,cap=33554432,cost=51 last_cap=33554432,cap=67108864,cost=102 last_cap=67108864,cap=134217728,cost=204 last_cap=134217728,cap=268435456,cost=407 last_cap=268435456,cap=536870912,cost=818 last_cap=536870912,cap=1073741824,cost=1633 last_cap=1073741824,cap=2147483648,cost=3372

可以看出,vector在2的n次幂都会扩容。重要节点是
  • capacity :1.3亿~2.6亿-时间约400ms
  • capacity :2.6亿~5.3亿-时间约800ms
  • capacity :5.3亿~10.7亿-时间约1600ms
3、问题解决 问题找到了,解决就容易了。
vector有reserve接口,可以在程序启动时就扩容好,防止运行时动态扩容造成卡顿。
其实程序在启动时,已经调用reserve提前扩容了,但是近期国际局势不稳定,造成订单量飙升,所以reserve的量不足,需要重新估计一下。
  • reserve:reserves storage
东北码农,全网同名,欢迎大家使用常用聊天软件关注、评论交流~
如果大家觉得有用,求点赞、转发~
谢谢你~

    推荐阅读