klm*_*123 12 c++ multithreading pthreads numa
关于我的问题:
我有一台带有2个AMD Opteron 6272和64GB RAM插槽的电脑.
我在所有32个核心上运行一个多线程程序,与运行2个程序的情况相比,速度降低了15%,每个程序在一个16核心插槽上运行.
如何以两个程序的速度制作单程序版本?
更多细节:
我有大量的任务,并希望完全加载系统的所有32个核心.因此,我将任务分组打包1000个.这样的组需要大约120Mb的输入数据,并且需要大约10秒才能在一个核心上完成.为了使测试更加理想,我将这些组复制32次,并使用ITBB的parallel_for循环在32个核心之间分配任务.
我pthread_setaffinity_np用来确保系统不会让我的线程在核心之间跳转.并确保所有核心都得到了相应的使用.
我mlockall(MCL_FUTURE)用来确保系统不会让我的内存在套接字之间跳转.
所以代码看起来像这样:
void operator()(const blocked_range<size_t> &range) const
{
for(unsigned int i = range.begin(); i != range.end(); ++i){
pthread_t I = pthread_self();
int s;
cpu_set_t cpuset;
pthread_t thread = I;
CPU_ZERO(&cpuset);
CPU_SET(threadNumberToCpuMap[i], &cpuset);
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated
TaskManager manager;
for (int j = 0; j < fNTasksPerThr; j++){
manager.SetData( &(InpData->fInput[j]) );
manager.Run();
}
}
}
Run Code Online (Sandbox Code Playgroud)
只有计算时间对我来说很重要,因此我在单独的parallel_for循环中准备输入数据.并且不包括准备时间测量.
void operator()(const blocked_range<size_t> &range) const
{
for(unsigned int i = range.begin(); i != range.end(); ++i){
pthread_t I = pthread_self();
int s;
cpu_set_t cpuset;
pthread_t thread = I;
CPU_ZERO(&cpuset);
CPU_SET(threadNumberToCpuMap[i], &cpuset);
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated
InpData[i].fInput = new ProgramInputData[fNTasksPerThr];
for(int j=0; j<fNTasksPerThr; j++){
InpData[i].fInput[j] = InpDataPerThread.fInput[j];
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我在32个核心上运行所有这些,并且看到每秒约1600个任务的速度.
然后,我创建了两个版本的方案,并与taskset和pthread确保在第一座和第二的16个内核,第一次运行-在第二插座.我使用&shell中的命令将它们彼此相邻地运行:
program1 & program2 &
Run Code Online (Sandbox Code Playgroud)
这些程序中的每一个都达到~900个任务/秒的速度.总共超过1800个任务/秒,比一个程序版本多15%.
我错过了什么?
我认为这可能是库中的问题,我只加载到集合线程的内存中.这可能是个问题吗?我可以复制库数据,以便它可以在两个套接字上独立使用吗?
我猜想是 STL/boost 内存分配在 numa 节点上为您的集合等分配内存,因为它们不知道 numa,并且您在每个节点上运行的程序中都有线程。
您使用的所有 STL/Boost 事物的自定义分配器可能会有所帮助(但可能是一项艰巨的工作)。