Até que ponto é genérica e metaprogramação usando modelos C ++ útil na ciência da computação?

17

A linguagem C ++ fornece programação genérica e metaprogramação por meio de modelos. Essas técnicas chegaram a muitos pacotes de computação científica em larga escala (por exemplo, MPQC , LAMMPS , CGAL , Trilinos ). Mas o que eles realmente contribuíram para a computação científica em valor que vai além de linguagens não genéricas e não meta, como C ou Fortran, em termos de tempo geral de desenvolvimento e usabilidade, para uma eficiência igual ou adequada?

Dada uma tarefa de computação científica, a programação genérica e a metaprogramação por meio de modelos C ++ demonstraram uma melhoria na produtividade, expressividade ou usabilidade medida por quaisquer parâmetros de referência bem compreendidos (linhas de código, esforço pessoal, etc ...)? Da mesma forma, quais riscos estão associados ao uso de modelos C ++ para genéricos e metaprogramação?

Último folego
fonte
Estou preocupado que essa pergunta possa ser muito aberta a opiniões, mas quero ver o que as pessoas da comunidade dizem.
precisa
11
Eu apaguei completamente o descarrilamento do comentário. Se você gostaria que ele fosse anunciado para bate-papo ou meta, estou feliz em fazê-lo. Veja minha meta aqui .
Aron Ahmadia 24/03/12
3
Além disso, consulte a pergunta relacionada aqui sobre conselhos sobre quando usar e quando evitar modelos de expressão .
Aron Ahmadia 24/03/12
Eu não acho correto dizer que o LAMMPS usa modelos ou metaprogramação. LAMMPS é um código orientado a objetos que se parece muito com o Fortran na maioria das vezes. Eu não acho que o MPQC também tenha muitos modelos, mas é fortemente orientado a objetos e polimórfico.
Jeff
11
O OpenFOAM faz uso intenso de modelos e outros recursos do C ++.
Dohn Joe

Respostas:

14

Penso que, em geral, a metaprogramação de modelos foi considerada inutilizável na prática - compila muito lentamente e as mensagens de erro que recebemos são impossíveis de decifrar. A barreira de entrada para os recém-chegados também é muito alta ao usar a metaprogramação.

É claro que a programação genérica é uma questão totalmente diferente, como testemunhado por Trilinos, deal.II (minha própria biblioteca), DUNE e muitas outras bibliotecas - expressar o mesmo conceito operando em diferentes tipos de dados é um acéfalo, e a comunidade o aceitou amplamente desde que permaneça dentro dos limites que evitam os problemas da metaprogramação. Eu acho que a programação genérica se qualifica como um sucesso óbvio.

Obviamente, nenhum desses tópicos é imediatamente conectado ao POO. OOP, novamente, eu diria, universalmente aceito pela comunidade científica da computação. Ainda menos que a programação genérica, não é um tópico de debate: toda biblioteca de sucesso escrita nos últimos 15 anos (seja em C ++, C ou Fortran) usa técnicas de POO.

Wolfgang Bangerth
fonte
4
O tmp pode ser difícil para usuários iniciantes, mas geralmente é feito muito bem dentro da biblioteca. É uma daquelas técnicas que podem realmente reduzir a quantidade de código, mas você realmente precisa saber o que está fazendo. Se você não acredita em mim, leia a fonte da Eigen ou Elemental. Seu código bonito que sem modelos seria praticamente impossível.
251 aterrel
5
Claro, é uma técnica com valor. Mas é difícil de manter e geralmente é difícil de usar se exposto à interface externa. Dito isso, acho que uma das razões pelas quais o TMP não foi o sucesso esperado pelas pessoas inicialmente é que os compiladores se tornaram muito bons. O TMP vive do fato de que muitas coisas são conhecidas em tempo de compilação e, em seguida, podem ser propagadas como constantes no código real. Mas os compiladores tornaram-se bastante bons em inlining, clonagem de funções etc., e assim uma parte significativa do benefício pode ser obtida hoje usando programação "normal".
Wolfgang Bangerth
15

Deixe-me dar um exemplo baseado na experiência. A maioria das bibliotecas que uso diariamente usa o OOP de alguma forma. OOP é capaz de esconder a complexidade necessária para muitos domínios, não é um mecanismo que realmente ajuda no desempenho. O que pode acontecer é que uma biblioteca é capaz de usar otimizações específicas com base na hierarquia de objetos, mas a maior parte é sobre ocultar a complexidade do usuário. Pesquise os Padrões de Design, eles são os mecanismos frequentemente empregados para realizar essa ocultação de complexidade.

Tome o PETSc como exemplo. O PETSc usa um modelo inspetor / executor de OOP, em que qualquer um de seus algoritmos analisa as rotinas disponíveis em um determinado objeto e escolhe o que executar para realizar a rotina. Isso permite que o usuário separe preocupações, por exemplo, a ação da matriz pode incluir qualquer tipo de rotina bloqueada ou otimizada e ser efetivamente usada por vários solucionadores iterativos. Ao dar ao usuário a capacidade de especificar seus próprios tipos de dados e avaliações, eles recebem algumas rotinas importantes e também têm toda a funcionalidade da biblioteca ainda disponível.

Outro exemplo que darei é FEniCS e deal.II. Ambas as bibliotecas usam OOP para generalizar um grande número de métodos de elementos finitos. Em tudo, desde o tipo de elemento, a ordem dos elementos, a representação em quadratura e assim por diante, é intercambiável. Embora essas duas bibliotecas sejam "mais lentas" do que alguns códigos FEM estruturados para fins especiais, eles podem resolver uma ampla variedade de problemas com grande parte da complexidade do FEM desconhecida pelo usuário.

