4

我正在研究一個由兩個併發線程組成的程序。其中一個(這裏是「時鐘」)正在定期進行一些計算(10Hz),並且內存密集。另一個(這裏是「hugeList」)使用更多的RAM,但不像第一個那麼重要。所以我決定把它的優先級降低到THREAD_PRIORITY_LOWEST。然而,當線程釋放大部分使用的內存時,關鍵線程並不能保持其時序。Windows7內存管理 - 如何防止併發線程阻塞

我能夠將問題壓縮到這一點代碼(確保優化已關閉!): while Clock嘗試保持10Hz計時hugeList-thread分配並釋放越來越多的未組織內存以任何類型的塊。

#include "stdafx.h" 
#include <stdio.h> 
#include <forward_list> 
#include <time.h> 
#include <windows.h> 
#include <vector> 

void wait_ms(double _ms) 
{ 
    clock_t endwait; 
    endwait = clock() + _ms * CLOCKS_PER_SEC/1000; 
    while (clock() < endwait) {} // active wait 
} 
void hugeList(void) 
{ 
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); 
    unsigned int loglimit = 3; 
    unsigned int limit = 1000; 
    while(true) 
    { 
     for(signed int cnt=loglimit; cnt>0; cnt--) 
     { 
      printf(" Countdown %d...\n", cnt); 
      wait_ms(1000.0); 
     } 
     printf(" Filling list...\n"); 
     std::forward_list<double> list; 
     for(unsigned int cnt=0; cnt<limit; cnt++) 
      list.push_front(42.0); 
     loglimit++; 
     limit *= 10; 
     printf(" Clearing list...\n"); 
     while(!list.empty()) 
      list.pop_front(); 
    } 
} 
void Clock() 
{ 
    clock_t start = clock()-CLOCKS_PER_SEC*100/1000; 
    while(true) 
    { 
     std::vector<double> dummyData(100000, 42.0); // just get some memory 
     printf("delta: %d ms\n", (clock()-start)*1000/CLOCKS_PER_SEC); 
     start = clock(); 
     wait_ms(100.0); 
    } 
} 

int main() 
{ 
    DWORD dwThreadId; 

    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Clock, (LPVOID) NULL, 0, &dwThreadId) == NULL) 
     printf("Thread could not be created"); 
    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&hugeList, (LPVOID) NULL, 0, &dwThreadId) == NULL) 
     printf("Thread could not be created"); 

    while(true) {;} 
    return 0; 
} 

首先我注意到爲鏈表分配內存要比釋放內存快得多。 在我的機器(Windows7)上,在「hugeList」方法的第四次迭代中 - 時鐘線程會受到很大的干擾(最多200ms)。如果沒有dummyData向量在Clock-Thread中「詢問」某些內存,效果就會消失。

所以,

  1. 是否有增加的內存分配的優先級在Win7時鐘線程的方法嗎?
  2. 或者我必須將兩個操作拆分爲兩個上下文(進程)?

請注意,我的原始代碼使用一些通過共享變量的通信,如果我選擇了第二個選項,它將需要某種IPC。

請注意,當「hugeList」方法等效於清除boost :: unordered_map並多次輸入ntdll.dll!RtIInitializeCriticalSection時,我的原始代碼會卡住大約1秒。 (observed by systinernals process explorer)

請注意,觀察到的效果不是由於交換,我使用我的16GB(64位win7)1.4GB。

編輯

只是想讓你知道,到現在爲止我一直沒能解決我的問題。將代碼的兩部分拆分到兩個進程似乎不是一種選擇,因爲我的時間非常有限,而且我從來沒有使用過程。恐怕我無法及時得到正在運行的版本。

但是,我設法通過減少非關鍵線程所做的內存釋放次數來降低影響。這是通過使用快速池化內存分配器(如boost庫中提供的內存分配器)來實現的。 似乎沒有可能在某些不需要同步的threadprivate堆中顯式創建某些對象(如我的示例中的巨大轉發列表)。

如需進一步閱讀:

http://bmagic.sourceforge.net/memalloc.html

Do threads have a distinct heap?

Memory Allocation/Deallocation Bottleneck?

http://software.intel.com/en-us/articles/avoiding-heap-contention-among-threads

http://www.boost.org/doc/libs/1_55_0/libs/pool/doc/html/boost_pool/pool/introduction.html

+0

你知道如何使用* free-list *加快這些分配,是的? –

+0

我注意到你在wait_ms中使用了無限循環。你有沒有嘗試過使用Sleep()?睡眠函數的幫助表明,運行Sleep()的線程放棄了其餘的執行時間,可能會產生一些影響? – Chris

+0

如何分析代碼以準確找到瓶頸的位置? –

回答

0

用std :: list替換std :: forward_list,我在corei7 4GB機器上運行你的代碼,直到消耗2GB。根本沒有干擾。 (在調試版本中)

P.S

是的。發佈版本重現了這個問題。我取代轉發列表使用數組

double* p = new double[limit]; 
for(unsigned int cnt=0; cnt<limit; cnt++) 
    p[cnt] = 42.0; 

for(unsigned int cnt=0; cnt<limit; cnt++) 
    p[cnt] = -1; 
delete [] p; 

它不會再重現。 看來,線程調度器是要求大量小內存塊的懲罰。

+0

謝謝。但是,我無法重現您的結果。在我的機器上,我使用list或forward_list並不重要。另外,即使開啓優化也不能解決問題!看起來好像矢量 dummyDate仍然存在於機器代碼中。奇怪...
你準確使用什麼配置? 我的代碼運行在Win7,64位,VisualStudio2010,i7-3770k,16GB – rava

+0

Win7,64bit,VS2008-32bit,i7-E3-1240,4GB – rmi

+0

啊我看。 嘗試「發佈構建」並告訴我爲什麼這會讓事情變得更慢。至少對我來說它確實如此。 :/ – rava