在创建线程时设置CPU亲和性
Set CPU affinity when create a thread
我想创建一个c++ 11线程,我希望它在我的第一个核心上运行。我发现pthread_setaffinity_np
和sched_setaffinity
可以改变线程的CPU亲和力,并将其迁移到指定的CPU。但是,此关联规范在线程运行后更改。
我如何创建一个c++ 11线程与特定的CPU亲和力(一个cpu_set_t
对象)?
如果在初始化c++ 11线程时不可能指定亲和性,我如何在C中使用pthread_t
?
我的环境是Ubuntu上的g++。
我很抱歉在这里成为"神话终结者",但是设置线程亲和性非常重要,并且随着时间的推移,随着我们使用的系统本质上变得越来越NUMA(非统一内存体系结构),它的重要性也越来越大。现在,即使是一个普通的双套接字服务器也会将RAM单独连接到每个套接字,并且从套接字到自己的RAM与相邻处理器套接字(远程RAM)访问内存的差异是巨大的。在不久的将来,处理器将进入内部核心集本身就是NUMA的市场(为不同的核心组提供单独的内存控制器等)。我不需要在这里重复其他人的工作,只需在网上查找"NUMA和线程亲和力"-您可以学习其他工程师多年的经验。
不设置线程亲和性实际上等于"希望"操作系统调度器将正确处理线程亲和性。让我解释一下:您有一个带有一些NUMA节点(处理和内存域)的系统。你启动一个线程,线程用内存做一些事情,例如malloc一些内存,然后处理等。到目前为止,现代操作系统(至少Linux,其他操作系统也可能)做得很好,默认情况下,内存是从线程运行的CPU的同一域中分配的(如果可用)。随着时间的推移,分时操作系统(所有现代操作系统)将使线程进入睡眠状态。当线程被放回运行状态时,它可以在系统中的任何内核上运行(因为您没有为它设置亲和掩码),并且您的系统越大,它在远离之前分配或使用的内存的CPU上被"唤醒"的可能性就越高。现在,所有的内存访问都将是远程的(不确定这对应用程序性能意味着什么?阅读更多关于NUMA系统上的远程内存访问(在线)
所以,总而言之,当在具有非平凡架构的系统上运行代码时,关联设置接口是非常重要的——如今,这种架构正迅速成为"任何系统"。一些线程运行时环境/库允许在运行时控制这一点,而不需要任何特定的编程(参见OpenMP,例如在英特尔的KMP_AFFINITY环境变量实现中)—对于c++ 11实实者来说,在其运行时库和语言选项中包含类似的机制是正确的(在此之前,如果您的代码旨在在服务器上使用,我强烈建议您在代码中实现affinity控制)
是的,有办法做到。我是在这个博客链接上看到这个方法的
我在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;
}
在c++ 11中,您不能在创建线程时设置线程关联(除非线程中运行的函数自己执行此操作),但是一旦创建线程,您可以通过获得线程的本机句柄(thread.native_handle())通过任何本机接口设置关联,因此对于Linux,您可以通过以下方式获得pthread id:
pthread_t my_thread_native = my_thread.native_handle();然后你可以使用任何pthread调用在my_thread_native中传递pthread的线程id。
请注意,大多数线程设施都是特定于实现的,即pthreads, windows线程,其他操作系统的本机线程都有自己的接口和类型,这部分代码将不是很可移植。
经过一段时间的搜索,我们似乎不能在创建c++ thread
时设置CPU亲和性。
原因是,在创建线程时,有不需要指定亲和性。所以,为什么要在语言中实现它呢?
假设我们希望工作负载f()
绑定到CPU0。我们可以通过调用pthread_setaffinity_np
将亲和关系更改为CPU0 ,就在实际工作负载之前。
然而,我们可以在c语言中创建线程时指定affinity(感谢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);
- 多态性和功能结合
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 松弛原子与无同步情况下的记忆连贯性
- C++Union/Struct位域的实现和可移植性
- C++boost序列化多态性问题
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 编写一个函数以使用 n 百分比的 CPU 使用率
- 如何更改唯一指针向量的可见性
- 获取 SFML 窗口的 HWND 和高可用性?
- 如何禁用 CPU 的无序执行
- 多核 CPU 上 32 位读取的原子性
- Intel MIC上的Intel TBB和Cilk Plus线程亲和性
- 如何防止子分支进程继承CPU亲和性
- 在创建线程时设置CPU亲和性
- Boost::Thread API中的CPU亲和性
- 响应性地检查两个队列而不锁定CPU
- 标准C++11是否保证high_resolution_clock测量实时性(非CPU周期)