被vector動態擴容給坑了!

東北碼農發表於2022-03-12

大家好,我是東北碼農。記錄一下工作中事。

前幾天,運維同事給我反饋了一個問題:
通過監控發現,線上的一個服務,業務執行緒時不時會出現卡頓,卡頓大約持續幾秒。

我們做金融系統後臺開發的,對效能要求很嚴格的,當然要查一查啦。

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

東北碼農,全網同名,歡迎大家使用常用聊天軟體關注、評論交流~
如果大家覺得有用,求點贊、轉發~
謝謝你~

相關文章