Por que a equipe da LMAX usou Java e projetou a arquitetura para evitar o GC a todo custo?

24

Por que a equipe da LMAX projetou o LMAX Disruptor em Java, mas todos os seus pontos de design para minimizar o uso do GC? Se alguém não deseja que o GC seja executado, por que usar um idioma de coleta de lixo?

Suas otimizações, o nível de conhecimento de hardware e o pensamento que colocam são impressionantes, mas por que Java?

Eu não sou contra Java ou qualquer coisa, mas por que uma linguagem GC? Por que não usar algo como D ou qualquer outro idioma sem o GC, mas permite um código eficiente? Será que a equipe está mais familiarizada com Java ou o Java possui alguma vantagem exclusiva que não estou vendo?

Digamos que eles o desenvolvam usando D com gerenciamento manual de memória, qual seria a diferença? Eles teriam que pensar em um nível baixo (o que já são), mas podem obter o melhor desempenho do sistema, pois é nativo.


fonte
6
Sei muito pouco sobre esse projeto, mas parece que é algum tipo de estrutura que outras pessoas podem construir. E se você conseguir escrever isso em Java (e permitir que outras pessoas codifiquem em Java e colher os benefícios), terá uma "base de clientes" MUITO maior do que se tivesse escrito em D.
Joachim Sauer
6
@kadaj: realmente não importa se o consumidor é público ou interno: se você o tornar acessível em uma linguagem amplamente conhecida, será mais útil, mesmo para o desenvolvimento interno. Se você iniciar seu argumento (hipotético) com: "Suponha que todo mundo conhece D e conhece Java, ...", provavelmente está perdendo alguma coisa.
Joachim Sauer
6
Algumas pessoas gostam de usar martelos para todos os tipos de problemas. Você tem uma aresta áspera que deseja aplainar, bata com o martelo até que fique liso. Tem um parafuso que você precisa enfiar, bata nele com um martelo até entrar. Tem um ornamento delicado que você precisa lixar, bata com um martelo e depois culpe o ornamento por "chupar". C ou C ++ teria sido uma escolha melhor que D, se apenas para a base de conhecimento existente. Não sei por que você mencionou D como exemplo TBH.
Gbjbaanb
2
@gbjbaanb Mencionei D porque ele fornece coleta de lixo (nos casos em que abstrações de alto nível são necessárias e mexer com a memória é muito difícil para o cérebro), mas também permite o gerenciamento manual de memória com malloc no estilo C e gratuito. D é como Objective-C com ARC (sem GC real), mas melhor que isso. Mas sim, C / C ++ caberia na conta.
4
@kadaj Vejo que você está recebendo críticas por falar sobre D, mas quero dizer que estou decepcionado com o tom que os outros estão usando e explique por que acho que D é central para a pergunta em questão. Embora D não seja amplamente utilizado, D fornece algumas construções de alto nível que eu poderia esperar encontrar em Java ou C #, mas não em C ++ (pelo menos no estilo antigo). Ele ainda fornece mixagens gerenciadas e não gerenciadas - que é praticamente o único idioma que conheço para fazer isso! Portanto, D não é apenas uma escolha de estimação, mas uma que tem objetivos coincidentes com as perguntas originais em torno da GC.
J Trana

Respostas:

20

Porque existe uma enorme diferença entre otimizar o desempenho e desativar completamente uma segurança

Ao reduzir o número de GC, sua estrutura é mais responsiva e pode ser executada (presumivelmente) mais rapidamente. Agora, otimizar o coletor de lixo não significa que eles nunca façam uma coleta de lixo. Significa apenas que eles fazem isso com menos frequência e, quando o fazem, é executado muito rápido. Esse tipo de otimização inclui:

  1. Minimizar o número de objetos que se deslocam para um espaço sobrevivente (isto é, que sobreviveram a pelo menos uma coleta de lixo) usando pequenos objetos descartáveis. Objetos que foram movidos para o espaço do sobrevivente são mais difíceis de coletar e uma coleta de lixo aqui implica em congelar toda a JVM.
  2. Não aloque muitos objetos para começar. Isso pode sair pela culatra se você não tomar cuidado, pois os objetos da geração jovem são super baratos para alocar e coletar.
  3. Certifique-se de que o novo objeto aponte para o antigo (e não o contrário), para que o objeto jovem seja fácil de coletar, pois não há nenhuma referência a eles que fará com que eles sejam mantidos

