使用太多文件句柄打开MPI和Boost MPI

时间:2014-07-25 08:22:28

标签: sockets boost mpi openmpi filehandle

我在计算群集上使用Boost MPI(1.55)而不是Open MPI(1.6.1)运行项目。

我们的集群有64个CPU的节点,我们在每个节点上产生一个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);
  }
}

1 个答案:

答案 0 :(得分:1)

这可能不是Open MPI打开这么多套接字的真正原因,但您似乎在poll()函数的以下部分泄漏了请求:

if(workReq.test()) {
  int target = assess(work);
  world.send(target, DATATAG, dowork);
  world.irecv(mpi::any_source, WORKTAG, data); // <-------
  return true;
}

world.irecv()返回的请求句柄永远不会保存,从而丢失。如果在同一个workReq对象上定期调用,则此分支将在请求完成后每次执行,因为测试已完成的请求始终返回true。因此,您将启动许多永远不会等待或测试的非阻塞接收。更不用说发送的消息了。

receiveFinish中存在类似问题 - finishReq按价值传递,而作业不会影响run()中的值。

旁注:这真的是你使用的代码吗?您在poll()中调用的run()函数看起来有两个参数,而此处显示的函数有四个参数,并且没有默认值的参数。