vector::reserve()似乎没有分配空间。

3
附加代码包含三个对std::vector :: assign()的调用。在第一次调用之前,调用std::vector :: reserve()来分配适当数量的内存,然后填充向量。第二次调用assign()在填充第二个向量之前不调用reserve(),因此我期望在此调用assign()期间至少会进行一次分配。第三次调用assign()用于使用新数据重新填充第一个向量。代码中的第四个块使用new分配一个int数组并填充它。
我期望第一、第三和第四个块的性能相等,而第二个块应该需要额外的时间,因为需要进行分配。但实际上我发现第一和第二个块花费相等的时间,而第三和第四个块运行速度显着更快(约30%)。我觉得reserve()的调用实际上没有分配所需空间,而是将分配推迟到调用assign()。
我在两个平台上进行了测试:使用Visual C ++ 2010编译的Windows Vista和使用gcc 4.3.2编译的SUSE Linux。我尝试了不同水平的优化(包括无优化),并继续看到相同的行为。有什么想法,为什么reserve()没有做我期望的事情?如果我看到的是正确的行为,那么reserve()的意义是什么?
谢谢, Josh
#include <vector>
#include <time.h>
#include <stdio.h>
#include <iostream>

int main()
{
  int     len=1E8, nloops=100;
  clock_t start, stop;
  double  wr=0.0, wor=0.0, wc=0.0, pb=0.0;

  printf("Number of values tested = %d\n", len);
  printf("Number of loops = %d\n", nloops);

  std::vector<int> vec1a, vec1b;
  for(int i=0; i<len; i++) vec1a.push_back(i);
  for(int i=len; i>0; i--) vec1b.push_back(i);

  for(int i=0; i<nloops; i++)
  {
    std::vector<int>  vec2, vec3;
    int              *vec4;

//First block
    vec2.reserve(len);
    start = clock();
    vec2.assign(vec1a.begin(), vec1a.end());
    stop = clock();
    wr += ((double)(stop-start))/((double)(CLOCKS_PER_SEC));

//Second block
    start = clock();
    vec3.assign(vec1a.begin(), vec1a.end());
    stop = clock();
    wor += ((double)(stop-start))/((double)(CLOCKS_PER_SEC));

//Third block
    vec2.clear();
    start = clock();
    vec2.assign(vec1b.begin(), vec1b.end());
    stop = clock();
    wc += ((double)(stop-start))/((double)(CLOCKS_PER_SEC));

//Fourth block
    start = clock();
    vec4 = new int[len];
    for(int j=0; j<len; j++) vec4[j] = vec1a[j];
    stop = clock();
    pb += ((double)(stop-start))/((double)(CLOCKS_PER_SEC));
    delete []vec4;
  }

  printf("With reserve()    = %10.4lE\n", wr);
  printf("Without reserve() = %10.4lE\n", wor);
  printf("With clear()      = %10.4lE\n", wc);
  printf("newed int array   = %10.4lE\n", pb);

  return 0;
}

我正在尝试对其进行分析,但它使用了超过250 MB的堆内存。为什么不尝试使用更少的元素但更多的循环呢? - baris.aydinoz
@sbi:^^这种事情发生在我们中的佼佼者身上 :P - rubenvb
我已经尝试了不同级别的优化(包括没有优化)。不过,我只尝试过发布版。 - Josh
@baris_a:对我来说,它的内存占用超过了1.5GB,但如果你的RAM不够大,为什么不直接改变“len”呢? - sbi
糟糕 - 标题错了,也在VC中。对此我感到抱歉! - Josh
2个回答

4
考虑到向量的源都在头文件中,您可以查找.assign的功能。在assign内部,它一次性分配所需数量的项。当您执行vec2.assign(vec1a.begin(), vec1a.end())时,模板足够聪明,知道它需要1E8。
如果您真的想测试它,可以通过每个项目进行迭代,并使用push_back而不是assign()。
附加说明:
只是为了澄清,在调用栈中使用.assign()计算新容量并调用_Grow_to(MSVC++10)。实际上,.assign()执行保留操作。

此外,vec2.clear() 不一定会释放任何内存,它可能只是将 size() 设置为 0 而不减少 capacity() - Ben Voigt
@Ben:如果我没记错的话,我甚至看到过一些相当可靠的论据表明 clear() 无法释放内存。它本来应该能够这样做,但是(记住,如果我没记错),在执行时可能会违反一些其他要求。 - Jerry Coffin
我的使用clear()是为了清空内容,但保证下一次尝试填充数据时内存已分配。 - Josh
@Josh:使用vec2.swap(std::vector<int>())来释放向量的容量。 - sbi
@sbi:正如你所说,使用vec2.swap(std::vector<int>())显示了与原始代码中块1和块2相同的结果。因此,这似乎表明调用reserve()没有起到任何作用。我是对的吗? - Josh
@Andrew:将赋值语句转换为push_back循环会显著提高性能,我之前肯定是吸了什么东西,以为这样做没有任何区别。感谢大家的帮助! - Josh

2

这并不能解释你的结果,但你可能会感兴趣知道,reservenew[]实际上并不分配内存。相反,它们只是在进程中分配地址空间,只有当它们被使用时,虚拟内存管理器才会分配实际的RAM页面(页面将逐个被分配,因此一个块的一部分可能被分配而另一部分则没有)。


有趣,我不知道这个。 - Josh
这解释了我的一部分结果:为什么第一次填充向量比第二次填充慢。感谢您的见解! - Josh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接