• 被vector动态扩容给坑了!


    大家好,我是东北码农。记录一下工作中事。

    前几天,运维同事给我反馈了一个问题:
    通过监控发现,线上的一个服务,业务线程时不时会出现卡顿,卡顿大约持续几秒。

    我们做金融系统后台开发的,对性能要求很严格的,当然要查一查啦。

    1、问题调查

    1.1、top 日志

    top中有内存和cpu信息,可以判断出卡顿时内存使用暴涨,但cpu使用没有下降。
    应该是做了非常耗cpu和内存的操作,而不是等待什么。

    1.2、代码分析

    通过top的分析,结合代码分析,发现业务代码中有vector操作,每收到一个包都会建立索引。伪代码如下

    vector<uint64_t> 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<uint64_t> 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

    东北码农,全网同名,欢迎大家使用常用聊天软件关注、评论交流~
    如果大家觉得有用,求点赞、转发~
    谢谢你~

  • 相关阅读:
    bellman-ford 单源最短路问题 图解
    单源最短路径 dijkstra
    【Jquery】-------JS实现关键字检索html内容,符合关键字的匹配项,进行标注背景色,可进行上一个,下一个切换定位
    在安卓手机上搭建一台微型服务器
    【C++进阶】:智能指针
    【go】报错整理与解决
    Java反射机制
    Mojo:为Web应用程序提供了完整的框架,包括路由、模板、插件、Websocket、HTTP客户端、HTTP服务器、配置文件管理等功能
    Navicat操作数据库与Mysql常见命令操作实战
    C++知识篇--强制类型转换
  • 原文地址:https://www.cnblogs.com/northeast-coder/p/15999054.html