Meu exemplo final é Elemental. Elemental é uma nova biblioteca de álgebra linear densa que levou a dificuldade de gerenciar comunicadores MPI e localização de dados para uma construção de linguagem muito simples. O resultado é que, se você tiver um código de série FLAME, alterando os tipos de dados, também poderá ter um código paralelo via Elemental. Ainda mais interessante, você pode brincar com a distribuição de dados configurando uma distribuição igual a outra.

OOP deve ser pensado como uma maneira de gerenciar a complexidade, não um paradigma para competir com a montagem enrolada manualmente. Se o fizer mal, resultará em muita sobrecarga, portanto, é preciso manter o tempo e atualizar os mecanismos com os quais eles a usam.

aterrel
fonte
3

O que os recursos de linguagem OOPfazem para a computação científica são declarações de código mais compactas, o que ajuda a entender e usar melhor o código. Por exemplo, as FFTrotinas precisam conter um grande número de argumentos para cada chamada de função, tornando o código complicado.

Usando moduleou classdeclarações, apenas o que é necessário no momento da chamada pode ser passado, pois o restante dos argumentos pertence à configuração do problema (ou seja, tamanho das matrizes e coeficientes).

Na minha experiência, recebi SUBROUTINEligações com 55 argumentos (entrada e saída) e reduzi para 5, tornando o código melhor.

Isso é valor.

ja72
fonte
3

Sou um forte defensor da programação genérica e da metaprogramação para computação científica. Na verdade, estou desenvolvendo uma biblioteca C ++ de software livre para métodos Galerkin, com base nessas técnicas, chamada Feel ++ (http://www.feelpp.org), que está cada vez mais ganhando força. É verdade que ainda existem dificuldades como tempos de compilação lentos e que a curva de aprendizado pode ser acentuada se alguém quiser entender o que está acontecendo nos bastidores. No entanto, isso é extremamente interessante e alucinante. Se feito no nível da biblioteca e ocultando a complexidade por trás de uma linguagem específica de domínio, você obtém uma ferramenta extremamente poderosa. Temos à nossa disposição uma ampla variedade de métodos para usar e comparar. Para o ensino da computação científica, isso é incrível, também para pesquisas e novos métodos numéricos, para aplicativos de grande escala, bem, nós trabalhamos nisso, mas até agora tudo bem, já podemos fazer algumas coisas legais. Temos engenheiros, físicos e matemáticos usando: a maioria deles apenas usa a linguagem para formulação variacional e está feliz com isso. Olhando para algumas das formulações que nossos colegas físicos manipulam, eu não gostaria de vê-las feitas "à mão" sem uma linguagem de alto nível para descrever a formulação variacional. Pessoalmente, considero que essas "técnicas" ou "paradigmas" agora são necessárias para lidar com a complexidade do código de computação científica, tendo que multiplicar o tamanho do código por um grande fator. Provavelmente, é necessário melhorar o suporte à metaprogramação em C ++, mas ele já está em boa forma, especialmente desde o C ++ 11.

christophe Prud'homme
fonte
2

Você pode encontrar o artigo http://arxiv.org/abs/1104.1729 relevante para sua pergunta. Ele discute modelos de expressão (uma aplicação específica da meta-programação de modelos usada no código científico) da perspectiva do desempenho.

Juan M. Bello-Rivas
fonte
Esse papel me deixa louco. Compare o Fortran simples mais rápido que você tem com o MKL e ele também perderá. A montagem ajustada à mão não é algo que se deseja, é o que você faz quando cada nanossegundo conta e pode ser reutilizado por um número muito grande de pessoas.
aterrel
@aterrel: Este é precisamente o contraste que estou pensando. Sabendo que você precisará fazer a otimização manual como o último estágio de desenvolvimento, qual idioma você escolheria como base para usar antes do último estágio? Temos dados concretos para sugerir qual idioma escolher?
Death Breath
9
@ Deathbreath: vou repetir uma resposta que fiz em vários outros tópicos também - que, em geral, ajustar o último pedaço de velocidade do seu código é algo que você faz muito raramente. Mas você programa algoritmos de alto nível o tempo todo. Portanto, escolha o idioma que permite que você faça grandes coisas rapidamente. Sempre existe uma maneira de incluir coisas de baixo nível de alguma forma, mas não deve ser a coisa que determina sua escolha da linguagem de programação.
Wolfgang Bangerth
0

Os modelos são muito bons na remoção de verificações de tipo / domínio em tempo de execução. Eles podem ser resolvidos no momento da compilação. Em teoria, isso pode aumentar o desempenho sobre o mesmo tipo de implementação em C ou Fortran, onde a verificação de tipo só pode ser feita em tempo de execução - as verificações são implementadas no código-fonte. No entanto, você pode obter os mesmos resultados em C usando as opções de pré-compilador, mas elas precisam ser feitas manualmente, diferentemente dos modelos.

No entanto, os modelos também podem gerar uma sobrecarga significativa. Geralmente, eles podem criar inchaço no código, o que pode afetar o uso do cache de instruções. Além disso, abordagens genéricas geralmente podem atrapalhar o compilador durante a otimização - nem sempre é fácil para a análise de código quando se usa abordagens genéricas. Sempre existe um problema com a automação - incluindo a otimização do compilador - muitas vezes o código de saída não é compatível com o cache.

Os benefícios da verificação de tipo / domínio, embora certamente mais seguros, são os únicos benefícios reais que posso ver em termos de desempenho e, geralmente, são imperceptíveis. Mas, como eu disse, o efeito geral pode ser negativo dependendo do que você está fazendo. É por isso que geralmente é melhor otimizar manualmente seu código onde você tem gargalos significativos.

cdcdcd
fonte