我正在研究一个由两个并发线程组成的程序。一个(此处为“时钟”)正在定期执行一些计算(10 Hz),并且占用大量内存。另一个(这里是“hugeList”)使用更多的RAM但不像第一个那样具有时间关键性。所以我决定将其优先级降低到THREAD_PRIORITY_LOWEST。然而,当线程释放它使用的大部分内存时,关键的一个不能保持其时间。
我能够将问题压缩到这段代码中(确保优化已关闭!): 当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-vector“询问”时钟线程中的某些内存,效果就会消失。
所以,
请注意,我的原始代码使用了一些通过共享变量进行的通信,如果我选择第二个选项,则需要使用某种IPC。
请注意,当相当于“hugeList”-method清除boost :: unordered_map并多次输入ntdll.dll!RtIInitializeCriticalSection时,我的原始代码会被卡住大约1秒。 (observed by systinernals process explorer)
请注意,观察到的效果不是由于交换,我使用的是1.4GB的16GB(64位win7)。
修改:
只是想让你知道到目前为止我还没能解决我的问题。将代码的两个部分拆分到两个进程似乎不是一个选项,因为我的时间相当有限,到目前为止我从未使用过程。我恐怕无法及时找到正在运行的版本。
但是,我设法通过减少非关键线程所做的内存释放次数来减少影响。这是通过使用快速池内存分配器(如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 :(得分: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;
它不会重新创建。 似乎线程调度程序因为要求大量的小内存块而受到惩罚。