当我在具有2个CPU的特定服务器上使用多线程代码时,我遇到了问题。服务器运行在Windows 7 x64上,Bi-Xeon E5-2697Wv2 12核心频率为2,7 GHz; 64 Gb RAM(8X8 Gb 1866 MHz);主板SuperMicro X9DAI。我的可执行文件是使用Visual Studio MSVC 2013生成的,并使用OpenMP生成多线程。
现在问题是我使用1个线程而不是24个线程有更好的性能......这个问题只在这台计算机上可见,当我附加一个探查器(CodeXL)时,我得到以下结果:
代码很复杂,我不能发布一个例子,但基本上它是一个蒙特卡罗代码,有一些动态分配(初始化阶段创建所有需要的数据),它仍然只是一个动态在一个事件开始时分配以存储事件数据。代码不包含任何互斥锁,除了在计算的开始和结束之外,每个线程都没有任何通信。
我对服务器和双CPU架构的了解非常有限,我想知道是否有什么办法可以避免这个问题(BIOS选项?),我猜是有一个控制器可以选择哪个CPU内存使用它,这个操作慢下来......
感谢您的阅读。
编辑: 我写了一个小基准来评估malloc / free的性能下降,这里是代码:
#include <omp.h>
#include <afx.h>
#include <vector>
#include <fstream>
#include <iostream>
#include <chrono>
// malloc allocation size tab
int allocSize[] =
{
4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072
};
int main()
{
// number max of thread
int nbThreadMax = omp_get_max_threads();
// malloc/free iteration per bench
unsigned int nbIteration = 1000000;
// Empty res tab
std::vector<double> emptyRes(16, 0.);
// Duration per thread
std::vector<std::vector<double>> avgDuration(nbThreadMax, emptyRes);
int nbThread = 1;
unsigned int idxt = 0;
while (nbThread <= nbThreadMax)
{
// Current bench result
std::vector<std::vector<double>> threadResult(nbThread, emptyRes);
std::cout << "Thread : " << nbThread << std::endl;
// Create parrallel region
#pragma omp parallel num_threads(nbThread)
{
int nt = omp_get_thread_num();
for (unsigned int i = 0; i < 16; ++i)
{
int allocationSize = allocSize[i];
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
for (unsigned int j = 0; j < nbIteration; ++j)
{
void* pData = malloc(allocationSize);
free(pData);
}
end = std::chrono::system_clock::now();
threadResult[nt][i] += std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 1000.;
}
}
// Sum
for (unsigned int i = 0; i < 16; ++i)
{
for (unsigned int j = 0; j <= idxt; ++j)
{
avgDuration[idxt][i] += threadResult[j][i];
}
// /!\ Normalize for one thread /!\
avgDuration[idxt][i] /= nbThread;
}
++idxt;
// Increase thread number (X2)
if (nbThread >= nbThreadMax)
break;
if (nbThread * 2 > nbThreadMax)
nbThread = nbThreadMax;
else
nbThread = nbThread * 2;
}
// Write results
{
std::ofstream ofs("resultats.csv");
ofs << "NbThread;";
for (unsigned int i = 0; i < 16; ++i)
{
ofs << allocSize[i] << ";";
}
ofs << std::endl;
int nbThread = 1;
for (unsigned int n = 0; n < idxt; ++n)
{
ofs << nbThread << ";";
for (unsigned int i = 0; i < 16; ++i)
{
ofs << avgDuration[n][i] << ";";
}
ofs << std::endl;
nbThread = nbThread * 2;
}
ofs.close();
}
}
以下是我服务器上的结果: malloc/free duration /thread malloc/free performance factor /thread
这种结果是显示问题还是正常的性能下降?
答案 0 :(得分:2)
BIOS选项远非太奇特了。最简单的解决方案是稍微偏离标准C方法并使用本机Windows方法。
第一项测试是将malloc/free
替换为HeapAlloc
。这里的好处是HeapAlloc
可以支持多个堆,并且HEAP_NO_SERIALIZE
每个堆都可以是单线程的。这 not 意味着你必须在同一个线程上调用HeapFree
。您可以在工作线程上调用HeapAlloc
,将结果存储在已分配的内存块中,与主线程(此处为内存屏障)连接,然后在主线程上从工作线程收集所有数据并调用{{1来自主线程。由于工作线程不再存在,因此没有序列化风险。
第二项改进(如有必要)将检查NUMA支持。最好将线程固定到CPU并从连接到该特定CPU的4xGB分配内存。但这要复杂得多。
答案 1 :(得分:0)
标准C11 / C ++ 11中的一种方法是为每个线程创建一个单项缓存 。在mymalloc
中,检查单个缓存条目是否可以满足请求(不需要锁定)。如果没有,请遵循常规malloc
。
大部分情报都在myfree
。如果已经有一个缓存条目,您需要决定做什么:保持最旧,保持最新,保持最小,保持最大,或可能是其他策略。 (如果您需要此处的大小,mymalloc
必须按sizeof(size_t)
过度分配并为请求的大小添加前缀。