¿Qué características necesitan los usuarios de una interfaz MPI C ++?

28

La versión 3.0 del estándar MPI eliminó formalmente la interfaz C ++ (anteriormente estaba en desuso). Si bien las implementaciones aún pueden admitirlo, las características que son nuevas en MPI-3 no tienen una interfaz C ++ definida en el estándar MPI. Consulte http://blogs.cisco.com/performance/the-mpi-c-bindings-what-happened-and-why/ para obtener más información.

La motivación para eliminar la interfaz C ++ de MPI fue que no tenía un valor significativo sobre la interfaz C. Hubo muy pocas diferencias además de "s / _ / :: / g" y no se emplearon muchas características a las que los usuarios de C ++ están acostumbrados (por ejemplo, la determinación automática de tipos mediante plantillas).

Como alguien que participa en el Foro MPI y trabaja con una serie de proyectos C ++ que han implementado su propia interfaz C ++ para las funciones MPI C, me gustaría saber cuáles son las características deseables de una interfaz C ++ para MPI. Si bien no me comprometo a nada, me interesaría ver la implementación de una interfaz MPI C ++ independiente que satisfaga las necesidades de muchos usuarios.

Y sí, estoy familiarizado con Boost :: MPI ( http://www.boost.org/doc/libs/1_54_0/doc/html/mpi.html ) pero solo es compatible con las funciones MPI-1 y el modelo de serialización sería extremadamente difícil de soportar para RMA.

Una interfaz de C ++ para MPI que me gusta es la de Elemental ( https://github.com/poulson/Elemental/blob/master/src/core/imports/mpi.cpp ) para que tal vez la gente pueda proporcionar algo de información enfoque. En particular, creo que MpiMap resuelve un problema esencial.

Jeff
fuente
No creo que este sea el lugar apropiado para tal pregunta.
Jack Poulson
¿Puedes dar algunas razones para eso? Muchas de las preguntas de MPI en este sitio me sugieren que las personas aquí están preparadas para responder esta pregunta. Además, 0.2 votos a favor por minuto sugieren que otras personas no están de acuerdo con su evaluación. En cualquier caso, sería más útil sugerir un lugar alternativo para publicar esto si no le gusta el lugar actual.
Jeff
La pregunta es valiosa, y creo que podría obtener algunas respuestas valiosas en listas de correo más amplias de ciencia computacional, si está dentro del alcance. (¿Quizás NA-digest, SIAM-CSE, o incluso una publicación pública en G +?) Esta pregunta puede no ser una buena opción para un sitio de Stack Exchange porque es subjetiva (ver scicomp.stackexchange.com/help/dont-ask ) . Mientras las respuestas sean concretas y se centren en casos de uso específicos (sin repeticiones significativas o superposición), creo que vale la pena mantenerse abierto.
Geoff Oxberry
@Jeff: La pregunta me parece una encuesta. No niego que sea valioso, pero no veo que haya una respuesta aceptada. ¿Sería una pregunta fuera de lo común para el foro MPI?
Jack Poulson
@JackPoulson No quiero saber qué piensan los implementadores que es la respuesta correcta; Quiero saber qué necesitan los científicos computacionales. A este respecto, la pregunta tiene respuestas objetivas. No hay una respuesta correcta, pero eso no significa que sea una situación subjetiva.
Jeff

Respuestas:

17

Permítanme primero responder por qué creo que las interfaces C ++ a MPI generalmente no han tenido demasiado éxito, después de haber pensado en el problema durante un buen tiempo al tratar de decidir si deberíamos usar los enlaces C estándar de MPI o construir en algo de nivel superior :

Cuando nos fijamos en los códigos MPI del mundo real (por ejemplo, PETSc, o en mi caso. II), uno encuentra que quizás sorprendentemente, el número de llamadas MPI no es realmente muy grande. Por ejemplo, en las 500k líneas de acuerdo. II, solo hay ~ 100 llamadas MPI. Una consecuencia de esto es que el dolor que implica el uso de interfaces de nivel inferior, como los enlaces MPI C, no es demasiado grande. Por el contrario, uno no ganaría mucho utilizando interfaces de nivel superior.

Mi segunda observación es que muchos sistemas tienen múltiples bibliotecas MPI instaladas (diferentes implementaciones de MPI o diferentes versiones). Esto plantea una dificultad significativa si desea utilizar, por ejemplo, boost :: mpi que no solo consiste en archivos de encabezado: o bien debe haber múltiples instalaciones de este paquete, o uno necesita construirlo como parte del proyecto que usa boost :: mpi (pero eso es un problema en sí mismo nuevamente, dado que boost usa su propio sistema de compilación, que es diferente a cualquier otra cosa).

Así que creo que todo esto ha conspirado contra la actual cosecha de interfaces C ++ a MPI: los viejos enlaces MPI C ++ no ofrecían ninguna ventaja, y los paquetes externos tenían dificultades con el mundo real.

Todo esto dicho, esto es lo que creo que serían las características asesinas que me gustaría tener desde una interfaz de nivel superior:

  • Debería ser genérico. Tener que especificar el tipo de datos de una variable definitivamente no es similar a C ++. Por supuesto, también conduce a errores. La clase MpiMap de Elemental ya sería un buen primer paso (aunque no puedo entender por qué diablos la MpiMap::typevariable no es constante estática, por lo que se puede acceder sin crear un objeto).

  • Debe tener facilidades para transmitir tipos de datos arbitrarios.

  • Las operaciones que requieren un MPI_Opargumento (p. Ej., Reducciones) deben integrarse bien con la std::functioninterfaz de C ++ , de modo que sea fácil pasar un puntero de función (¡o un lambda!) En lugar de tener que registrar algo torpemente.

boost :: mpi en realidad satisface todo esto. Creo que si fuera una biblioteca de solo encabezado, sería mucho más popular en la práctica. También ayudaría si admitiera funciones posteriores a MPI 1.0, pero seamos honestos: esto cubre la mayor parte de lo que necesitamos la mayoría del tiempo.

Wolfgang Bangerth
fuente
Uno de los problemas con la serialización en Boost :: MPI es que no funciona con un solo lado (también conocido como RMA). ¿Cree que será posible crear tipos de datos MPI para los objetos C ++ que interesan a los usuarios? Por supuesto, en teoría todo debería ser compatible, pero prefiero comenzar con un objetivo más pragmático.
Jeff
Creo que es un error pensar que el tipo de datos serializados puede funcionar con comunicaciones unilaterales. Esto supone una visión de que la serialización solo implica empaquetar datos en una cadena y, por otro lado, desempaquetarlos nuevamente. Pero las funciones de serialización pueden hacer mucho más (por ejemplo, rastrear punteros a otros objetos, contar bytes que han sido serializados, etc.) de lo que uno puede esperar que funcione si no puede ejecutar nada en el host de destino. Mi punto de vista es que la serialización de estilo C ++ y la comunicación unilateral es simplemente una no iniciación. Creo que nadie debería esperar que esto funcione, por lo que se extrañaría muy poco.
Wolfgang Bangerth
Hola Wolfgang, tienes razón en que MpiMap sería más elegante si usara una variable miembro de constante estática. He reorganizado la implementación: github.com/poulson/Elemental/commit/…
Jack Poulson
Sí, mucho mejor :-)
Wolfgang Bangerth
+1 sobre no muchas llamadas mpi @WolfgangBangerth. Fundamentalmente, se supone que mpi hace que los cálculos sean más rápidos, ¡desea minimizar las llamadas mpi porque le cuestan!
Charles
6

Para que la pelota ruede, estas son dos de mis necesidades:

  • La interfaz debe poder eliminar argumentos redundantes o innecesarios, por ejemplo, MPI_IN_PLACE.
  • La interfaz debe detectar automáticamente los tipos de datos incorporados en MpiMap de Elemental.
  • Si / siempre que sea posible, los tipos de datos definidos por el usuario deben construirse para las clases.
Jeff
fuente
5

Mi lista en ningún orden particular de preferencia. La interfaz debe:

  • ser solo encabezado, sin dependencias pero <mpi.h>, y la biblioteca estándar,
  • ser genérico y extensible
  • no bloquee solo (si desea bloquear, bloquee explícitamente, no de forma predeterminada),
  • permitir el encadenamiento basado en la continuación de operaciones sin bloqueo,
  • admite serialización extensible y eficiente (como Boost.Fusion, de modo que funcione con RMA),
  • tener penalización de abstracción cero (es decir, ser al menos tan rápido como la interfaz C),
  • estar a salvo (el destructor de un futuro no preparado se llama? -> std :: terminate!),
  • tener un DEBUGmodo fuerte con toneladas de afirmaciones
  • extremadamente seguro para escribir (no más ints / void * para todo, ¡diablos, quiero que las etiquetas sean tipos!),
  • debería funcionar con lambdas (p. ej., todos reducen + lambda),
  • use excepciones consistentemente como mecanismo de informe y manejo de errores (¡no más códigos de error! ¡No más argumentos de salida de función!
  • MPI-IO debería ofrecer una interfaz de E / S sin bloqueo al estilo de Boost.AFIO,
  • y simplemente siga las buenas prácticas modernas de diseño de interfaz C ++ (defina tipos regulares, funciones que no sean amigos y que no sean amigos, juegue bien con la semántica de movimientos, soporte de operaciones de rango, ...)

Extras:

  • permítame elegir el ejecutor del entorno MPI, es decir, qué grupo de subprocesos utiliza. En este momento, puede tener aplicaciones con una combinación de OpenMP, MPI, CUDA y TBB ... todo al mismo tiempo, donde cada tiempo de ejecución cree que es el propietario del entorno y, por lo tanto, solicita subprocesos al sistema operativo cada vez que lo desee. eso. ¿Seriamente?

  • use la convención de nomenclatura STL (y Boost). ¿Por qué? Todo programador de C ++ lo sabe.

Quiero escribir código como este:

auto buffer = some_t{no_ranks};
auto future = gather(comm, root(comm), my_offsets, buffer)
              .then([&](){
                /* when the gather is finished, this lambda will 
                   execute at the root node, and perform an expensive operation
                   there asynchronously (compute data required for load 
                   redistribution) whose result is broadcasted to the rest 
                   of the communicator */
                return broadcast(comm, root(comm), buffer);
              }).then([&]() {
                /* when broadcast is finished, this lambda executes 
                   on all processes in the communicator, performing an expensive
                   operation asynchronously (redistribute the load, 
                   maybe using non-blocking point-to-point communication) */
                 return do_something_with(buffer);
              }).then([&](auto result) {
                 /* finally perform a reduction on the result to check
                    everything went fine */
                 return all_reduce(comm, root(comm), result, 
                                  [](auto acc, auto v) { return acc && v; }); 
              }).then([&](auto result) {
                  /* check the result at every process */
                  if (result) { return; /* we are done */ }
                  else {
                    root_only([](){ write_some_error_log(); });
                    throw some_exception;
                  }
              });

/* Here nothing has happened yet! */

/* ... lots and lots of unrelated code that can execute concurrently 
   and overlaps with communication ... */

/* When we now call future.get() we will block 
   on the whole chain (which might have finished by then!).
*/

future.get();

Piense cómo se podrían encadenar todas estas operaciones usando los MPI_C request. Tendría que probar en múltiples (o todos) pasos intermedios a través de una gran cantidad de código no relacionado para ver si puede avanzar su cadena sin bloquear .

gnzlbg
fuente
Esta es una lista intensa de requisitos. Algunos de ellos son imposibles para todos los fines prácticos (por ejemplo, apoyar lambdas en reducciones). Sin embargo, en general creo que es el tipo de cosas que la comunidad MPI debería aspirar a apoyar.
Jeff
En cuanto a los subprocesos y el entorno de tiempo de ejecución, MPI no usa subprocesos o subprocesos del sistema operativo (POSIX, Windows o Solaris, dependiendo del sistema operativo) internamente. No estoy muy seguro de entender el requisito aquí.
Jeff
El problema es que MPI puede solicitar subprocesos arbitrarios del sistema operativo. Mi aplicación tiene un grupo de subprocesos y me gustaría que MPI solicite esos subprocesos de mi grupo de subprocesos. Actualmente, esto no es posible (y generalmente no es un problema), a menos que tenga una aplicación que use OpenMP, TBB y MPI, cada una con sus propios grupos de subprocesos, cada uno con 4 veces más núcleos.
gnzlbg
1
MPI puede pero generalmente no usa hilos de SO internamente. En todos los casos con los que estoy familiarizado (MPICH (2), MVAPICH2, OpenMPI, CrayMPI), solo una opción de tiempo de ejecución proporcionada por el usuario hace que esto ocurra, es decir, el valor predeterminado es que no lo hace. Blue Gene / Q MPI es una excepción, pero de tal forma que no es relevante para esta discusión.
Jeff
Gracias @ Jeff! ¿Podría explicar cómo MPI maneja múltiples llamadas sin bloqueo mientras usa un solo hilo? ¿Utiliza corutinas / hilos verdes / fibras?
gnzlbg
2

Personalmente, no me importa llamar a funciones largas de estilo C por la razón exacta que mencionó Wolfgang; realmente hay pocos lugares a los que deba llamarlos e incluso entonces, casi siempre quedan envueltos por algún código de nivel superior.

Las únicas cosas que realmente me molestan con el MPI de estilo C son los tipos de datos personalizados y, en menor grado, las operaciones personalizadas (porque las uso con menos frecuencia). En cuanto a los tipos de datos personalizados, diría que una buena interfaz C ++ debería ser capaz de soportar una forma genérica y eficiente de manejar esto, probablemente a través de la serialización. Por supuesto, esta es la ruta que boost.mpiha tomado, que si tiene cuidado , es un gran ahorro de tiempo.

En cuanto a boost.mpitener dependencias adicionales (en particular, boost.serializationque no es solo de encabezado), recientemente me encontré con una biblioteca de serialización de C ++ de solo encabezado llamada cereal que parece prometedora; dado que requiere un compilador compatible con C ++ 11. Podría valer la pena analizarlo y usarlo como base para algo similar a boost.mpi.

GradGuy
fuente
Tenga en cuenta que no necesariamente estaba buscando algo para poner en el estándar MPI. Estoy completamente de acuerdo en que MPI casi siempre debe "quedar envuelto en algún código de nivel superior", así que me pregunto qué aspecto tendrá ese código de nivel superior. Elemental tiene un buen enfoque, pero ¿es el mejor para todos los casos? Si uno quisiera tener una interfaz C ++ para MPI que intentara hacer feliz a un gran número de personas, ¿cómo sería?
Jeff
@Jeff. Para mí: 1. Me gusta poder enviar tipos de datos personalizados con facilidad. 2. Me gusta poder realizar reducciones con operaciones personalizadas con facilidad. También creo que 1 me inclino más importante / útil que 2
GradGuy
No veo cómo C ++ hace algo mágico wrt (2). ¿Qué te imaginas aquí?
Jeff
@Jeff Estaba pensando en algo como cómo thrustfunciona para las reducciones: docs.thrust.googlecode.com/hg/group__reductions.html
GradGuy
-1

El proyecto github easyLambda proporciona una interfaz de alto nivel para MPI con C ++ 14.

Creo que el proyecto tiene objetivos similares y dará una idea de las cosas que se pueden hacer y se están haciendo en esta área utilizando C ++ moderno. Guiando otros esfuerzos, así como easyLambda en sí.

Los puntos de referencia iniciales sobre el rendimiento y las líneas de código han mostrado resultados prometedores.

ingrese la descripción de la imagen aquí

A continuación se incluye una breve descripción de las características y la interfaz que proporciona.

La interfaz se basa en la programación del flujo de datos y las operaciones de la lista funcional que proporcionan paralelismo inherente. El paralelismo se expresa como propiedad de una tarea. La asignación del proceso y la distribución de datos para la tarea se pueden solicitar con una propiedad .prll (). Hay un buen número de ejemplos en la página web y en el repositorio de código que incluyen el procesamiento posterior de dinámica molecular LAMMPS, la solución explícita de diferencia finita a la ecuación de calor, la regresión logística, etc. Como ejemplo, el problema de difusión de calor discutido en el artículo HPC está muriendo ... se puede expresar en ~ 20 líneas de código.

Espero que esté bien dar enlaces en lugar de agregar más detalles y códigos de ejemplo aquí.

Descargo de responsabilidad: soy el autor de la biblioteca. Creo que no estoy haciendo ningún daño al esperar obtener una retroalimentación constructiva sobre la interfaz actual de easyLambda que podría ser ventajosa para easyLambda y cualquier otro proyecto que persiga objetivos similares.

Utkarsh Bhardwaj
fuente
La pregunta dice " Estamos buscando respuestas largas que brinden alguna explicación y contexto. No solo dé una respuesta de una línea; explique por qué su respuesta es correcta, idealmente con citas. Las respuestas que no incluyen explicaciones pueden eliminarse . ". ¿Por qué crees que tu respuesta es lo suficientemente completa como para ajustarse a esta descripción?
nicoguaro
Estoy apuntando hacia un proyecto que proporciona una interfaz similar a lo que pide el OP. Puedo dar detalles de la motivación y las características del proyecto en la respuesta misma y dejar que el OP y otros lean y sugieran lo que piensan al respecto. Sin embargo, elegí dar solo enlaces. Entiendo tu punto. Permítanme agregar un texto con referencias a la respuesta.
Utkarsh Bhardwaj
@UtkarshBhardwaj: Algunos comentarios: (1) Gracias por escribir una biblioteca que interconecte C ++ con MPI. Es una tarea importante y parece que has trabajado mucho. (2) Examinando rápidamente los documentos (de nuevo, buen trabajo), lo que destaca son las largas cadenas de comandos de métodos utilizados, lo que parece estilísticamente ... doloroso de depurar, aunque los haya formateado bien. (3) Las abstracciones suponen un paradigma funcional, que parece útil para tareas de reducción de mapas, pero como alguien que programa en MPI, no quiero volver a trabajar mis algoritmos y alejarme demasiado de las interfaces que conozco.
Geoff Oxberry
(4) No veo comunicadores en ninguna parte del ejemplo, lo que me lleva a sospechar que está usando MPI_COMM_WORLD para todo y limita el potencial de su biblioteca. (5) Las abstracciones parecen basarse en las abstracciones de MPI y podrían tener un backend diferente. (6) Basado en (3) - (5), no creo que esta biblioteca sea una interfaz MPI, y creo que este comentario está fuera de tema como resultado.
Geoff Oxberry
@ Geoff gracias por los valiosos comentarios, lo aprecio mucho. Responda a puntos específicos: 2) El encadenamiento a veces se denomina ExpressionBuilder . Es una forma común de expresar la composición en un estilo funcional. No es necesario escribir en este estilo en ezl. Es posible escribir unidades individuales primero y luego componerlas, verifique el [ejemplo] ( goo.gl/YzaL0k ). 3) Sé que es difícil pasar del flujo de control e imperativo al flujo de datos y funcional. Cada uno tiene sus pros y contras. Sin embargo, creo que este último necesita ser explorado más para conocer su verdadera eficacia.
Utkarsh Bhardwaj