распределение памяти и доступ на оборудовании NUMA - PullRequest
4 голосов
/ 16 октября 2011

Я занимаюсь разработкой научного вычислительного инструмента на python, который должен быть способен распределять работу по нескольким ядрам в среде общей памяти NUMA. Я ищу наиболее эффективный способ сделать это.

Потоки, к сожалению, вне игры из-за глобальной блокировки интерпретатора python, которая оставляет форк как мой единственный вариант. Для межпроцессного взаимодействия я предполагаю, что мои варианты - это каналы, сокеты или mmap. Пожалуйста, укажите, если что-то отсутствует в этом списке.

Моему приложению потребуется довольно много связи между процессами и доступ к некоторому количеству общих данных. Моя главная проблема - латентность.

Мои вопросы: когда я разветвляю процесс, будет ли его память находиться рядом с ядром, которому он назначен? Поскольку форк в * nix копирует при записи, изначально я полагаю, что это не может иметь место. Хочу ли я принудительно создать копию для более быстрого доступа к памяти, и если да, то как лучше это сделать? Если я использую mmap для связи, может ли эта память по-прежнему распределяться по ядрам или она будет расположена на одном? Есть ли процесс, который прозрачно перемещает данные для оптимизации доступа? Есть ли способ прямого контроля над физическим распределением или способ запроса информации о распределении для оптимизации?

На более высоком уровне, какие из этих вещей определяются моим оборудованием, а какие операционной системой? Я нахожусь в процессе покупки высокопроизводительной мультисокетной машины и сомневаюсь между AMD Opteron и Intel Xeon. Какое влияние оказывает конкретное оборудование на любой из приведенных выше вопросов?

Ответы [ 2 ]

2 голосов
/ 23 декабря 2012

Поскольку одной из ахиллесовой пяты Python является GIL, улучшена многопроцессорная поддержка.Например, есть очереди, каналы, блокировки, общие значения и общие массивы.Существует также то, что называется «Менеджер», который позволяет вам оборачивать множество структур данных Python и обмениваться ими с использованием IPC.Я полагаю, что большинство из них работает через каналы или сокеты, но я не слишком углубился во внутренние механизмы.

http://docs.python.org/2/library/multiprocessing.html

Как Linux моделирует системы NUMA?

Ядро обнаруживает, что оно работает на многоядерном компьютере, а затем определяет, сколько оборудования и какова топология.Затем он создает модель этой топологии, используя идею узлов.Узел - это физический сокет, который содержит процессор (возможно, с несколькими ядрами) и память, которая к нему подключена.Почему основанный на узле вместо основанного на ядре?Поскольку шина памяти - это физические провода, которые соединяют ОЗУ с сокетом ЦП, и все ядра ЦП в одном сокете будут иметь одинаковое время доступа ко всей ОЗУ, которая находится на этой шине памяти.

Какдоступ к памяти на одной шине памяти ядром на другой шине памяти?

В системах x86 это происходит через кеши.Современные ОС используют часть аппаратного обеспечения, называемую «Lookaside Buffer перевода» (TLB), для сопоставления виртуальных адресов с физическими.Если память, для которой был задан кэш, является локальной, она читается локально.Если он не локальный, он будет проходить через шину Hyper Transport в системах AMD или QuickPath в Intel для удаленной памяти, чтобы быть удовлетворенным.Поскольку это сделано на уровне кэша, вам теоретически не нужно об этом знать.И вы, конечно, не имеете никакого контроля над этим.Но для высокопроизводительных приложений это невероятно полезно понять, чтобы минимизировать количество удаленных обращений.

Где ОС фактически определяет физические страницы виртуальной памяти?

Когда процесс разветвляется, он наследуетсявсе это родительские страницы (из-за коровы).Ядро имеет представление о том, какой узел является «лучшим» для процесса, а какой «предпочтительный» узел.Это можно изменить, но снова по умолчанию будет таким же, как родительский.Распределение памяти будет по умолчанию для того же узла, что и родительский, если это не будет явно изменено.

Есть ли прозрачный процесс, который перемещает память?

Нет.После выделения памяти она фиксируется на узле, на котором она была выделена.Вы можете сделать новое распределение на другом узле, переместить данные и освободить на первом узле, но это немного хлопотно.

Есть ли способ контролировать распределение?

По умолчанию выделяется локальный узел.Если вы используете libnuma, вы можете изменить способ распределения (скажем, циклический перебор или чередование) вместо значения по умолчанию для локального.

Я взял много информации из этого сообщения в блоге:

http://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/

Я определенно рекомендую вам прочитать его полностью, чтобы собрать дополнительную информацию.

0 голосов
/ 24 марта 2015
##client.py
import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])



sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:

    sock.connect((HOST, PORT))
    sock.sendall(bytes(data +"\n", "utf-8"))

    received = str(sock.recv(1024), "utf-8")
finally:
    sock.close()

print("Sent:     {}".format(data))
print("Received: {}".format(received))

##server.py
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):

        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "10.0.0.1", 9999
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
...