Планировщик Linux 2.6.31 и многопоточные задания - PullRequest
6 голосов
/ 13 мая 2010

Я выполняю массовые параллельные задания по научным вычислениям на общем компьютере Linux с 24 ядрами. Большую часть времени мои задания могут масштабироваться до 24 ядер, когда на этом компьютере больше ничего не работает. Однако, похоже, что даже когда выполняется одно однопоточное задание, не мое, моим 24-поточным заданиям (которые я установил для высоких значений nice) удается получить только ~ 1800% ЦП (с использованием нотации Linux). Между тем, около 500% циклов ЦП (опять же, с использованием нотации Linux) простаивают. Может кто-нибудь объяснить это поведение и что я могу с этим сделать, чтобы получить все 23 ядра, которые не используются кем-то еще?

Примечания:

  1. В случае, если это уместно, я наблюдал это на слегка разных версиях ядра, хотя не могу вспомнить, какая из них была у меня на голове.

  2. Архитектура ЦП x64. Вполне возможно, что тот факт, что мои 24-ядерные рабочие места являются 32-разрядными, а другие рабочие места, в которых я работаю, являются 64-разрядными, имеет значение?

Редактировать: Одна вещь, которую я только что заметил, заключается в том, что использование до 30 потоков, кажется, в некоторой степени облегчает проблему. Это дает мне процессор до ~ 2100%.

Ответы [ 5 ]

6 голосов
/ 14 мая 2010

Возможно, это вызвано тем, что планировщик пытается сохранить каждую из ваших задач на том же процессоре, на котором он ранее работал (это происходит потому, что задача, вероятно, перенесла свой рабочий набор в кеш этого процессора - "кеш горячий").

Вот несколько идей, которые вы можете попробовать:

  • Запустите вдвое больше потоков, чем у вас ядер;
  • Запустите на один или два потока меньше, чем у вас есть ядер;
  • Уменьшить значение /proc/sys/kernel/sched_migration_cost (возможно, до нуля);
  • Уменьшите значение /proc/sys/kernel/sched_domain/.../imbalance_pct ближе к 100.
2 голосов
/ 14 мая 2010

Ваши темы должны синхронизироваться? Если это так, у вас может быть следующая проблема:

Предположим, у вас есть система с 4 процессорами и работа с 4 потоками. При работе в одиночку потоки разветвляются, чтобы использовать все 4 ядра, и общее использование почти идеально (мы назовем это 400%).

Если вы добавите одно однопоточное мешающее задание, планировщик может разместить 2 ваших потока в одном процессоре. Это означает, что 2 из ваших потоков теперь работают практически вдвое медленнее своего нормального темпа (резкое упрощение), и если ваши потоки должны периодически синхронизироваться, прогресс вашей работы может быть ограничен самым медленным потоком, который в этом случае работает с половина нормальной скорости. Вы увидите, что загрузка составляет всего 200% (от вашей работы 4x50%) плюс 100% (мешающая работа) = 300%.

Аналогичным образом, если вы предполагаете, что мешающее задание использует только 25% времени одного процессора, вы можете увидеть один из ваших потоков и источник помех на одном и том же процессоре. В этом случае самый медленный поток работает с нормальной скоростью 3/4, в результате чего общее использование составляет 300% (4x75%) + 25% = 325%. Поиграйте с этими числами, и нетрудно придумать что-то похожее на то, что вы видите.

Если это проблема, вы, конечно же, можете поиграть с приоритетами, чтобы дать нежелательным задачам лишь крошечные доли доступного ЦП (я предполагаю, что задержки ввода-вывода не имеют значения). Или, как вы обнаружили, попробуйте увеличить потоки, чтобы каждый процессор имел, скажем, 2 потока, минус несколько, чтобы учесть системные задачи. Таким образом, 24-ядерная система может работать лучше всего, скажем, с 46 потоками (что всегда оставляет половину времени 2 ядер доступным для системных задач).

1 голос
/ 16 мая 2010

Общаются ли ваши темы друг с другом?

Попробуйте вручную связать каждую нить с процессором, используя sched_setaffinity или pthread_setaffinity_np. Планировщик может быть довольно тупым при работе с большим количеством связанных потоков.

0 голосов
/ 15 мая 2010

Как вы думаете, узкое место в вашем приложении или алгоритм планирования ядра? Перед тем, как вы начнете настраивать параметры планирования, я предлагаю вам попробовать запустить простое многопоточное приложение, чтобы посмотреть, работает ли оно так же, как ваше приложение.

// 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);
}
0 голосов
/ 13 мая 2010

Возможно, стоит использовать mpstat (часть пакета sysstat), чтобы выяснить, не работают ли целые процессоры, а другие полностью заняты. Он должен дать вам более подробное представление об использовании, чем top или vmstat: запустите mpstat -P ALL, чтобы увидеть 1 строку на процессор.

В качестве эксперимента вы можете попытаться установить сродство ЦП в каждом потоке так, чтобы каждый был связан с отдельным ЦП; это позволит вам увидеть, на что похожа производительность, если вы не позволите планировщику ядра решать, на какой процессор запланирована задача. Это не очень хорошее постоянное решение, но если оно очень помогает, оно дает вам представление о том, где не хватает планировщика.

...