Quando você diminui o desempenho, geralmente ajusta algum "hot spot" muito específico, ignorando o código que não é executado com frequência. Se você fizer isso em Java, poderá deixar o coletor de lixo ainda cuidar dos cantos escuros (já que não fará muita diferença) enquanto otimiza com muito cuidado a área que executa em um loop apertado. Assim, você pode escolher onde otimizar e onde não, e assim concentrar seu esforço no que interessa.


Agora, se você desativar completamente a coleta de lixo, não poderá escolher. Você deve descartar manualmente todos os objetos, sempre. Esse método é chamado no máximo uma vez por dia? Em Java, você pode deixar, pois seu impacto no desempenho é insignificante (pode ser bom permitir que um GC completo ocorra todos os meses). No C ++, você ainda está vazando recursos, portanto, você deve cuidar mesmo desse método obscuro. Portanto, você deve pagar o preço pelo gerenciamento de recursos em cada parte única do seu aplicativo, enquanto em Java você pode se concentrar.


Mas piora.

E se você tiver um bug, digamos no canto escuro do seu aplicativo, que só é acessado na segunda-feira na lua cheia? Java tem uma forte garantia de segurança. Há pouco ou nenhum "comportamento indefinido". Se você usar algo errado, uma exceção será lançada, o programa será interrompido e nenhuma corrupção de dados ocorrerá. Então você tem certeza de que nada de errado pode acontecer sem você perceber.

Mas em algo como D, você pode ter um acesso incorreto ao ponteiro ou um estouro de buffer e pode corromper sua memória, mas seu programa não saberá (você desligou a segurança, lembra-se?) E continuará funcionando com as informações incorretas. dados, faça algumas coisas bastante desagradáveis ​​e corrompa seus dados, e você não sabe, e à medida que mais corrupção acontece, seus dados ficam cada vez mais errados e, de repente, eles se quebram, e estavam em um aplicativo essencial à vida, e ocorreu algum erro no cálculo de um foguete, e por isso não funciona, o foguete explode e alguém morre, e sua empresa está na primeira página de todos os jornais e seu chefe aponta o dedo para você dizendo "Você está o engenheiro que sugeriu que usamos o D para otimizar o desempenho, por que você não pensou em segurança?". E a culpa é sua. Você matou essas pessoas com sua tentativa tola de desempenho.


OK, ok, na maioria das vezes é muito menos dramático do que isso. Mas mesmo um aplicativo essencial para os negócios ou apenas um aplicativo de GPS ou, digamos, um site de assistência médica do governo pode gerar algumas conseqüências bastante negativas se você tiver bugs. Usar uma linguagem que os impeça completamente ou que falhem rapidamente quando eles acontecem geralmente é uma idéia muito boa.

Há um custo para desativar uma segurança. Ser nativo nem sempre faz sentido. Às vezes, é muito mais simples e seguro otimizar um idioma um pouco seguro, para buscar um idioma em que você possa se dar muito bem. Em muitos casos, a correção e a segurança superam os poucos nanossegundos que você teria eliminado ao eliminar completamente o GC. Disruptor pode ser usado nessas situações, então acho que o LMAX-Exchange fez a ligação certa.

Mas e D em particular? Você tem um GC, se quiser, para os cantos escuros, e o subconjunto SafeD (que eu não conhecia antes da edição) remove o comportamento indefinido (se você se lembrar de usá-lo!).

Bem, nesse caso, é uma simples questão de maturidade. O ecossistema Java está cheio de ferramentas bem escritas e bibliotecas maduras (melhor para o desenvolvimento). Muito mais desenvolvedores conhecem Java do que D (melhor para manutenção). Buscar uma linguagem nova e não tão popular para algo tão crítico quanto um aplicativo financeiro não seria uma boa idéia. Com uma linguagem menos conhecida, se você tiver um problema, poucos podem ajudá-lo e as bibliotecas que você encontra tendem a apresentar mais erros, pois foram expostas a menos pessoas.

Portanto, meu último argumento ainda é válido: se você deseja evitar problemas com conseqüências terríveis, fique com opções seguras. Neste ponto da vida de D, seus clientes são as pequenas empresas prontas para assumir riscos loucos. Se um problema pode custar milhões, é melhor ficar mais longe na curva dos sinos da inovação .

