繁体   English   中英

为什么Windows中重复的内存分配速度变慢?

[英]Why does repeated memory allocation in windows slow down?

我想了解为什么以下代码在我的linux和Windows 7机器上的行为不同:在linux上,每次迭代大约需要120ms。 在Windows 7上,第一次迭代需要0.4秒,而后续迭代要花费更长的时间。 迭代8大约需要11秒,迭代22大约需要1分钟。

我在不同的硬件上观察到了这种行为。 它似乎与Windows有关。

#include <iostream>
#include <time.h>
#include <chrono>

void iteration() {
  int n = 25000;
  // Allocate memory
  long** blocks = new long*[n];
  for( int i = 0; i<n; ++i )
  {
    blocks[i] = new long[100008];
  }
  // Free all allocated memory
  for( int i = 0; i<n; ++i )
  {
    delete[] blocks[i];
  }
  delete[] blocks;
}

int main(int argc, char **argv) {
  int nbIter = 30;
  for( int i = 0; i < nbIter; ++i )
  {
    auto start = std::chrono::system_clock::now();
    iteration();
    auto end = std::chrono::system_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "Iteration #" << i << ": time=" << elapsed.count() << "ms" << std::endl;
  }
  return 0;
}

谁能告诉我这里发生了什么,以及如何使代码在Windows上稳定运行?

编辑:我在Windows上的VS2013中做了一个发布版本,我从VS外部执行了程序。 这是一些更精确的运行时间(以秒为单位):

Iteration #0: time=0.381000
Iteration #1: time=0.391000
Iteration #2: time=0.451000
Iteration #3: time=1.507000
Iteration #4: time=1.711000
Iteration #5: time=2.938000
Iteration #6: time=4.770000
Iteration #7: time=7.840000
Iteration #8: time=10.563000
Iteration #9: time=14.606000
Iteration #10: time=20.732000
Iteration #11: time=24.948000
Iteration #12: time=30.255000
Iteration #13: time=34.608000
Iteration #14: time=38.114000
Iteration #15: time=43.217000
Iteration #16: time=39.926000
Iteration #17: time=43.506000
Iteration #18: time=43.660000
Iteration #19: time=45.291000
Iteration #20: time=50.003000

围绕引用有关在Windows(以及一些 信息安全 的文章就可以了),大概有一些花絮常见的堆缓慢起伏里面写明了几个有

  • 由于分配操作而变慢。
  • 自由操作导致速度降低。
  • 由于频繁分配和重新分配而导致速度降低。

这确实有助于解释为什么速度变慢(即频繁的分配和重新分配),但并不能真正解释为什么速度变慢。

首先要注意的是sizeof(long) != sizeof(long) ,也就是说,在我使用g++和Visual Studio 12对64位版本进行的测试中,Windows上的sizeof(long)4或更高。 Linux是8 这是分配/取消分配内存时的重要注意事项。 如果将代码从long类型更改为sizeof(T) == 8 (如long long ),那么问题就消失了,并且各个迭代的时间是一致的。 例:

void iteration() {
    int n = 25000;
    // Allocate memory
    long long** blocks = new long long*[n];
    for (int i = 0; i < n; ++i) {
        blocks[i] = new long long[100008];
    }
    // Free all allocated memory
    for (int i = 0; i < n; ++i) {
        delete[] blocks[i];
    }
    delete[] blocks;
}
// sizeof(long long) == 8 on my Linux/Unix and Windows 64-bit machines

还应当指出的是,计时问题会消失只有正是 这种代码。

如果您保留long long的类型但将100008调整为16666 ,则问题再次出现。 此外,如果你把它改成16668 ,跑了long long的迭代紧接着的long版本,定时会去弥补long long的功能,再往下long ,例如:

template < typename T >
void iteration() {
    int n = 25000;
    // Allocate memory
    T** blocks = new T*[n];
    for (int i = 0; i < n; ++i) {
        blocks[i] = new T[16668];
    }
    // Free all allocated memory
    for (int i = 0; i < n; ++i) {
        delete[] blocks[i];
    }
    delete[] blocks;
}

for (int i = 0; i < nbItrs; ++i) {
    iteration<long long>(); // time goes UP
}
for (int i = 0; i < nbItrs; ++i) {
    iteration<long>(); // time goes DOWN
}

另外,由于new / malloc (在Windows上)调用HeapAlloc ,因此发布的代码使用malloc / freeLocalAlloc / LocalFree和/或HeapAlloc / HeapFree产生相似的结果。 原因与Windows如何管理其堆内存以及释放的内存的位置有关。 当必须删除页面时,需要对空闲阻止列表进行清理,并且可能需要相应地调整列表。

在搜索以及从列表中替换或删除旧内存块期间,此调整可能会花费一些时间。 如果块没有落在干净的边界上,则可能需要对可用堆块列表进行其他调整。

深入探讨Windows堆管理的方式和原因将涉及解释Windows内核的设计及其内存管理。 进入该主题将超出此问题/答案的范围,但是,我上面链接的某些 文章具有很好的概述,并且很好地解释了如何以及为什么进行。

但是,你确实问过

如何使代码在Windows上稳定运行?

如上所述,更改类型将允许更一致的时间安排,此外,如另一个答案所述 ,以相反的顺序删除列表也将获得更一致的时间安排;

for (int i = n; i > 0; --i )
{
    delete[] blocks[i-1];
}

这是由于Windows内存管理器使用单链接列表来维护堆位置这一事实,因此,为什么要遍历列表, deletedelete时间会增加,以及为什么Windows上的时间比Linux上的时间慢(尽管我的测试在进行这些更改时实际上产生了相似的时间)。

希望能对您有所帮助。

有趣的问题。 我能够繁殖。

我得到了一致的结果-尽管仍然有些迟钝-通过delete[]性能-以相反的顺序分配块:

for( int i = 0; i<n; ++i )
    delete[] blocks[n - 1 - i];

我怀疑这可能与合并开销有关-从MSDN此处

自由操作导致速度降低。 自由操作会消耗更多的周期,主要是在启用合并的情况下。 在合并期间,每个空闲操作应“找到”其邻居,将其拉出以构造更大的块,然后将更大的块重新插入到空闲列表中。 在此查找期间,内存可能会以随机顺序被触摸,从而导致缓存未命中和性能下降。

尽管有一些奇怪的事情:

  • 我的测量结果显示,尽管delete[]在第一次或三次迭代中花费了大约80%的时间,但在又六次new[]上花费了几乎相同的时间。

  • 当我new long[91134]升级到... 91135 ,问题突然出现了,这几乎是356kb,但是我没有设法用Google搜索任何相关内容。

非常有趣的问题。 我无法在Windows 10上使用MS Visual Studio Community 2013复制它,但是,如果您要分配固定大小的内存块,则可以尝试使用固定大小的内存块分配算法(也称为内存)替换新的/删除的内存块。池。 它以更快的速度和恒定的速度工作。 在这里,您可以找到基于BSD许可证的示例: https : //github.com/intelmm/FixMemAlloc/blob/master/Sources/MemoryPool.h 也许可以帮上忙。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM