Quais recursos os usuários precisam de uma interface MPI C ++?

28

A versão 3.0 do padrão MPI excluiu formalmente a interface C ++ (ela foi descontinuada anteriormente). Embora as implementações ainda possam suportá-lo, os recursos novos no MPI-3 não possuem uma interface C ++ definida no padrão MPI. Consulte http://blogs.cisco.com/performance/the-mpi-c-bindings-what-happened-and-why/ para obter mais informações.

A motivação para remover a interface C ++ do MPI era não ter valor significativo sobre a interface C. Havia poucas diferenças além de "s / _ / :: / g" e muitos recursos aos quais os usuários do C ++ estão acostumados não foram empregados (por exemplo, determinação automática de tipo por meio de modelos).

Como alguém que participa do Fórum MPI e trabalha com vários projetos C ++ que implementaram sua própria interface C ++ para as funções MPI C, gostaria de saber quais são os recursos desejáveis ​​de uma interface C ++ para MPI. Embora eu não me comprometa com nada, eu estaria interessado em ver a implementação de uma interface MPI C ++ independente que atenda às necessidades de muitos usuários.

E sim, eu estou familiarizado com o Boost :: MPI ( http://www.boost.org/doc/libs/1_54_0/doc/html/mpi.html ), mas ele suporta apenas os recursos MPI-1 e o modelo de serialização seria extremamente difícil de suportar para RMA.

Uma interface C ++ para o MPI que eu gosto é a do Elemental ( https://github.com/poulson/Elemental/blob/master/src/core/imports/mpi.cpp ), para que talvez as pessoas possam fornecer alguns prós e contras de que aproximação. Em particular, acho que o MpiMap resolve um problema essencial.

Jeff
fonte
Não acho que este seja o local apropriado para essa pergunta.
Jack Poulson
Você pode dar algumas razões para isso? Muitas das perguntas do MPI neste site sugerem que as pessoas aqui estão preparadas para responder a essa pergunta. Além disso, 0,2 voto positivo por minuto sugere que outras pessoas não concordam com sua avaliação. De qualquer forma, seria mais útil sugerir um local alternativo para postar isso, se você não gostar do local atual.
21413 Jeff Jeff
A pergunta é valiosa, e acho que poderia obter respostas valiosas em listas de discussão mais amplas sobre ciência da computação, se estiver no escopo. (Talvez um resumo de NA, SIAM-CSE ou até mesmo uma publicação pública no G +?) Essa pergunta pode não ser uma boa opção para um site do Stack Exchange porque é subjetivo (consulte scicomp.stackexchange.com/help/dont-ask ) . Contanto que as respostas sejam concretas e se concentrem em casos de uso específicos (sem repetições ou sobreposições significativas), acho que vale a pena manter em aberto.
precisa
@ Jeff: A pergunta aparece como uma enquete para mim. Não discuto que é valioso, mas não vejo uma resposta aceita. Essa pergunta seria incomum para o fórum do MPI?
Jack Poulson
@JackPoulson Não quero saber o que os implementadores acham que é a resposta certa; Eu quero saber o que os cientistas computacionais precisam. A esse respeito, a pergunta tem respostas objetivas. Não existe uma resposta certa, mas isso não significa que seja uma situação subjetiva.
21413 Jeff

Respostas:

17

Deixe-me primeiro responder por que eu acho que as interfaces C ++ para MPI geralmente não obtiveram muito sucesso, tendo pensado sobre o problema por um bom tempo ao tentar decidir se devemos apenas usar as ligações C padrão do MPI ou desenvolver algo de nível superior :

Quando você olha para os códigos MPI do mundo real (digamos, PETSc ou, no meu caso, II), percebe-se que, surpreendentemente, o número de chamadas MPI não é realmente muito grande. Por exemplo, nas 500 mil linhas de acordo.II, existem apenas ~ 100 chamadas MPI. Uma conseqüência disso é que a dor envolvida no uso de interfaces de nível inferior, como as ligações MPI C, não é muito grande. Por outro lado, não se ganha muito usando interfaces de nível superior.

Minha segunda observação é que muitos sistemas têm várias bibliotecas MPI instaladas (diferentes implementações MPI ou versões diferentes). Isso representa uma dificuldade significativa se você deseja usar, digamos, boost :: mpi, que não consiste apenas em arquivos de cabeçalho: também é necessário que haja várias instalações deste pacote ou é necessário compilá-lo como parte do projeto que usa boost :: mpi (mas isso é um problema em si mesmo novamente, dado que o boost usa seu próprio sistema de compilação, que é diferente de qualquer outro).

Então, acho que tudo isso conspirou contra a atual safra de interfaces C ++ para MPI: as antigas ligações MPI C ++ não ofereciam nenhuma vantagem e os pacotes externos tinham dificuldades com o mundo real.

Tudo isso dito, aqui está o que eu acho que seriam os recursos mais interessantes que eu gostaria de ter em uma interface de nível superior:

  • Deve ser genérico. A necessidade de especificar o tipo de dados de uma variável não é decididamente semelhante ao C ++. Obviamente, isso também leva a erros. A classe MpiMap da Elemental já seria um bom primeiro passo (embora eu não consiga entender por que diabos a MpiMap::typevariável não é const estática, para que possa ser acessada sem a criação de um objeto).

  • Deveria ter facilidades para transmitir tipos de dados arbitrários.

  • Operações que requerem um MPI_Opargumento (por exemplo, reduções) devem se integrar perfeitamente à std::functioninterface do C ++ , para que seja fácil passar apenas um ponteiro de função (ou um lambda!) Ao invés de ter que registrar algo desajeitadamente.

O boost :: mpi realmente satisfaz tudo isso. Eu acho que se fosse uma biblioteca apenas de cabeçalho, seria muito mais popular na prática. Também ajudaria se suportasse funções pós-MPI 1.0, mas sejamos honestos: isso cobre a maior parte do que precisamos na maioria das vezes.

Wolfgang Bangerth
fonte
Um dos problemas com a serialização no Boost :: MPI é que ele não funciona com unilateral (também conhecido como RMA). Você acha que será possível criar tipos de dados MPI para os objetos C ++ nos quais os usuários estão interessados? Obviamente, em teoria, todos devem ser apoiados, mas prefiro começar com um objetivo mais pragmático.
21413 Jeff Jeff
Eu acho que é um erro pensar que o tipo de dados serializados possa funcionar com comunicações unilaterais. Isso pressupõe uma visão de que a serialização envolve apenas empacotar dados em uma sequência e, por outro lado, descompactá-los novamente. Porém, as funções de serialização podem fazer muito mais (por exemplo, rastrear ponteiros para outros objetos, contar bytes que foram serializados etc.) do que se pode esperar que funcione se você não puder executar nada no host de destino. Minha opinião é que a serialização no estilo C ++ e a comunicação unilateral são simplesmente não-iniciantes. Eu acho que ninguém deveria esperar que isso funcionasse, tão pouco seria perdido.
Wolfgang Bangerth
Oi Wolfgang, você está certo de que o MpiMap seria mais elegante se usasse uma variável estática de membro const. Reorganizei a implementação: github.com/poulson/Elemental/commit/…
Jack Poulson
Sim, muito mais agradável :-)
Wolfgang Bangerth
+1 sobre poucas chamadas mpi @WolfgangBangerth. Fundamentalmente, o mpi deve acelerar os cálculos; você deseja minimizar as chamadas de mpi porque elas custam a você!
Charles
6

Para fazer a bola rolar, aqui estão duas das minhas necessidades:

  • A interface deve ser capaz de eliminar argumentos redundantes ou desnecessários, por exemplo, MPI_IN_PLACE.
  • A interface deve detectar automaticamente tipos de dados internos, como o MpiMap da Elemental.
  • Se / sempre que possível, tipos de dados definidos pelo usuário devem ser construídos para classes.
Jeff
fonte
5

Minha lista em nenhuma ordem particular de preferência. A interface deve:

  • somente o cabeçalho, sem nenhuma dependência <mpi.h>, mas a biblioteca padrão,
  • ser genérico e extensível,
  • ser non-blocking única (se você deseja bloquear, em seguida, bloquear explicitamente, não por padrão),
  • permitir encadeamento baseado em continuação de operações sem bloqueio,
  • suporta serialização extensível e eficiente (como o Boost.Fusion, para que funcione com RMA),
  • tem zero de penalidade de abstração (ou seja, pelo menos, tão rápido quanto a interface C),
  • estar seguro (o destruidor de um futuro não pronto é chamado? -> std :: terminate!),
  • tem um DEBUGmodo forte com toneladas de afirmações,
  • extremamente seguro (não há mais ints / void * para tudo, diabos, eu quero que as tags sejam do tipo!),
  • deve funcionar com lambdas (por exemplo, todos reduzem + lambda),
  • use as exceções de forma consistente como mecanismo de relatório e tratamento de erros (sem mais códigos de erro! sem mais argumentos de saída de função!),
  • O MPI-IO deve oferecer uma interface de E / S sem bloqueio no estilo Boost.AFIO,
  • e siga as boas práticas modernas de design de interface C ++ (defina tipos regulares, funções que não são membros e não são amigos, jogam bem com a semântica de movimentos, suportam operações de intervalo, ...)

Extras:

  • permita-me escolher o executor do ambiente MPI, ou seja, qual pool de threads ele usa. No momento, você pode ter aplicativos com uma mistura de OpenMP, MPI, CUDA e TBB ... tudo ao mesmo tempo, em que cada tempo de execução pensa que é o proprietário do ambiente e, portanto, solicita threads ao sistema operacional sempre que lhe apetecer. isto. A sério?

  • use a convenção de nomenclatura STL (e Boost). Por quê? Todo programador de C ++ sabe disso.

Eu quero escrever 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();

Pense em como alguém poderia encadear todas essas operações usando os MPI_C's request. Você precisaria testar em várias etapas intermediárias (ou em todas as etapas) de um monte de código não relacionado para ver se é possível avançar sua cadeia sem bloquear .

gnzlbg
fonte
Esta é uma lista intensa de requisitos. Alguns deles são impossíveis para todos os fins práticos (por exemplo, apoiar lambdas em reduções). No entanto, no geral, acho que é o tipo de coisa que a comunidade MPI deve aspirar a apoiar.
Jeff
Quanto aos encadeamentos e ambiente de tempo de execução, o MPI usa sem encadeamentos ou encadeamentos de SO (POSIX, Windows ou Solaris, dependendo do SO) internamente. Não tenho muita certeza de entender o requisito aqui.
Jeff
O problema é que o MPI pode solicitar arbitrariamente threads do sistema operacional. Meu aplicativo possui um pool de threads e eu gostaria que a MPI solicitasse esses threads do meu pool de threads. No momento, isso não é possível (e normalmente não é um problema), a menos que você tenha um aplicativo usando OpenMP, TBB e MPI, cada um com seus próprios conjuntos de encadeamentos, cada um com um número 4x de núcleos.
precisa
1
O MPI pode, mas geralmente não usa threads do SO internamente. Em todos os casos com os quais eu estou familiarizado (MPICH (2), MVAPICH2, OpenMPI, CrayMPI), apenas uma opção de tempo de execução fornecida pelo usuário faz com que isso ocorra, ou seja, o padrão é que não. O Blue Gene / Q MPI é uma exceção, mas de forma a não ser relevante para esta discussão.
Jeff Jeff
Obrigado @Jeff! Você poderia explicar como o MPI lida com várias chamadas sem bloqueio enquanto usa um único thread? Utiliza corotinas / fios verdes / fibras?
gnzlbg
2

Pessoalmente, não me importo de chamar funções longas no estilo C pelo motivo exato que Wolfgang mencionou; realmente existem poucos lugares em que você precisa chamá-los e, mesmo assim, eles quase sempre ficam envolvidos por algum código de nível superior.

As únicas coisas que realmente me incomodam com o MPI no estilo C são os tipos de dados personalizados e, em menor grau, as operações personalizadas (porque eu as uso com menos frequência). Quanto aos tipos de dados personalizados, eu diria que uma boa interface C ++ deve oferecer suporte à maneira genérica e eficiente de lidar com isso, provavelmente por meio de serialização. É claro que esse é o caminho que boost.mpi, se você for cuidadoso , economiza muito tempo.

Quanto a boost.mpiter dependências extras (principalmente as boost.serializationque não são apenas de cabeçalho), recentemente deparei com uma biblioteca de serialização C ++ de cabeçalho chamada cereal, que parece promissora; concedido, requer um compilador compatível com C ++ 11. Pode valer a pena examiná-lo e usá-lo como base para algo semelhante a boost.mpi.

GradGuy
fonte
Observe que eu não estava necessariamente procurando algo para colocar no padrão MPI. Concordo plenamente que o MPI quase sempre "deve ser envolvido por algum código de nível superior", então o que eu me pergunto é: como é esse código de nível superior? O Elemental tem uma boa abordagem, mas é o melhor para todos os casos? Se alguém quisesse ter uma interface C ++ para MPI que tentasse fazer um número muito grande de pessoas felizes, como seria?
21413 Jeff
@Jeff. Para mim: 1. Gosto de poder enviar tipos de dados personalizados com facilidade. 2. Gosto de poder realizar reduções com operações personalizadas com facilidade. Também acho que 1 eu balanço mais importante / útil que 2
GradGuy
Não vejo como o C ++ faz algo mágico (2). O que você imagina aqui?
Jeff
@ Jeff eu estava pensando algo ao longo das linhas de como thrustfaz isso por reduções: docs.thrust.googlecode.com/hg/group__reductions.html
GradGuy
-1

O projeto github easyLambda fornece uma interface de alto nível para MPI com C ++ 14.

Eu acho que o projeto tem objetivos semelhantes e dará uma idéia das coisas que podem ser e estão sendo feitas nessa área usando o C ++ moderno. Orientando outros esforços, além da própria easyLambda.

Os benchmarks iniciais de desempenho e linhas de código mostraram resultados promissores.

insira a descrição da imagem aqui

A seguir, é apresentada uma breve descrição dos recursos e da interface que ele fornece.

A interface é baseada na programação do fluxo de dados e nas operações da lista funcional que fornecem paralelismo inerente. O paralelismo é expresso como propriedade de uma tarefa. A alocação do processo e a distribuição de dados para a tarefa podem ser solicitadas com uma propriedade .prll (). Há um bom número de exemplos na página da Web e no repositório de códigos que incluem o pós-processamento da dinâmica molecular LAMMPS, solução explícita de diferença finita para equação do calor, regressão logística etc. Como exemplo, o problema de difusão de calor discutido no artigo HPC está morrendo ... pode ser expresso em ~ 20 linhas de código.

Espero que seja bom fornecer links em vez de adicionar mais detalhes e códigos de exemplo aqui.

Disclamer: Eu sou o autor da biblioteca. Acredito que não estou prejudicando a esperança de obter um feedback construtivo sobre a interface atual do easyLambda que possa ser vantajosa para o easyLambda e qualquer outro projeto que busque objetivos semelhantes.

Utkarsh Bhardwaj
fonte
A pergunta diz: " Estamos procurando respostas longas que forneçam alguma explicação e contexto. Não dê apenas uma resposta em uma linha; explique por que sua resposta está correta, idealmente com citações. As respostas que não incluem explicações podem ser removidas . " Por que você acha que sua resposta é completa o suficiente para se encaixar nessa descrição?
nicoguaro
Estou apontando para um projeto que fornece uma interface semelhante ao que o OP está pedindo. Posso dar detalhes da motivação e características do projeto na própria resposta e deixar o OP e outros lerem e sugerirem o que pensam sobre ele. No entanto, eu escolhi dar apenas links. Eu entendi o seu ponto. Deixe-me adicionar um texto com referências à resposta.
Utkarsh Bhardwaj
@UtkarshBhardwaj: Alguns comentários: (1) Obrigado por escrever uma biblioteca que faz interface com C ++ e MPI. É um empreendimento significativo e parece que você colocou muito trabalho nele. (2) Examinando rapidamente os documentos (novamente, bom trabalho), o que se destaca são as longas cadeias de comandos de método usados, que parecem estilisticamente ... dolorosas para depurar, mesmo que você as tenha formatado bem. (3) As abstrações pressupõem um paradigma funcional, que parece útil para tarefas do tipo redução de mapas, mas como alguém que programa no MPI, não quero refazer meus algoritmos e me afastar muito das interfaces que conheço.
Geoff Oxberry
(4) Não vejo comunicadores em nenhum lugar do exemplo, o que me leva a suspeitar que você está usando o MPI_COMM_WORLD para tudo e limita o potencial da sua biblioteca. (5) As abstrações parecem basear-se nas abstrações do MPI e podem ter um back-end diferente. (6) Com base em (3) - (5), não acho que essa biblioteca seja uma interface MPI, e acredito que esse comentário esteja fora do tópico como resultado.
Geoff Oxberry
@ Geoff obrigado pelo feedback valioso, muito aprecio. Responda a pontos específicos: 2) Às vezes, o encadeamento é chamado de ExpressionBuilder . É uma maneira comum de expressar composição em estilo funcional. Não é necessário escrever neste estilo no ezl. É possível escrever unidades individuais primeiro e depois compor, verifique o [exemplo] ( goo.gl/YzaL0k ). 3) Eu sei que é difícil passar do fluxo de controle e imperativo para o fluxo de dados e funcional. Cada um tem seus prós e contras. No entanto, acredito que o último precisa ser mais explorado para conhecer sua verdadeira eficácia.
Utkarsh Bhardwaj