Планирование ядра для 1024 процессоров

Azul Systems имеет устройство, которое поддерживает тысячи когерентных процессоров кэша. Я хотел бы понять, какие изменения должны произойти в операционной системе, чтобы запланировать тысячи одновременно запущенных потоков.

8 ответов


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

есть также несколько кусочков clarififying информация, которая помогла бы определить параметры проблемы:

сколько IPC (inter process communication) вам нужно?
Действительно ли они должны быть потоками, или они могут быть процессами?
Если это процессы, нормально ли, если они должны разговаривать друг с другом через сокеты, а не с помощью общая память?
Что такое архитектура памяти? Вы прямо SMP с 1024 ядрами, или есть какая-то другая NUMA (неоднородная архитектура памяти) или MMP происходит здесь? Каковы ваши таблицы страниц?

зная только самую маленькую информацию о системах Azul, я бы предположил, что у вас очень мало IPC, и что простая модель "запустить одно ядро на ядро" может на самом деле сработать просто отлично. Если процессам нужно поговорить друг с другом, то они могут создавать сокеты и передавайте данные таким образом. Поддерживает ли ваше оборудование эту модель? (Вам, вероятно, понадобится один IP-адрес на ядро, а при 1024 IP-адресах это может быть хлопотно, хотя все они могут быть NAT'D, и, возможно, это не такое уж большое дело). Если, конечно, эта модель приведет к некоторым неэффективностям, таким как дополнительные таблицы страниц и справедливые накладные расходы на ОЗУ, и может даже не поддерживаться вашей аппаратной системой.

даже если "1 ядро на ядро" не работает, вы можете вероятно запустите 1024/8 ядер и будьте в порядке, позволяя каждому ядру управлять 8 физическими процессорами.

тем не менее, если вы хотите запустить 1 поток на ядро в традиционной машине SMP с 1024 ядрами (и только несколькими физическими процессорами), то я ожидал бы, что старомодный планировщик O(1) - это то, что вам нужно. Вероятно, ваш CPU[0] в конечном итоге будет почти 100% в ядре и выполнять обработку прерываний, но это нормально для этого случая использования, если вам не нужно больше 1 ядра для обработки вашего рабочая загрузка.


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


создание масштаба Linux было долгим и постоянным проектом. Первое многопроцессорное ядро Linux имело одну блокировку, защищающую все ядро (Big Kernel Lock, BKL), которая была простой, но ограниченной масштабируемостью.

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

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

кроме того, некоторые алгоритмы выбираются с учетом масштабируемости. Например. некоторые данные чтения в основном защищены Read-Copy-Update (RCU) вместо традиционных мьютексов; это позволяет читателям продолжать во время параллельного обновления.

Что касается памяти, Linux пытается выделить память из того же узла NUMA, где выполняется процесс. Это обеспечивает лучшую пропускную способность памяти и задержку для приложений.


мое необразованное предположение было бы, что существует очередь выполнения на процессор и алгоритм кражи работы, когда процессор простаивает. Я мог видеть, что это работает в модели M:N, где есть один процесс на cpu и легкие процессы в качестве рабочих элементов. Это было бы похоже на threadpool для кражи работы, например, в библиотеке fork-join Java-7.

Если вы действительно хотите знать, пойти забрать Solaris Internals или копаться в коде ядра Solaris. Я все еще читаю. Design & Impl FreeBSD, с Solaris Internals следующим в моем списке, поэтому все, что я могу сделать, это сделать дикие догадки atm.


Я уверен, что SGI Altix у нас есть на работе, (который делает ccNUMA) использует специальное оборудование для согласованности кэша.

есть огромные накладные расходы, связанные с удержанием кэша 4mb на ядро когерентного. Это вряд ли произойдет только в программном обеспечении.

в массиве из 256 процессоров вам понадобится 768MB ram только для хранения битов Cache-invalidation. 12Мб кэш-памяти / 128 байт в кэш-линию * 2562 ядер.


изменение ОС-это одно, но использование неизмененного кода приложения-это пустая трата оборудования. При превышении некоторого предела (в зависимости от оборудования) усилия по поддержанию согласованности и синхронизации для выполнения общего кода просто слишком велики. Вы можете это сделать, но это будет очень дорого. Со стороны ОС вам понадобится сложная модель сродства, т. е. не прыгать процессоры только потому, что ваш занят. Планирование потоков на основе аппаратной топологии-сотрудничающие потоки на процессорах, которые "закрыть", чтобы минимизировать штрафы. Простая кража работы не является хорошим решением, вы должны учитывать топологию. Одним из решений является иерархическая кража работы-кража работы по расстоянию, разделение топологии на сектора и попытка украсть у ближайшего. Коснувшись немного проблемы блокировки; вы все равно будете использовать spin-locks и такие, но используя совершенно разные реализации. Это, вероятно, самая запатентованная область в CS в эти дни. Но, опять же, вам нужно будет программировать специально для такого масштабного масштаба. Или вы просто при использовании его. Никакие автоматические "параллелизаторы" не сделают этого за вас.


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


даже в двухъядерных системах intel я уверен, что Linux уже может обрабатывать "тысячи" потоков с собственными потоками posix.

(Glibc и ядро должны быть настроены для поддержки этого, однако, но я считаю, что большинство систем в эти дни имеют это по умолчанию сейчас).