创建线程时设置CPU关联

Pen*_*ang 13 c c++ multithreading pthreads c++11

我想创建一个C++ 11线程,我希望它在我的第一个核心上运行.我发现pthread_setaffinity_np并且sched_setaffinity可以更改线程的CPU亲和性并将其迁移到指定的CPU.但是,在线程运行后,此关联性规范会发生更改.

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

如果在初始化C++ 11线程时无法指定关联,那么如何pthread_t在C中执行?

我的环境是Ubuntu上的G ++.一段代码表示赞赏.

小智 24

我很抱歉在这里成为"神话破坏者",但设置线程亲和力非常重要,随着时间的推移它越来越重要,因为我们所使用的系统本质上变得越来越多的NUMA(非统一内存架构).如今,即使是一个简单的双插槽服务器也会将RAM单独连接到每个插槽,并且从插槽到其自己的RAM访问存储器与相邻处理器插槽(远程RAM)的存储器的差异很大.在不久的将来,处理器正在进入市场,其中内部核心集合本身就是NUMA(用于单独核心组的独立存储器控制器等).我没有必要在这里重复其他人的工作,只需在线查找"NUMA和线程亲和力" - 您可以从其他工程师的多年经验中学习.

不设置线程亲和性实际上等于"希望"OS调度程序将正确处理线程关联.让我解释一下:你有一个带有一些NUMA节点(处理和内存域)的系统.你启动一个线程,线程用内存做一些事情,例如malloc一些内存,然后处理等等.现代操作系统(至少Linux,其他可能也是)到目前为止做得很好,默认情况下,内存被分配(如果可用的话)来自运行该线程的CPU的同一域.来吧,分时操作系统(所有现代操作系统)都会让线程进入休眠状态.当线程回到运行状态时,它可以在系统中的任何核心上运行(因为你没有为它设置一个亲和力掩码),系统越大,它的可能性就越高.在远离先前分配或使用的内存的CPU上"唤醒".现在,您的所有内存访问都是远程的(不确定这对您的应用程序性能意味着什么?了解有关NUMA系统上的远程内存访问的更多信息)

因此,总而言之,在具有更重要的体系结构的系统上运行代码时,亲和设置接口非常重要 - 这些日子正迅速成为"任何系统".一些线程运行时环境/库允许在运行时控制它而无需任何特定的编程(参见OpenMP,例如在英特尔的KMP_AFFINITY环境变量的实现中) - 对于C++ 11实现者来说,包含类似的机制是正确的.他们的运行时库和语言选项(在此之前,如果您的代码旨在用于服务器,我强烈建议您在代码中实现关联控制)

  • 这似乎根本无法回答这个问题。 (2认同)

Z-Y*_*Y00 15

是的,有办法做到。我在这个博客链接上遇到了这个方法

我在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;
}

Run Code Online (Sandbox Code Playgroud)


Pen*_*ang -11

搜索了一段时间,似乎我们在创建 C++ 时无法设置 CPU 亲和力thread

原因是,创建线程时不需要指定亲和力。那么,为什么还要用语言让它成为可能呢?

比如说,我们希望工作负载f()绑定到 CPU0。我们可以通过调用在实际工作负载之前pthread_setaffinity_np更改与 CPU0 的关联性。

然而,我们可以在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);
Run Code Online (Sandbox Code Playgroud)

  • 不好的答案。您使用粗体大写字母对用例做出过于广泛的断言。pthread_setaffinity_np 更改当前正在运行的进程的线程,导致不必要的上下文切换。 (5认同)
  • @aho 我会澄清一个用例。在某些系统中,例如嵌入式或实时优先级系统,抢占式多线程是不可能或不可取的。此 C++ 线程 api 要求线程在与生成进程相同的硬件核心/超线程上启动(从技术上讲,它不允许您在启动线程之前预先指定 CPU 关联性)。因此,它假设抢占式多线程的可用性。如果当前线程以实时优先级运行,则新线程将永远不会启动。如果此限制不适用于您,请使用 C++ 线程 API。 (3认同)