【发布时间】:2017-11-08 16:38:25
【问题描述】:
好的,假设我们有一个带有“某种”动态扁平容器的线程池,它的最大容量为 x,因为内存在堆栈上以提高性能。
用最少的代码(我不想具体说明):
template <int32 QSIZE, int32 PSIZE> class ThreadPool
{
public:
ThreadPool()
{
for (int32 i = 0; PSIZE > i; ++i)
{
m_Workers.push(Thread(thread_main, m_Queue, m_Signal, m_IsRunning));
}
}
~ThreadPool()
{
//Wait and destroy all threads
}
void run(Task task)
{
m_Queue.push(task);
m_Signal.wake_all();
}
private:
FlatVector<Thread, PSIZE> m_Workers; //PSIZE --> max capacity
FlatQueue<Task, QSIZE> m_Queue; //QSIZE --> max capacity
ConditionVariable m_Signal;
AtomicBool m_IsRunning;
};
class Task 是具有绑定参数和移动语义的就地函数的实现。
FlatVector 是一个向量,其内存在堆栈上,最大容量为 PSIZE(池大小)。
FlatQueue 与容量为QSIZE(队列大小)的队列基本相同
Task 的最大大小为 512 位。
有没有一个好的经验法则,在最坏的情况下线程池任务队列应该增长到多大? (如果可能考虑给定的示例,如果不可能,也可以猜测常规线程池。)
在大多数情况下,我的池运行 8 个线程,因为这是我的核心数,并且使用池的应用程序可以充分利用更高的线程数。 (这是一个简单的物理模拟)
将任务打包成任务包是更好的方法吗(只要它们一起不超过 512 位,考虑到这个例子。)或者我应该跳过不能放在这个中的计算不再框架并在下一个计算它们?物理计算将计算 2 帧。
通常我选择的队列大小介于 64 到 128 个任务之间,这很好(至少在性能方面),但实际上感觉池中同时有 128 个任务对我来说有点多,我不想浪费这么多内存。
如果我将池设置为高负载,有时我会超过池中同时 64 个任务的限制。 (这就是我决定首先增加池大小的原因。)
在我的池中添加一个 512 位任务(最坏情况)在我的系统上需要 1,02 到 1,3 e power(-7) 秒。
对“常规”线程池和具有堆分配和移动语义的“常规”函数绑定执行相同操作需要 1.8 - 2.3 e power(-5) 秒之间的时间,这表明使用堆栈确实有好处在这种情况下。
【问题讨论】:
-
编写自己的线程池类很像编写自己的字符串类。也许你一生中至少做一次这件事有点重要,但将你所做的与其他程序员所做的进行比较同样重要。只有那个才能提供洞察力。当你这样做时,你最终会消除 PSIZE,因为它的最佳值是一个运行时细节,它取决于你的代码运行的特定机器。而且您也很可能会消除 QSIZE,因为没有像样的方法来预先猜测值并处理超出该限制的情况。比较一下,很重要。
-
公平点,我知道一个池(通常)应该有一个 cpu 核心的大小。稍后,至少线程数将在运行时用 getCoreCount() 函数替换(这就是你正确的地方)。尽管如此,带有 QSIZE 的堆栈容器仍将保留,因为堆栈容器的性能优势非常强大。实际上,我已经在以一种我不太喜欢的方式处理溢出:如果队列已满,我会在主线程上执行它们。这是我想在未来避免的事情,这就是为什么我正在寻找一个运行更并行的更好的解决方案。
标签: c++ multithreading threadpool