我有4个可执行程序,它们可以执行一些非常复杂的任务,仅这些程序中的每一个就可能占用四核CPU的单核功率的近100%,因此几乎占CPU总功率的25%。由于所有这些程序都使用无法在多个进程之间共享的硬件资源,因此我希望运行一个可执行文件,该可执行文件会产生3个子进程,而这些子进程又会占用其他三个核心。我在Linux上,使用的是C++11。大多数复杂的代码都在自己的类中运行,最难的部分在我通常称为Process()
的函数中运行,所以我有4个对象,每个对象都有自己的Process()
,当运行时,它占用100%的单个核心。
我尝试过使用OpenMP,但我认为这不是最好的解决方案,因为我无法控制CPU亲和力。另外,使用std::thread
也不是一个好主意,因为线程继承了主进程的CPU相关性。在Linux中,我认为我可以用fork()来实现这一点,但我不知道整个结构是如何构建的。
这可能与我的另一个问题有关,这个问题部分没有得到回答,可能是因为我尝试了错误的方法,这种方法在某些情况下有效,但在我的情况下无效。
伪代码的一个例子可能是:
int main()
{
// ...init everything...
// This alone takes 100% of a single core
float out1 = object1->Process();
// This should be spawned as a child process running on another core
float out2 = object2->Process();
// on another core...
float out3 ...
// yet another core...
float out4 ...
// This should still run in the parent process
float total_output = out1 + out2 + out3 + out4;
}
您可以使用std::thread
,它是pthread_create()
的前端。然后从线程本身设置它与sched_setaffinity()
的亲和性。
正如你所问,这里有一个工作存根:
#include <sched.h>
#include <thread>
#include <list>
void thread_func(int cpu_index) {
cpu_set_t cpuSet;
CPU_ZERO(&cpuSet);
CPU_SET(cpu_index, &cpuSet);
sched_setaffinity(0, sizeof( cpu_set_t), &cpuSet);
/* the rest of the thread body here */
}
using namespace std;
int main(int argc, char **argv) {
if (argc != 2) exit(1);
int n_cpus = atoi(argv[1]);
list< shared_ptr< thread > > lot;
for (int i=0; i<n_cpus; ++i) {
lot.push_back( shared_ptr<thread>(new thread(thread_func, i)));
}
for(auto tptr = lot.begin(); tptr != lot.end(); ++tptr) {
(*tptr)->join();
}
}
请注意,为了获得最佳行为,如果您希望您的代码也在多处理器上进行优化,那么每个线程在线程体中初始化其内存(即构造其对象)是很重要的,因为如果您在NUMA系统上工作,则使用内存页在靠近CPU的内存上分配内存页。
例如,你可以看看这个博客。
然而,在您的特定情况下,这并不是一个问题,因为您处理的是一个单处理器系统,或者更具体地说,是一个只有一个numa节点的系统(许多当前的AMD处理器确实包含两个numa节点,即使是在一个物理包中),并且所有的内存组都连接在那里。
在这种情况下使用sched_setaffinity()
的最终效果只是将每个线程固定到一个特定的核心。
您不必编写任何程序。命令taskset
更改当前正在运行的进程的CPU相关性,或者为新进程创建并设置它。
运行一个生成其他程序的可执行文件与直接执行程序没有什么不同,只是存根中的注释暗示了常见的初始化。
taskset 1 program_for_cpu_0 arg1 arg2 arg3...
taskset 2 program_for_cpu_1 arg1 arg2 arg3...
taskset 4 program_for_cpu_2 arg1 arg2 arg3...
taskset 8 program_for_cpu_3 arg1 arg2 arg3...
我对设置CPU亲和力持怀疑态度。我还没有找到这样做的实际用途(除了满足一些内心的控制需求)。
除非CPU在某些方面不相同,否则不需要将进程限制为特定的CPU。
- Linux内核通常将进程保持在同一CPU上,除非它进入对i/o、信号量等的扩展等待
- 将进程从一个CPU切换到另一个CPU不会产生任何特定的开销,除非在每个CPU具有本地内存的NUMA配置中。AFAIK,ARM的实现并不能做到这一点
- 如果进程应该表现出非CPU绑定行为,那么允许内核调度器灵活地将进程重新分配给现在可用的CPU应该可以提高系统性能。绑定到CPU的进程无法参与资源可用性