簡體   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