多线程:为什么两个程序比一个程序好
Multithreading: Why two programs is better than one?
关于我的问题:
我有一台带有 2 个 AMD 皓龙 6272 插槽和 64GB RAM 的计算机。
我在所有 32 个内核上运行一个多线程程序,与运行 15 个程序的情况相比,速度降低了 2%,每个程序在一个 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();
}
}
}
只有计算时间对我来说很重要,因此我在单独的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];
}
}
}
现在我在 32 个内核上运行所有这些,并看到每秒 ~1600 个任务的速度。
然后我创建了两个版本的程序,并使用taskset
和pthread
确保第一个在第一个插槽的 16 个内核上运行,第二个 - 在第二个插槽上运行。我简单地使用 shell 中的&
命令将它们并排运行:
program1 & program2 &
这些程序中的每一个都实现了~900个任务/秒的速度。总共是>1800 个任务/秒,比一个程序版本多 15%。
我错过了什么?
我认为问题可能出在库中,我只加载到集合线程的内存中。这会是一个问题吗?我可以复制库数据,使其在两个套接字上独立可用吗?
我猜是 STL/boost 内存分配在 numa 节点上为您的集合等分配内存,因为它们不是 numa 感知的,并且您在每个节点上运行的程序中都有线程。
您使用的所有 STL/boost 内容的自定义分配器可能会有所帮助(但可能是一项艰巨的工作)。
您可能会遇到错误共享缓存的糟糕情况:http://en.wikipedia.org/wiki/False_sharing
您的线程可能通过block_range引用共享对同一数据结构的访问权限。 如果只需要速度,则可能需要将副本传递给每个线程。 如果你的数据太大而无法容纳到调用堆栈中,你可以在不同的缓存段中动态分配每个范围的副本(即,只要确保它们足够远)。
或者,也许我需要查看其余代码才能更好地理解您正在做什么。
- 试图在visual studio上用C++创建一个桌面应用程序
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 我正在尝试使用 c++ 创建一个货币转换程序,我不知道如何继续
- 试图创建一个多线程程序来查找0-100000000之间的总素数
- 我试图制作一个程序,要求用户输入问题和答案,但程序循环不正确
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 如何声明一个可以在整个程序中使用的全局 2d 3d 4d .. 数组(堆版本)变量?
- 我写了一个C++程序来模拟Enigma机器.我没有得到输出
- 为什么这个程序返回最后一个单词而不是最长的单词?
- 如何使用C++读取另一个程序中的源代码输出
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 我的程序有一个保存配置文件的GUI,如何双击此配置文件以直接加载带有配置数据的GUI?
- 我想通过带有C++和Python的插件创建一个可扩展的应用程序
- 3-3. 编写一个程序来计算每个不同单词在其输入中出现的次数
- 为什么有时我输入一个整数,程序将第一个输入的数字打印成十进制数?
- 未定义的操作?程序一个接一个地打印出不同的值
- 如何给这个简单的 c++ 程序一个适当的循环
- 我如何给一个c++程序一个图标