STL中vector的内存分配原则是分配为2倍的,为什么是2倍呢,让我们来分析一下。
其实分配内存就是在时间和空间上做一个平衡,如果每次增加的内存很少,那么在空间上浪费的就要少一点,但是时间耗费的要多。如果每次增加的内存很多,那么重新分配的次数就要少一点,时间耗费的也要少一点,但是浪费的空间就要多一点。可能有的人对空间浪费的说法不太清除,我在这里简单说明一下。假设每次增加内存时都以常数c增加,那么你最多浪费c-1个内存空间。假设每次重新分配时以2倍的策略分配,假设某个时刻vector的容量为n,那么当数据增加到n+1时,vector需要重新分配2n的空间,此时你浪费的空间就是n-1,随着分配的次数越多,浪费的空间也就越大。
当vector需要重新分配内存时,它要做以下几步:
(1)首先要分配一个新的内存
(2)把旧内存的数据拷贝到新内存中
(3)销毁旧内存中的数据
(4)释放旧内存
1.首先我们假设每次分配内存都以常数C为增量分配
设我们现在有nc个数据要放到vector中,假设vector初始容量为c,当vector的容量为2c时拷贝了c个数据到新内存中,vector的容量为3c时,要拷贝原来2c个数据到新内存中。依次类推,当vector的容量为nc时,一共拷贝了c+2c+.....+nc。假设拷贝一个数据的时间复杂度为常数,那么vector的容量增加到nc时需要的时间复杂度是O(n^2)。这种方法太耗时了。
2.假设我们每次分配都以倍数k来分配,然后我们再看一下情况会是怎样
上面图片的意思是当前大小为n时,前一次vector的大小为n/k,当vector的容量为n/k时,前一次vector大大小为n/k^2。正方形的数字1,2代表这段内存的数据搬移的次数。所以采用倍数为k的策略分配内存的时间复杂度为(k-1)n / k + n/k + n/k^2 +..... + 1 = O(n)。这样看来时间复杂度降低了。STL就是把k设为了2。
3.比较好的k的选取
其实k最好取 1<k<2 中的数,因为这样的话分配若干次后就可以重新使用之前使用过的堆内存(如果取大于等于2的话,那么每一次需要的内存都比前面加起来的多),不要再去分配内存了(个人感觉不一定每次都能重用之前的空间,只是有可能)。让我们看个VS中的情况,VS是把k设为了1.5。下面我们看一下堆中的内存分配情况,数字1,2等代表第几次分配(可能内存分配的大小与VS不太相同,但是不影响表达意思),其中0只是又来对其数字的。
1. 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2. 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0
3. 0 0 0 1 2 3 0 0 0 0 0 0 0 0 0
4. 1 2 3 4 0 0 0 0 0 0 0 0 0 0 0
参考文章:
https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md
http://www.drdobbs.com/c-made-easier-how-vectors-grow/184401375