Laurent Bourgault-Roy
fonte
2
A postagem original chama especificamente D. Na verdade, há uma grande diferença entre C ++ e D com relação à granularidade de escolha. Mesmo se você optar por gerenciar completamente o subconjunto SafeD, acho que você terá um pouco mais de controle sobre certos aspectos da coleta e do tempo (ativar / desativar, coletar, minimizar). Confira as estratégias da Digital Mars para gerenciamento de memória!
J Trana
2
Lmax passo deliberadamente lado algum da segurança Java fornece
James
Essa seria uma ótima resposta, exceto que o Java não está licenciado para software de missão crítica. Se você tiver um reator anuclear, ele será escrito em C ++ e não em Java, que meio que joga fora todo o aspecto da "segurança".
Gbjbaanb
@gbjbaanb, [citação necessária]. Os padrões / diretrizes de confiabilidade que vi recomendam evitar primeiro o C / C ++ em favor de outros idiomas; e se entrar nelas, use versões altamente restritas dos idiomas (MISRA, etc). E uma vez que você aceita restrições, não vejo por que você não pôde fazer o mesmo com nenhum outro idioma. Se você estava pensando sobre a menção da Java Licence a "não para instalações nucleares" na seção RESTRIÇÕES, parece que isso mudou há algum tempo e agora apenas diz algo semelhante a "ter cuidado, não é nossa responsabilidade". Ainda assim, (...)
hmijail 9/09
(...) o texto original era como as licenças do gcc e do clang: não há garantias para fins específicos. Portanto, você não os usaria para algo que necessitasse de confiabilidade e, em vez disso, precisaria usar algum compilador certificado, se não for até um idioma específico para o trabalho (Ada?).
precisa saber é
4

Parece que o motivo pelo qual ele foi escrito em Java é que eles possuem conhecimento interno em Java e provavelmente foi escrito (embora ainda esteja em desenvolvimento ativo) antes que o C ++ agisse junto com o C ++ 0x / 11.

Seu código é realmente apenas Java pelo nome, eles usam sun.misc.Unsafe bastante que meio que derrota o ponto de Java e a segurança é supostamente concedida. Eu escrevi uma porta C ++ do Disruptor e ela supera o código Java fornecido (não gastei muito tempo ajustando a JVM).

Dito isto, os princípios que o disruptor segue não são específicos do idioma, por exemplo, não espere código C ++ de baixa latência que aloque ou libere do heap.

James
fonte
Você pode apontar para sua implementação? Eu vi algumas dessas reimplementações que alegaram um desempenho mais alto, mas ambas trapacearam com simplificações: por exemplo, conectar 1 produtor + 1 consumidor em vez de ser multiprodutor / consumidor capaz como o Disruptor original. O próprio autor do Disruptor mencionou em um tópico dos Grupos do Google que o desempenho poderia ser melhorado com os parâmetros de conexão na versão Java.
precisa saber é
4

Essa pergunta afirma uma premissa incorreta como fato e, em seguida, argumenta sobre essa premissa incorreta.

Vamos nos aprofundar nisso ... "todos os seus pontos de design para minimizar o uso do GC" - simplesmente não é verdade. A inovação no disruptor tem pouco a ver com a GC. O disruptor funciona porque seu design considera inteligentemente como os computadores modernos funcionam - algo muito menos comum do que se poderia esperar. Veja a palestra de Cliff Click http://www.azulsystems.com/events/javaone_2009/session/2009_J1_HardwareCrashCourse.pdf para uma discussão.

É sabido que a LMax é cliente da Azul. Sei em primeira mão que os GCs da Azul são simplesmente uma não emissão - mesmo com montes de 175 GB.

