2014-07-25 3 views
2

Je cours un projet utilisant Boost MPI (1.55) sur Open MPI (1.6.1) sur un cluster de calcul.Ouvrir MPI et Boost MPI en utilisant trop de handles de fichiers

Notre cluster a des nœuds qui ont 64 processeurs et nous engendrons un seul processus MPI sur chacun. La plupart de nos communications se font entre des processus individuels, chacun ayant une série de requêtes irecv() ouvertes (pour des balises différentes) et les envois sont bloqués en utilisant send().

Le problème que nous obtenons est que, après un court laps de temps de traitement (habituellement de moins de 10 minutes), nous obtenons cette erreur qui est à l'origine du programme à la fin:

[btl_tcp_component.c:1114:mca_btl_tcp_component_accept_handler] accept() failed: Too many open files in system (23). 

débogage plus approfondi montre que ce sont les sockets réseau qui prennent ces handles de fichiers, et nous atteignons notre limite d'OS de 65536 poignées de fichiers ouvertes. La plupart d'entre elles sont dans le statut "TIME_WAIT", ce qui est apparemment ce que TCP fait (habituellement) 60 secondes après la fermeture d'un socket (afin d'intercepter les paquets en retard). J'avais l'impression qu'Open MPI ne fermait pas les sockets (http://www.open-mpi.org/faq/?category=tcp#tcp-socket-closing) et gardait simplement les sockets N^2 ouvertes pour que tous les processus puissent se parler. De toute évidence 65536 est bien au-delà de 64^2 (la cause la plus fréquente de cette erreur impliquant MPI est simplement que la limite de fichier est inférieure à N^2) et la plupart de ces sockets étaient dans un statut récemment fermé. Notre code C++ est trop grand pour tenir ici, mais j'en ai écrit une version simplifiée pour au moins montrer notre implémentation et voir s'il y a des problèmes avec notre technique. Y a-t-il quelque chose dans notre utilisation de MPI qui provoquerait la fermeture et la réouverture d'un trop grand nombre de sockets par 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

Oh mon cher C++, alors s'il vous plaît prenez ce qui suit avec l'énorme avertissement que je ne comprends pas ce langage ... De toute façon, je ne vois pas mpi_wait ou mpi_test - chaque fois que vous avez une communication mpi non bloquante une attente correspondante. Mon soupçon est que sans ces sockets ne sont pas fermés, mais je dois souligner que c'est une conjecture que l'attente/test pourrait être ailleurs, ou C++ pourrait être encore plus bizarre que je pense actuellement. –

+1

Ouais @IanBush, C++ n'a pas été trop amical avec nous. Avec la couche Boost MPI en haut, les instructions comme dataReq.test() sont analogues à mpi_test sur cet irecv particulier. Je ne suis pas sûr s'il y a aussi un besoin d'attendre s'il y a un sondage régulier avec test? – Marc

Répondre

1

Cela pourrait ne pas être la véritable raison pour ouvrir Ouvrez MPI tant de prises, mais vous semblent être des demandes qui fuient dans la partie suivante de poll() fonction:

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

La poignée de demande retourné par world.irecv() n'est jamais enregistré et donc perdu. Si elle est appelée périodiquement sur le même objet workReq, cette branche s'exécutera chaque fois après l'achèvement de la requête puisque le test d'une requête déjà terminée renvoie toujours true. Par conséquent, vous commencerez beaucoup de récepteurs non bloquants qui ne seront jamais attendus ou testés. Sans oublier les messages envoyés.

Un problème similaire existe dans receiveFinish - finishReq est transmis par valeur et l'affectation n'affectera pas la valeur dans run().

Remarque: est-ce vraiment le code que vous utilisez? Il semble que la fonction poll() que vous appelez en run() prend deux arguments alors que l'un montre ici prend quatre arguments et il n'y a pas d'arguments avec des valeurs par défaut.

+0

Salut Hristo et merci pour votre contribution. Ce n'est pas le code que nous utilisons, c'est essentiellement une version paraphrasée, parce que c'est assez volumineux et compliqué.La ligne irecv que vous avez indiquée était une erreur et aurait dû lire: 'workReq = world.irecv (mpi :: any_source, WORKTAG, données);' Cependant j'ai jeté un oeil à notre code et nous passons en effet certaines des demandes par valeur (alors que dans d'autres endroits par référence). Cela pourrait en effet être la source du problème – Marc

+0

On dirait que les requêtes passées par valeur au lieu de référence ont été mon problème. Merci beaucoup pour l'avoir repéré. – Marc

+0

Vous êtes les bienvenus. –