创建线程时设置CPU关联

klh5stk1  于 12个月前  发布在  其他
关注(0)|答案(5)|浏览(88)

我想创建一个C++11线程,我希望它在我的第一个核心上运行。我发现pthread_setaffinity_npsched_setaffinity可以更改线程的CPU亲和性并将其迁移到指定的CPU。然而,这个亲和性规范在线程运行后会发生变化。

如何创建具有特定CPU亲和性(cpu_set_t对象)的C++11线程?

如果在初始化C11线程时无法指定亲和性,如何在C中使用pthread_t
我的环境是Ubuntu上的G
。一段代码是赞赏。

mtb9vblg

mtb9vblg1#

我很抱歉成为这里的“流言终结者”,但是设置线程关联非常重要,随着时间的推移,随着我们使用的系统变得越来越NUMA,它的重要性也在增加(非均匀内存架构)。即使是一个普通的双插槽服务器,这些天有RAM单独连接到每个插槽,以及从一个插槽到其自己的RAM的访问内存与相邻处理器插槽的访问内存的差异在不久的将来,处理器将进入内部核心本身就是NUMA的市场(单独的内存控制器用于单独的核心组等)。这里没有必要我重复别人的工作,只需在网上寻找“NUMA和线程亲和力”-而且你可以从其他工程师的多年经验中学习。
不设置线程关联实际上等于“希望”操作系统调度程序能够正确处理线程关联。让我来解释一下:您有一个带有一些NUMA节点的系统(处理和内存域)。你启动一个线程,线程用内存做一些事情,例如malloc一些内存,然后处理等。现代操作系统(至少Linux,其他的可能也是)到目前为止做得很好,内存是默认分配的(如果有的话)从线程运行的CPU的同一个域。来的时候,分时操作系统(所有现代操作系统)将线程置于睡眠状态。当线程回到运行状态时,它可以在系统中的任何核心上运行(因为您没有为它设置关联掩码),并且您的系统越大,它在远离它以前分配或使用的内存的CPU上被“唤醒”的机会就越高。现在,所有内存访问都将是远程的(不确定这对应用程序性能意味着什么?在线阅读有关NUMA系统上的远程内存访问的更多信息)
因此,总而言之,当在具有非常重要的体系结构的系统上运行代码时,亲和性设置接口非常重要-这些体系结构正在迅速成为“任何系统”。(参见OpenMP,例如在Intel的KMP_AFFINITY环境变量的实现中)-这对C++来说是正确的11个实现者在他们的运行时库和语言选项中包含类似的机制(在此之前,如果你的代码旨在在服务器上使用,我强烈建议你在代码中实现亲和控制)

sqserrrh

sqserrrh2#

是的,有办法。我在这个博客link上偶然发现了这个方法
我在Eli Bendersky的博客上重写了代码,链接粘贴在上面。你可以保存下面的代码到test.cpp并编译和运行它:

// g++ ./test.cpp  -lpthread && ./a.out
// 
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>
#include <sched.h>
#include <pthread.h>
int main(int argc, const char** argv) {
  constexpr unsigned num_threads = 4;
  // A mutex ensures orderly access to std::cout from multiple threads.
  std::mutex iomutex;
  std::vector<std::thread> threads(num_threads);
  for (unsigned i = 0; i < num_threads; ++i) {
    threads[i] = std::thread([&iomutex, i,&threads] {
      // Create a cpu_set_t object representing a set of CPUs. Clear it and mark
      // only CPU i as set.
      cpu_set_t cpuset;
      CPU_ZERO(&cpuset);
      CPU_SET(i, &cpuset);
      int rc = pthread_setaffinity_np(threads[i].native_handle(),
                                      sizeof(cpu_set_t), &cpuset);
      if (rc != 0) {
        std::cerr << "Error calling pthread_setaffinity_np: " << rc << "\n";
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(20));
      while (1) {
        {
          // Use a lexical scope and lock_guard to safely lock the mutex only
          // for the duration of std::cout usage.
          std::lock_guard<std::mutex> iolock(iomutex);
          std::cout << "Thread #" << i << ": on CPU " << sched_getcpu() << "\n";
        }

        // Simulate important work done by the tread by sleeping for a bit...
        std::this_thread::sleep_for(std::chrono::milliseconds(900));
      }
    });

  }

  for (auto& t : threads) {
    t.join();
  }
  return 0;
}

字符串

ni65a41a

ni65a41a3#

在C++ 11中,当线程被创建时,你不能设置线程的亲合性(除非在线程中运行的函数自己做了这件事),但是一旦线程被创建,你可以通过任何你拥有的本机接口来设置亲合性,方法是获取线程的本机句柄(thread.native_handle()),所以对于Linux,你可以通过以下方式获取pthread id:
pthread_t mythread_native = mythread.native_handle();
然后,您可以使用任何pthread调用,在my_thread_native中传递pthread线程ID。
请注意,大多数线程工具都是特定于实现的,即pthreads,windows线程,其他操作系统的本地线程都有自己的接口和类型。

q1qsirdb

q1qsirdb4#

要添加到Y00的答案中,似乎可以通过调用直接从线程本身修改CPU亲和性,而无需通过调用pthread_self引用外部threads[i]数组。当然,它不能移植到Linux之外,但降低了复杂性。
下面是一个示例:

#include <stdio.h>
#include <thread>
#include <mutex>
#include <vector>
#include <unistd.h>
#include <string.h>

typedef std::lock_guard<std::mutex>        TGuard;

int totalCores = 0;

void
runner(int idx)
{
    int pinCore = idx % totalCores;
    printf("Launching Running # %d. Pin to %d\n", idx, pinCore);
    
    pthread_t self = pthread_self();
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(pinCore, &cpuset);
    
    int rc = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset);
    if (rc != 0)
    {
        printf("Failed to pin core: %s\n", strerror(errno));
        exit(1);
    }

    while (1)
    {
        printf("#%d Running: CPU %d\n", idx, sched_getcpu());
        sleep(1);
    }
}

int main(int argc, char **argv)
{
    totalCores = std::thread::hardware_concurrency();
    printf("Starting. %d cores\n", totalCores);
    std::vector<std::thread> threadList;

    const int N = 6;
    for (int i = 0; i < N; i++)
    {
        std::thread th = std::thread(runner, i);
        threadList.push_back(std::move(th));
    }
    
    for (auto &th : threadList)
    {
        if (th.joinable())
            th.join();
    }
    
    printf("Complete\n");
}

字符串

mwngjboj

mwngjboj5#

经过一段时间的搜索,我们似乎无法在创建C++ thread时设置CPU亲和性。
原因是,在创建线程时,没有NEED指定亲和力。所以,为什么要在语言中使其成为可能呢?
比如,我们希望将工作负载x1m1 n1x绑定到CPU 0。我们可以通过调用x1m2 n1x,在真实的工作负载之前将亲和性更改为CPU 0。
但是,我们可以在C中创建线程时指定亲和力(感谢Tony D的评论)。例如,以下代码输出“Hello pthread”。

void *f(void *p) {
  std::cout<<"Hello pthread"<<std::endl;
}

cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
pthread_attr_t pta;
pthread_attr_init(&pta);
pthread_attr_setaffinity_np(&pta, sizeof(cpuset), &cpuset);
pthread_t thread;
if (pthread_create(&thread, &pta, f, NULL) != 0) {
    std::cerr << "Error in creating thread" << std::endl;
}
pthread_join(thread, NULL);
pthread_attr_destroy(&pta);

字符串

相关问题