peterbooth
fonte
Há um grão de verdade nisso. Eles reiniciam a VM todas as noites para evitar uma grande coleção. De qualquer forma, foi o que Martin Fowler escreveu e ele não é burro: "Como o resto do sistema, os disruptores são devolvidos da noite para o dia. Esse retorno é feito principalmente para limpar a memória, para que haja menos chances de um evento caro de coleta de lixo durante as negociações". martinfowler.com/articles/lmax.html
JimmyJames
2
Não é bem assim. Costumávamos acionar um GC manual todas as noites em um intervalo de negociação de 5 minutos e ajustávamos para que esse fosse o único GC importante em um dia. Isso se tornou redundante com o Azul Zing. (Fonte: Eu trabalhava em LMAX até recentemente)
Tom Johnson
@TomJohnson Adoro receber informações privilegiadas. Você está dizendo que a descrição de Martin Fowler está errada? É possível que a solução tenha evoluído ao longo do tempo?
JimmyJames
2
Estou dizendo que ele não estava exatamente correto em alguns detalhes menores. Nunca devolvemos nossos sistemas diariamente, mas fizemos uma limpeza no final do dia.
Tom Johnson
3

Eles teriam que pensar em baixo nível

Acima faz metade da resposta que você está procurando. Você pode encontrar outra metade para concluir o raciocínio não mais do que no blog LMAX :

Embora muito eficiente, pode levar a vários erros, pois é muito fácil estragar tudo ...

Conforme admitido pelos desenvolvedores do LMAX, um código como esse pode ser bastante difícil de desenvolver, entender e depurar - mesmo em Java. Ir um nível mais baixo além do que eles estão agora apenas exacerba esse problema, como apontado no artigo da Wikipedia sobre linguagens de programação de baixo nível :

Um programa escrito em uma linguagem de baixo nível pode ser executado rapidamente, e com um espaço de memória muito pequeno; um programa equivalente em um idioma de alto nível será mais pesado. Os idiomas de baixo nível são simples, mas são considerados difíceis de usar, devido aos inúmeros detalhes técnicos que devem ser lembrados .

Por comparação, uma linguagem de programação de alto nível isola a semântica de execução de uma arquitetura de computador da especificação do programa, o que simplifica o desenvolvimento ...

mosquito
fonte
3

Se você usar Java como uma linguagem de sintaxe e evitar suas bibliotecas JDK, poderá ser tão rápido quanto uma linguagem não GC compilada. O GC não é adequado para sistemas em tempo real, mas é possível desenvolver sistemas em Java que não deixam lixo para trás. Como resultado, o GC nunca é acionado.

Acreditamos que a linguagem e a plataforma Java têm muitas vantagens sobre o C / C ++ e desenvolvemos e comparamos alguns componentes Java de latência ultra baixa para prová-lo. Falamos sobre as técnicas para fazer isso neste artigo: Desenvolvimento Java sem GC .

rdalmeida
fonte
2
Existem coletores de lixo adequados para sistemas em tempo real. O coletor padrão da JVM pode não ser, mas isso não significa que o GC em geral não é adequado em tempo real. Mas o plain malloc/freenão é adequado para tempo real, já que o tempo de alocação é ilimitado devido à fragmentação.
Doval
11
Defendemos o uso de pools de objetos rápidos para tudo, para que nenhuma alocação aconteça após o aquecimento.
rdalmeida 12/06
2

LMAX é uma biblioteca de mensagens entre threads de alto desempenho.

Para ser útil, alguém precisa escrever o código para que cada thread faça um trabalho útil. Dado que o código provavelmente está em Java ou C # e, em seguida, há muito poucas opções de linguagem que se adaptam bem a eles.

Usar C ou C ++ não é uma boa opção, a menos que você queira limitar seus usuários a um único sistema operacional, pois não há um modelo de encadeamento definido neles.

Atualmente, o Java é o padrão para muitos desenvolvimentos de software. Portanto, a menos que você tenha uma boa razão, é a melhor escolha. (Quando em Roma, faça como os romanos…)

A gravação de software de alto desempenho em Java (ou C #) geralmente é feita para provar um ponto…

Ian
fonte
11
O novo C ++ 11 suporta o padrão multithreading ...
Casey
@ Casey, e quantos compiladores C ++ do mundo real o utilizam? E quanto custam esses compiladores. Talvez daqui a 20 anos seja útil, até então você não poderá mais depender disso.
23413 Ian
Disruptor usos sun.misc.Unsafe um pouco o que mostra que você não pode realmente escrever baixa código de latência em Java sem mergulhar seu dedo do pé em terra C
James
3
Gcc suporta tópicos C ++ e é grátis
James
@Ian: 2 anos depois e todos os compiladores usados ​​em geral o suportam;). Mesmo os que são gratuitos.
Rutix