一个好的线程池队列大小

A good thread pool queue size

本文关键字:线程 队列 一个      更新时间:2023-10-16

好吧,让我们假设我们有一个带有"类型"动态平坦容器的线程池,该容器的最大容量为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功率(-7(秒。

使用"常规"线程池和具有堆分配和移动语义的"常规"线程池和"常规"功能绑定在1.8-2.3 e功率(-5(秒之间,这表明使用堆栈有真正的好处在这种情况下。

对问题的一般答案:

对于不再等待其他资源而不断运行的工作负载,从逻辑上讲,最大线程的数量应与物理处理器的数量相同(或者如果处理器具有超线程(。

对于等待其他资源的工作负载(例如,等待套接字连接(,您将需要补偿此延迟以获得最大吞吐量,通过拥有比逻辑处理器更多的线程(取决于您的等待时间(。如果大多数被阻止,数百个线程将很好。您可以考虑将任务的延迟限制部分和任务的CPU密集部分分开,以充分加载平衡工作负载,每个工作负载每个都有不同的线程计数。

假设您想最大化吞吐量,您可以从经验上确定最佳螺纹计数。

使用控制理论可以实现软件自我调整线程计数的有趣解决方案。Philipp K. Janert的《计算机系统的反馈控制》是一个很好的参考。

有一个好的经验法则,在最坏情况下,线程池任务队列应长多少?

我认为要问的正确问题是:

  • 任务可以等待多长时间才能进行评估?
  • 特定类型的新任务是否将现有任务取代?