C++ 动态分配是否将数据存储在堆中的随机位置?

C++ 动态分配是否将数据存储在堆中的随机位置?,c++,caching,heap,dynamic-memory-allocation,C++,Caching,Heap,Dynamic Memory Allocation,我知道局部变量将有序地存储在堆栈上 但是,当我在C++中堆内存中动态分配变量时,类似于这个。 int * a = new int{1}; int * a2 = new int{2}; int * a3 = new int{3}; int * a4 = new int{4}; 问题1:这些变量是否存储在连续的内存位置? 问题2:如果不是,是因为动态分配将变量存储在堆内存中的随机位置吗? 问题3:那么动态分配是否会增加缓存未命中的可能性并具有较低的空间局部性?第1部分:单独的分配是否连续? 答案是

我知道局部变量将有序地存储在堆栈上

<>但是,当我在C++中堆内存中动态分配变量时,类似于这个。
int * a = new int{1};
int * a2 = new int{2};
int * a3 = new int{3};
int * a4 = new int{4};
问题1:这些变量是否存储在连续的内存位置? 问题2:如果不是,是因为动态分配将变量存储在堆内存中的随机位置吗? 问题3:那么动态分配是否会增加缓存未命中的可能性并具有较低的空间局部性?

第1部分:单独的分配是否连续? 答案是可能不是。动态分配如何发生取决于实现。如果像上面的例子那样分配内存,两个单独的分配可能是连续的,但不能保证会发生这种情况(而且永远不应该依赖于它)

<>不同的C++实现使用不同的算法来决定内存分配的方式。p> 第二部分:分配是随机的吗? 有点;但并非完全如此。内存并不是故意以随机方式分配的。通常情况下,内存分配器会尝试分配彼此相邻的内存块,以最小化页面错误和缓存未命中,但这并不总是可能做到

分配分两个阶段进行:

  • 分配器从操作系统请求一大块内存
  • 每次调用new时,都会从大数据块中提取一部分并返回它们,直到您请求的内存超过它必须提供的内存,在这种情况下,它会从操作系统中请求另一个大数据块
  • 第二个阶段是实现可以尝试为您提供接近其他最近分配的内存,但是它对第一个阶段几乎没有控制权(操作系统通常只提供可用的内存,而不知道程序的其他分配)

    第3部分:避免缓存未命中 如果缓存未命中是代码中的瓶颈

    • 尝试减少间接寻址的数量(通过让数组按值而不是按指针存储对象)
    • 确保您正在操作的内存在设计允许的范围内是连续的(因此使用std::array或std::vector,而不是链表,并且更喜欢一些大的分配,而不是许多小的分配);及
    • 尝试设计算法,使其在内存中的跳转次数尽可能少
    一个好的一般原则是只使用对象的std::vector,除非您有很好的理由使用更高级的对象。因为它们具有更好的缓存位置,所以std::vector在插入和删除元素方面比std::list更快,甚至可以插入几十个甚至数百个元素

    最后:尝试利用堆栈。除非有充分的理由将某个对象作为指针,否则只需声明为存在于堆栈上的变量即可。如果可能的话

    • 更喜欢使用
      MyClass x{}
      而不是
      MyClass*x=newmyclass{},以及
    • 首选
      std::vector
      而不是
      std::vector

    通过扩展,如果您可以使用静态多态性(即模板),请使用该多态性而不是动态多态性

    < P> iMHO:这是操作系统特定的/C++标准库实现。
    new
    最终使用较低级别的虚拟内存分配服务,并使用诸如mmap和munmap之类的系统调用一次分配多个页面。
    new
    的实现可以在相关的时候重用以前释放的内存空间。 新
    的实施可以对“大”和“小”分配使用各种不同的策略

    在您给出的第一个
    new
    结果的示例中,系统调用内存分配(通常是几页),分配的内存可能足够大,以便后续
    new
    调用会导致连续分配。但这取决于实现

  • 一点也不(由于对齐、堆管理数据、分配的块可能被重用等原因而存在填充)

  • 一点也不(好吧,堆算法是确定性的,没有任何随机性)

  • 通常是(例如,内存池在这里可能会有所帮助)


  • 我甚至不确定局部变量是否需要存储在堆栈上和/或有序存储。我认为没有这样的保证。局部变量有时存储在堆栈上,有时存储在cpu寄存器中,有时完全优化(编译器可能非常聪明)。当它们存储在堆栈上时,它们可能是按顺序存储的,但是有空间填充以保持内存对齐,并且在表达式的上下文中,临时变量也可能在该表达式的持续时间内占用堆栈上的空间。