C ++ 11线程,亲和力和超线程

本文详细介绍了C++11中线程和并发的概念,以及如何使用C++11的`std::thread`库创建和管理线程。文章通过示例展示了如何获取系统的CPU数量,启动线程并设置线程亲和性,确保线程在特定CPU上运行。此外,还探讨了超线程技术,解释了为何在某些情况下将线程固定到特定CPU可以提升性能。最后,作者提供了一个基准测试来比较不同工作负载在不同CPU核心上的性能表现,强调了性能关键代码的测量和优化的重要性。
摘要由CSDN通过智能技术生成

背景和介绍 几十年来,C和C ++标准将多线程和并发视为标准领域之外存在的东西 - 在标准所针对的“抽象机器”所涵盖的“依赖于目标”的阴影世界中。立即冷血的回复“C ++不知道什么是线程”在邮件列表的山区和处理并行性的新闻组问题将永远作为对过去的提醒。 但所有这些都是用C ++ 11结束的。C ++标准委员会意识到语言将无法长时间保持相关性,除非它与时间保持一致并最终认识到线程,同步机制,原子操作和内存模型的存在 - 就在标准中,迫使C ++编译器和库供应商为所有支持的平台实现这些。这就是恕我直言,这是该语言的C ++ 11版本提供的大量改进的最大积极变化之一。 这篇文章不是关于C ++ 11线程的教程,但它使用它们作为主要的线程机制来演示它的观点。它从一个基本的例子开始,然后迅速转向线程亲和力,硬件拓扑和超线程的性能影响的专业领域。它在可移植的C ++中尽可能地做到了,清楚地标记了对特定于平台的调用的偏差。

逻辑CPU,内核和线程 大多数现代机器都是多CPU的。当然,这些CPU是否分为插槽和硬件核心取决于机器,但操作系统会看到许多可以同时执行任务的“逻辑”CPU。 在Linux上获取此信息的最简单方法是cat / proc / cpuinfo,它按顺序列出系统的CPU,提供有关每个CPU的一些信息(例如当前频率,缓存大小等)。在我的(8-CPU)机器上:

 
 

$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 60 model name : Intel(R) Core(TM) i7-4771 CPU @ 3.50GHz [...] stepping : 3 microcode : 0x7 cpu MHz : 3501.000 cache size : 8192 KB physical id : 0 siblings : 8 core id : 0 cpu cores : 4 apicid : 0 [...] processor : 1 vendor_id : GenuineIntel cpu family : 6 [...] [...] processor : 7 vendor_id : GenuineIntel cpu family : 6

可以从lscpu获取摘要输出:

 
 

$ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 8 On-line CPU(s) list: 0-7 Thread(s) per core: 2 Core(s) per socket: 4 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 60 Stepping: 3 CPU MHz: 3501.000 BogoMIPS: 6984.09 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 8192K NUMA node0 CPU(s): 0-7

 

每个CPU启动一个线程 C ++ 11线程库优雅地提供了一个实用程序函数,我们可以使用它来查找机器有多少CPU,以便我们可以规划并行策略。该函数称为hardware_concurrency,这是一个完整的示例,使用它来启动适当数量的线程。以下只是一个代码片段; 可以在此存储库中找到此帖子的完整代码示例以及适用于Linux的Makefile 。

 
 

int main(int argc, const char** argv) { unsigned num_cpus = std::thread::hardware_concurrency(); std::cout << "Launching " << num_cpus << " threads\n"; // A mutex ensures orderly access to std::cout from multiple threads. std::mutex iomutex; std::vector<std::thread> threads(num_cpus); for (unsigned i = 0; i < num_cpus; ++i) { threads[i] = std::thread([&iomutex, i] { { // 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 << " is running\n"; } // Simulate important work done by the tread by sleeping for a bit... std::this_thread::sleep_for(std::chrono::milliseconds(200)); }); } for (auto& t : threads) { t.join(); } return 0; }

甲的std ::线程是围绕一个平台特定的线程对象的薄包装纸; 这是我们很快就会利用的东西。所以当我们启动一个std :: thread时,会启动实际的OS线程。这是相当低级别的线程控制,但在本文中,我不会绕过基于任务的并行性等更高级别的构造,将此留待将来发布。

线程亲和力 因此,我们知道如何查询系统的CPU数量,以及如何启动任意数量的线程。现在让我们做一些更先进的事情。 所有现代操作系统都支持为每个线程设置CPU 关联。亲和性意味着不要在任何CPU上自由运行线程,而是要求OS调度程序仅将给定线程调度到单个CPU或预定义的CPU集。默认情况下,关联性涵盖系统中的所

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值