MPI_SEND занимает огромную часть виртуальной памяти

Отлаживая мою программу на большом количестве ядер, я столкнулся с очень странной ошибкой insufficient virtual memory, Мои исследования привели к спокойствию кода, когда мастер посылает небольшие сообщения каждому рабу. Затем я написал небольшую программу, в которой 1 мастер просто отправил 10 целых чисел с MPI_SEND и все рабы получают его с MPI_RECV, Сравнение файлов /proc/self/status до и после MPI_SEND показал, что разница в размерах памяти огромна! Самое интересное (что приводит к сбою моей программы), это то, что эта память не освобождается после MPI_Send и до сих пор занимают огромное пространство.

Есть идеи?

 System memory usage before MPI_Send, rank: 0
Name:   test_send_size                                                                                
State:  R (running)                                                                                  
Pid:    7825                                                                                           
Groups: 2840                                                                                        
VmPeak:   251400 kB                                                                                 
VmSize:   186628 kB                                                                                 
VmLck:        72 kB                                                                                  
VmHWM:      4068 kB                                                                                  
VmRSS:      4068 kB                                                                                  
VmData:    71076 kB                                                                                 
VmStk:        92 kB                                                                                  
VmExe:       604 kB                                                                                  
VmLib:      6588 kB                                                                                  
VmPTE:       148 kB                                                                                  
VmSwap:        0 kB                                                                                 
Threads:    3                                                                                          

 System memory usage after MPI_Send, rank 0
Name:   test_send_size                                                                                
State:  R (running)                                                                                  
Pid:    7825                                                                                           
Groups: 2840                                                                                        
VmPeak:   456880 kB                                                                                 
VmSize:   456872 kB                                                                                 
VmLck:    257884 kB                                                                                  
VmHWM:    274612 kB                                                                                  
VmRSS:    274612 kB                                                                                  
VmData:   341320 kB                                                                                 
VmStk:        92 kB                                                                                  
VmExe:       604 kB                                                                                  
VmLib:      6588 kB                                                                                  
VmPTE:       676 kB                                                                                  
VmSwap:        0 kB                                                                                 
Threads:    3        

2 ответа

Решение

Это ожидаемое поведение практически от любой реализации MPI, работающей через InfiniBand. Механизмы IB RDMA требуют, чтобы буферы данных были зарегистрированы, то есть они сначала фиксируются в фиксированной позиции в физической памяти, а затем драйвер сообщает InfiniBand HCA, как сопоставить виртуальные адреса с физической памятью. Это очень сложный и, следовательно, очень медленный процесс регистрации памяти для использования IB HCA, и поэтому большинство реализаций MPI никогда не отменяют регистрацию памяти, которая когда-то была зарегистрирована, в надежде, что эта же память будет позже использована в качестве источника или цели данных снова. Если зарегистрированная память была кучей памяти, она никогда не возвращается обратно в операционную систему, и поэтому ваш сегмент данных только увеличивается в размере.

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

Существуют определенные параметры, которые управляют очередями IB, используемыми Intel MPI. Самое важное в вашем случае это I_MPI_DAPL_BUFFER_NUM который контролирует количество предварительно выделенной и предварительно зарегистрированной памяти. Это значение по умолчанию 16так что вы можете уменьшить его. Имейте в виду возможные последствия для производительности, хотя. Вы также можете попробовать использовать динамические предварительно выделенные размеры буфера, установив I_MPI_DAPL_BUFFER_ENLARGEMENT в 1, Если эта опция включена, Intel MPI сначала регистрирует небольшие буферы, а затем увеличивает их при необходимости. Также обратите внимание, что IMPI открывает соединения лениво, и поэтому вы видите огромный рост используемой памяти только после вызова MPI_Send,

Если не используется транспорт DAPL, например, с использованием ofa вместо этого, вы не можете многое сделать. Вы можете включить очереди XRC, установив I_MPI_OFA_USE_XRC в 1, Это должно как-то уменьшить используемую память. Также включение динамического создания пар очереди путем настройки I_MPI_OFA_DYNAMIC_QPS в 1 может уменьшить использование памяти, если коммуникационный граф вашей программы подключен не полностью (полностью подключенная программа - это та, в которой каждый ранг обращается ко всем остальным рангам).

Ответ Христо в основном правильный, но поскольку вы используете небольшие сообщения, есть небольшая разница. Сообщения попадают на активный путь: сначала они копируются в уже зарегистрированный буфер, затем этот буфер используется для передачи, и получатель копирует сообщение из активного буфера на своем конце. Повторное использование буферов в вашем коде поможет только с большими сообщениями.

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

Эти активные буферы несколько расточительны. Например, они по умолчанию 16 КБ на Intel MPI с глаголами OF. Если не используется агрегация сообщений, каждое сообщение размером 10 int занимает четыре страницы по 4 КБ. Но агрегация в любом случае не поможет при общении с несколькими получателями.

Так что делать? Уменьшите размер активных буферов. Это контролируется установкой порога нетерпеливости / рандеву (I_MPI_RDMA_EAGER_THRESHOLD переменная окружения). Попробуйте 2048 или даже меньше. Обратите внимание, что это может привести к увеличению задержки. Или изменить I_MPI_DAPL_BUFFER_NUM переменная для управления количеством этих буферов, или попробуйте функцию динамического изменения размера, предложенную Христо. Это предполагает, что ваш IMPI использует DAPL (по умолчанию). Если вы используете глаголы OF напрямую, переменные DAPL не будут работать.


Изменить: Таким образом, окончательное решение для запуска этого было настройка I_MPI_DAPL_UD=enable, Я могу рассуждать о происхождении магии, но у меня нет доступа к коду Intel, чтобы это подтвердить.

IB может иметь разные виды транспорта, двумя из которых являются RC (надежное соединение) и UD (ненадежная датаграмма). RC требует явного соединения между хостами (например, TCP), и некоторое количество памяти расходуется на соединение. Что еще более важно, каждое соединение имеет привязанные к нему буферы, и это действительно складывается. Это то, что вы получаете с настройками Intel по умолчанию.

Возможна оптимизация: совместное использование активных буферов между соединениями (это называется SRQ - Shared Receive Queue). Существует еще одно расширение только для Mellanox, называемое XRC (eXtended RC), которое продвигает совместное использование очереди: между процессами, которые находятся на одном узле. По умолчанию Intel MPI обращается к устройству IB через DAPL, а не напрямую через глаголы OF. Я думаю, что это исключает эти оптимизации (у меня нет опыта работы с DAPL). Можно включить поддержку XRC, установив I_MPI_FABRICS=shm:ofa а также I_MPI_OFA_USE_XRC=1 (заставляя Intel MPI использовать интерфейс OFA вместо DAPL).

Когда вы переключаетесь на транспорт UD, вы получаете дополнительную оптимизацию поверх общего буфера: больше нет необходимости отслеживать соединения. Совместное использование буфера является естественным в этой модели: поскольку нет никаких соединений, все внутренние буферы находятся в общем пуле, как и в SRQ. Таким образом, существует дальнейшая экономия памяти, но за счет затрат: доставка дейтаграмм может быть неудачной, и дело в программном обеспечении, а не в оборудовании IB для обработки повторных передач. Конечно, все это прозрачно для кода приложения с использованием MPI.

Другие вопросы по тегам