Я запускаю проект с использованием Boost MPI (1.55) поверх Open MPI (1.6.1) в вычислительном кластере.
В нашем кластере есть узлы с 64 процессорами, и мы запускаем по одному процессу MPI на каждом. Большая часть нашего взаимодействия осуществляется между отдельными процессами, каждый из которых имеет серию открытых запросов irecv () (для разных тегов), а отправка выполняется с блокировкой с помощью send ().
Проблема, которую мы получаем, заключается в том, что после короткого времени обработки (обычно менее 10 минут) мы получаем эту ошибку, которая приводит к завершению программы:
[btl_tcp_component.c:1114:mca_btl_tcp_component_accept_handler] accept() failed: Too many open files in system (23).
Более тщательная отладка показывает, что эти дескрипторы файлов берут на себя сетевые сокеты, и мы достигли предела нашей ОС в 65536 открытых дескрипторов. Большинство из них находятся в состоянии «TIME_WAIT», что, по-видимому, является тем, что TCP делает в течение (обычно) 60 секунд после закрытия сокета (чтобы перехватить любые поздние пакеты). У меня создалось впечатление, что Open MPI не закрывает сокеты (http://www.open-mpi.org/faq/?category=tcp#tcp-socket-closing) и просто оставил открытыми до N ^ 2 сокетов, чтобы все процессы могли взаимодействовать друг с другом. Очевидно, 65536 выходит за рамки 64 ^ 2 (наиболее частая причина этой ошибки, связанной с MPI, заключается просто в том, что ограничение файла меньше N ^ 2), и большинство из них были сокетами, которые недавно были в закрытом состоянии.
Наш код C ++ слишком велик, чтобы уместиться здесь, но я написал упрощенную версию некоторых из них, чтобы хотя бы показать нашу реализацию и посмотреть, есть ли какие-либо проблемы с нашей техникой. Есть ли что-то в нашем использовании MPI, что может привести к закрытию и повторному открытию слишком большого количества сокетов OpenMPI?
namespace mpi = boost::mpi;
mpi::communicator world;
bool poll(ourDataType data, mpi::request & dataReq, ourDataType2 work, mpi::request workReq) {
if(dataReq.test()) {
processData(data); // do a bunch of work
dataReq = world.irecv(mpi::any_source, DATATAG, data);
return true;
}
if(workReq.test()) {
int target = assess(work);
world.send(target, DATATAG, dowork);
world.irecv(mpi::any_source, WORKTAG, data);
return true;
}
return false;
}
bool receiveFinish(mpi::request finishReq) {
if (finishReq.test()) {
world.send(0, RESULTS, results);
resetSelf();
finishReq = world.irecv(0, FINISH);
return true;
}
return false;
}
void run() {
ourDataType data;
mpi::request dataReq = world.irecv(mpi::any_source, DATATAG, data);
ourDataType2 work;
mpi::request workReq = world.irecv(mpi::any_source, WORKTAG, work);
mpi::request finishReq = world.irecv(0, FINISH); // the root process can call a halt
while(!receiveFinish(finishReq)) {
bool doWeContinue = poll(data, dataReq);
if(doWeContinue) {
continue;
}
// otherwise we do other work
results = otherwork();
world.send(0, RESULTS, results);
}
}