减少具有相同优先级的线程之间的上下文切换
Reduce Context Switches Between Threads With Same Priority
我正在编写一个使用第三方库执行繁重计算的应用程序。
这个库在内部实现并行,并生成给定数量的线程。我想运行这个库的几个(动态计数)实例,因此最终会严重超额订阅cpu。
有没有什么方法可以增加进程中所有线程的"时间量",例如,所有具有正常优先级的线程很少切换上下文(yield),除非它们是通过例如信号量显式生成的?
这样,我就可以避免过度订阅cpu带来的大部分性能开销。请注意,在这种情况下,我不在乎线程是否会被饿死几秒钟。
编辑:
一种复杂的方法是手动执行线程调度。
- 枚举具有特定优先级的所有线程(例如普通线程)
- 暂停所有操作
- 创建一个循环,每隔40ms恢复/挂起线程,并确保运行的线程数不超过当前cpu数
这种方法有什么主要缺点吗?不确定恢复/挂起线程的开销是多少?
没有什么特别的事情需要做。任何像样的调度程序都不会允许非强制上下文切换占用很大一部分CPU资源。任何没有合适调度程序的操作系统都不应该使用。
超额预订CPU的性能开销是,而不是非强制上下文切换的成本。为什么?因为调度器可以简单地避免这些。调度程序只在有好处的情况下执行非强制上下文切换。性能成本为:
-
完成一项作业可能需要更长的时间,因为从作业开始到作业完成,其他作业将完成更多的工作。
-
额外的线程为其堆栈和相关的其他跟踪信息消耗内存。
-
更多的线程通常意味着更多的争用(例如,当分配内存时),这可能意味着更多强制的上下文切换,因为线程无法向前推进,所以必须切换。
您只想在知道调度程序不知道的重要内容时尝试更改调度程序的行为。这里没有发生这样的事。所以默认行为就是你想要的。
这种方法有什么主要缺点吗?不确定的开销是多少恢复/挂起线程是什么?
是,恢复/挂起线程是在程序的用户模式下进行的非常非常危险的活动。因此,它不应该被使用(几乎永远不会)。此外,我们不应该用这些概念来实现任何现代调度器为我们所做的事情。这个问题的其他帖子也提到了这一点。
上面的内容适用于任何操作系统,但从SO post标签来看,我似乎已经被要求使用基于Microsoft Windows的系统。现在,如果我们阅读MSDN中的SuspendThread(),我们会得到以下内容:
"此函数主要设计用于调试器。不打算用于线程同步。如果调用线程试图获得挂起线程所拥有的同步对象,如互斥对象或关键节,则在拥有同步对象的线程上调用SuspendThread会导致死锁"。
因此,考虑线程已经获取了一些资源的场景(通过库或内核模式隐式地,即非代码的一部分),如果我们挂起线程,这将导致神秘的死锁情况,因为该进程的其他线程将等待该特定资源。事实上,我们不确定(在任何时候)在我们的程序中,任何正在运行的线程获取什么样的资源,挂起/恢复线程都不是一个好主意。
- 在cuda线程之间共享大量常量数据
- 线程之间的布尔停止信号
- 线程之间的实时数据共享
- 线程之间的通信不起作用 - C++
- 简单使用 std::atomic 在两个线程之间共享数据
- 不同线程之间的互斥锁同步
- 多个线程之间的获取-释放内存顺序
- 如何在两组线程之间正确来回传输控制权
- 如果两个线程调用同一个函数,但函数中的所有变量都是局部变量,我还需要担心线程之间共享数据吗?
- 填充和保存线程之间的共享缓冲区
- 在C 中共享线程之间的列表
- 在 API 和应用程序线程之间共享数据
- 将线程之间的标准输入/输出重定向
- Boost::线程如何在主线程和工作线程之间同步
- std::线程之间的互斥体同步
- C++ - 多线程 - 线程之间的通信
- Qthread char阵列在两个线程之间传递后被摧毁
- 线程之间类的静态数据共享
- 增强ASIO和线程之间的消息传递
- 将数组分配在固定数量的线程之间