Как связать потоки с ядрами с заранее определенными объектами пула памяти? (80-ядерная архитектура Nehalem 2Tb RAM)

Я столкнулся с незначительной проблемой HPC после запуска некоторых тестов в архитектуре nehalem 80core (160HT) с DRAM 2Tb:

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

Ядро появляется на 100%, хотя я знаю, что они ждут, когда удаленный сокет вернет запрос.

Поскольку большая часть кода выполняется асинхронно, гораздо проще переписать код, поэтому я могу просто анализировать сообщения из потоков на одном сокете, чтобы потоки были другими (без блокировки ожидания). Кроме того, я хочу блокировать каждый поток в пулах памяти, поэтому я могу обновлять объекты, а не тратить время (~ 30%) на сборщик мусора.

Следовательно, вопрос:

Как связать потоки с ядрами с предопределенными объектами пула памяти в Python?

Немного больше контекста:

У Python нет проблем с запуском multicore, когда вы ставите ZeroMQ посередине и создаете искусство из передачи сообщений между пулом памяти, управляемым каждым ZMQworker. На ZMQ 8M msg/second это внутреннее обновление объектов занимает больше времени, чем трубопровод может быть заполнен. Все это описано здесь: http://zguide.zeromq.org/page:all#Chapter-Sockets-and-Patterns

Итак, с небольшим упрощением, я запускаю 80 ZMQworkerprocesses и 1 ZMQrouter и загружаю контекст с большим роем объектов (на самом деле 584 миллиона объектов). Из этой "начальной точки" объекты должны взаимодействовать для завершения вычисления.

Это идея:

  • Если "объект X" должен взаимодействовать с "объектом Y" и доступен в локальный пул памяти в python-потоке, то взаимодействие следует делать напрямую.
  • Если "Объект Y" НЕ доступен в одном пуле, то я хочу, чтобы он отправьте сообщение через ZMQrouter и позвольте маршрутизатору возвращать ответ в какой-то более поздний момент времени. Моя архитектура не блокирует, так что то, что происходит в конкретном потоке python, продолжается, не дожидаясь ответа zmqRouters. Даже для объектов в одном и том же сокете, но в другом ядре, я предпочел бы НЕ взаимодействовать, поскольку я предпочитаю иметь чистые обмены сообщениями вместо того, чтобы иметь 2 потока, управляющих одним и тем же объектом памяти.

Для этого мне нужно знать:

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

Но я не могу найти ссылки в документах python о том, как это сделать и на google. Я должен искать неправильную вещь.

Update:

В отношении вопроса "зачем использовать ZeroMQ для архитектуры MPI?", пожалуйста, прочитайте поток: Распространение против MPI vs zeromq?, поскольку приложение, над которым я работаю, предназначенный для распределенного развертывания, хотя он протестирован на архитектуре, где MPI более подходит.

Обновление 2:

Относительно вопроса:

"Как связать потоки с ядрами с предопределенными пулами памяти в Python (3)" ответ находится в psutils:

>>> import psutil
>>> psutil.cpu_count()
4
>>> p = psutil.Process()
>>> p.cpu_affinity()  # get
[0, 1, 2, 3]
>>> p.cpu_affinity([0])  # set; from now on, this process will run on CPU #0 only
>>> p.cpu_affinity()
[0]
>>>
>>> # reset affinity against all CPUs
>>> all_cpus = list(range(psutil.cpu_count()))
>>> p.cpu_affinity(all_cpus)
>>>

Работник может быть привязан к ядру, в котором NUMA может быть эффективно использована (найдите свой тип процессора, чтобы убедиться, что это NUMA-архитектура!)

Второй элемент - это определение пула памяти. Это можно сделать с помощью psutils или библиотека ресурсов:

Ответ 1

Вы можете недооценить проблему, нет супер-простого способа выполнить то, что вы хотите. В качестве общего руководства вам необходимо работать на уровне операционной системы, чтобы настроить все так, как вы хотите. Вы хотите работать с так называемой "близостью к процессору" и "сродством к памяти", и вам нужно много думать о своей системной архитектуре, а также о вашей архитектуре программного обеспечения, чтобы все было в порядке. В реальном HPC названные "сродства" обычно обрабатываются библиотекой MPI, такой как Open MPI. Возможно, вы захотите рассмотреть возможность использования одного и позволить вашим различным процессам обрабатывать эту библиотеку MPI. Интерфейс между операционной системой, библиотекой MPI и Python может быть предоставлен пакетом mpi4py.

Вам также необходимо получить свою концепцию потоков и процессов и настройку ОС прямо. В то время как для планировщика времени процессора поток является задачей, которая должна быть запланирована, и поэтому теоретически может иметь индивидуальную близость, я знаю только маски близости для целых процессов, то есть для всех потоков в рамках одного процесса. Для управления доступом к памяти NUMA (неравномерный доступ к памяти) является ключевым словом справа, и вы можете захотеть просмотреть http://linuxmanpages.com/man8/numactl.8.php

В любом случае вам нужно прочитать статьи о теме сродства и, возможно, захотите начать чтение в Часто задаваемых вопросах о MPI в Open MPI: http://www.open-mpi.de/faq/?category=tuning#paffinity-defs

Если вы хотите достичь своей цели, не используя библиотеку MPI, изучите пакеты util-linux или schedutils и numactl вашего дистрибутива Linux, чтобы получить полезные инструменты командной строки, такие как taskset, которые вы можете, например, вызов из Python, чтобы установить маски соответствия для определенных идентификаторов процесса.

В этой статье ярко описывается, как библиотека MPI может быть полезной с вашей проблемой:

http://blogs.cisco.com/performance/open-mpi-v1-5-processor-affinity-options/

Этот ответ SO описывает, как вы делите свою аппаратную архитектуру: fooobar.com/questions/446030/...

В общем, мне интересно, является ли машина, на которую вы подаете заявку, правильной для задачи или если вы, возможно, оптимизируетесь не на том конце. Если вы отправляете сообщения внутри на одном компьютере и нажимаете ограничения пропускной способности памяти, я не уверен, что ZMQ (через TCP/IP, правильно?) Является правильным инструментом для выполнения обмена сообщениями. Возвращаясь к MPI, интерфейс передачи сообщений для приложений HPC...

Ответ 2

Просто интересно, не может ли это быть связано с использованием удаленных объектов python - это может стоить исследования, но, к сожалению, у меня нет доступа к такому оборудованию.

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

На более низком уровне Pyro - это всего лишь форма межпроцессного общения. Поэтому везде, где вы могли бы использовать более примитивную форму IPC (например, простые сокеты TCP/IP) между компонентами Python, вы могли бы вместо этого использовать Pyro.

В то время как pyro может добавить некоторые накладные расходы, он может ускорить работу и сделать вещи более удобными.