四核Linux:单个可执行文件,4个进程

四核Linux:单个可执行文件,4个进程,linux,multithreading,c++11,fork,Linux,Multithreading,C++11,Fork,我有4个可执行文件,它们可以执行一些非常复杂的任务,仅这些程序中的每一个就可能占用四核CPU单核的近100%的功率,从而导致CPU总功率的近25%。由于所有这些程序都使用了无法在多个进程之间共享的硬件资源,因此我希望运行一个可执行文件,生成3个子进程,这些子进程反过来又占据了其他三个核心。我使用的是Linux,我使用的是C++11。大多数复杂代码都在自己的类中运行,最难的部分在我通常调用的函数中运行,因此我有4个对象,每个对象都有自己的Process(),当运行时,它占据了单个核心的100% 我

我有4个可执行文件,它们可以执行一些非常复杂的任务,仅这些程序中的每一个就可能占用四核CPU单核的近100%的功率,从而导致CPU总功率的近25%。由于所有这些程序都使用了无法在多个进程之间共享的硬件资源,因此我希望运行一个可执行文件,生成3个子进程,这些子进程反过来又占据了其他三个核心。我使用的是Linux,我使用的是C++11。大多数复杂代码都在自己的类中运行,最难的部分在我通常调用的函数中运行,因此我有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\u 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();
  }
}
#包括
#包括
#包括
无效线程函数(int cpu索引){
cpu\u设置\u t cpuSet;
CPU_零(&cpuSet);
CPU设置(CPU索引和cpuSet);
sched_setaffinity(0,sizeof(cpu_set)&cpuSet);
/*线体的其余部分在这里*/
}
使用名称空间std;
int main(int argc,字符**argv){
如果(argc!=2)退出(1);
int n_cpu=atoi(argv[1]);
列出>批次;
对于(int i=0;ijoin();
}
}
请注意,为了实现最佳行为,每个线程在线程体中初始化其内存(即构造其对象)是很重要的,如果您希望代码在多处理器上也得到优化,因为如果您在NUMA系统上工作,内存页将分配到使用它们的CPU附近的内存上

例如,您可以查看

但是,在您的具体情况下,这不是一个问题,因为您处理的是单处理器系统,或者更具体地说是一个只有一个numa节点的系统(许多当前AMD处理器确实包含两个numa节点,即使在单个物理包中),并且所有内存库都连接在那里


在此上下文中使用
sched_setaffinity()
的最终效果是将每个线程固定到一个特定的内核。

您不必编写任何程序。该命令更改当前运行进程的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配置中
  • 如果进程表现出非CPU绑定的行为,允许内核调度器灵活地将进程重新分配给现在可用的CPU应该可以提高系统性能。绑定到CPU的进程不能参与资源可用性

  • 我相信您仍然可以使用
    std::thread
    ,方法是从该线程中调用
    gettid()
    ,然后将其作为
    pid
    参数传递给
    sched_setaffinity()
    。Edit:实际上,您甚至不需要调用
    gettid()
    ,只需将
    pid
    保留为零和
    sched_setaffinity()
    将修改调用线程的关联性。确切地说,我正在使用四核Cortex A9 ARMv7 CPU,这是一个嵌入式Linux平台。好吧,我想那肯定不是SMP!我报告的信息适用于所有AMD SMP和MIPS SMP,以及Intel SMP放弃前端总线(FSB)时的情况.那么,你认为它能与
    std::thread
    sched_setaffinity()一起工作吗
    ?您能提供一些我可以测试的示例代码吗?我想这里的混淆源于这样一个事实,即HT也被用作多个处理器的互连,以及CPU是什么。CPU和核心是同义词。基本上,一个处理器有多个CPU,这些CPU具有一个或多个执行上下文(硬件线程,如果您愿意的话)@MarkMiles如果您的平台只有一个numa节点,那么您肯定不需要检查关联性,因为所有的内存都是由所有的内核平等访问的。我将添加代码,稍后修复答案以确保完整性。1.或者需要执行另一个进程,线程开始在内核上跳舞。2.缓存除外即使旧内核在同一个处理器中,也会在旧内核中留下行,因此交换内核会导致代价高昂且无用的缓存未命中。这是当前内核试图强制执行线程-内核亲和力的最终原因。3.我同意您会失去灵活性,但它总是发生在您优化时,正如OP所尝试的那样确实如此。@Sigismondo:关于缓存的观点很好,但在多核ARM上,0级缓存是统一的。我所研究的系统都没有1级或更高级别的缓存。0级是L2的ARM名称吗?我还不是ARM专家:)但是L1是内核的本地缓存,还是我得到了