Linux 2.6.31调度程序和多线程作业

时间:2010-05-13 16:43:42

标签: linux performance multithreading linux-kernel scheduler

我在具有24个核心的共享Linux计算机上运行大规模并行的科学计算工作。大多数时候,当这台计算机上没有其他任何东西运行时,我的工作能够扩展到24个核心。然而,似乎即使一个不是我的单线程作业正在运行,我的24线程作业(我设置为高优值)只能获得~1800%的CPU(使用Linux表示法)。同时,大约500%的CPU周期(再次使用Linux表示法)处于空闲状态。任何人都可以解释这种行为以及我可以做些什么来获得所有未被其他人使用的23个核心?

注意:

  1. 如果它是相关的,我在稍微不同的内核版本上观察到了这一点,虽然我不记得我的头脑。

  2. CPU架构是x64。我的24核作业是32位,而我正在竞争的其他作业是64位的这一事实是否有可能是相关的?

  3. 编辑:我刚注意到的一件事是,最多30个线程似乎在某种程度上缓解了这个问题。它让我达到了大约2100%的CPU。

5 个答案:

答案 0 :(得分:6)

这可能是由于调度程序试图将每个任务保持在以前运行的同一CPU上运行(这样做是因为该任务可能将其工作集带入该CPU的缓存中 - 它是“缓存热”)。

以下是您可以尝试的一些想法:

  • 运行两倍于核心的线程;
  • 运行比核心少一两个线程;
  • 减少/proc/sys/kernel/sched_migration_cost的值(可能降至零);
  • /proc/sys/kernel/sched_domain/.../imbalance_pct的值降低到接近100。

答案 1 :(得分:2)

您的线程是否必须同步?如果是这样,您可能会遇到以下问题:

假设你有一个4-cpu系统和一个4线程的作业。当单独运行时,线程会扇出来使用所有4个内核,总使用率接近完美(我们称之为400%)。

如果添加一个单线程干扰作业,则调度程序可能会将2个线程放在同一个cpu上。这意味着你的两个线程现在正在以正常速度的一半运行(显着的简化),如果你的线程需要定期同步,你的工作进度可能受到最慢线程的限制,在这种情况下运行时半正常速度。你会看到只有200%的利用率(从你的工作中运行4倍50%)加上100%(干扰工作)= 300%。

同样,如果您假设干扰作业仅使用一个处理器时间的25%,您可能会在同一CPU上看到一个线程和干扰源。在这种情况下,最慢的线程以3/4正常速度运行,导致总利用率为300%(4x 75%)+ 25%= 325%。玩这些数字并不难想出类似于你所看到的东西。

如果这就是问题所在,你当然可以优先考虑给不受欢迎的任务提供可用CPU的一小部分(我假设I / O延迟不是一个因素)。或者,正如您所发现的那样,尝试增加线程,以便每个CPU具有2个线程,减去几个线程以允许系统任务。通过这种方式,24核心系统可能运行得最好,例如46个线程(总是使2个核心的一半时间可用于系统任务)。

答案 2 :(得分:1)

您的线程是否相互通信?

尝试使用sched_setaffinitypthread_setaffinity_np手动将每个线程绑定到cpu。在使用大量相关线程时,调度程序可能会相当愚蠢。

答案 3 :(得分:0)

使用mpstatsysstat包的一部分)来确定是否有整个CPU闲置而其他CPU被充分利用可能是值得的。它应该提供比top或vmstat更详细的利用率视图:运行mpstat -P ALL以查看每个CPU 1行。

作为一项实验,您可以尝试在每个线程上设置CPU关联,使每个线程绑定到一个CPU;如果你不让内核调度程序决定在哪个CPU上安排任务,这将让你看到性能是什么样的。这不是一个好的永久性解决方案,但如果它有很大帮助,它可以让您了解调度程序的不足之处。

答案 4 :(得分:0)

您认为瓶颈在您​​的应用程序或内核的调度算法中吗?在开始调整调度参数之前,我建议您尝试运行一个简单的多线程应用程序,看它是否表现出与您的应用程序相同的行为。

// COMPILE WITH: gcc threads.c -lpthread -o thread
#include <pthread.h>
#define NUM_CORES 24

void* loop_forever(void* argument) {
    int a;
    while(1) a++;
}

void main() {
    int i;
    pthread_t threads[NUM_CORES];

    for (i = 0; i < NUM_CORES; i++)
        pthread_create(&threads[i], 0, loop_forever, 0);

    for (i = 0; i < NUM_CORES; i++)
        pthread_join(threads[i], 0);
}