Nativo ou MappedByteBuffer - - vs. simples ol' FileOutputStream desempenho / estabilidade de um arquivo de memória mapeada

StackOverflow https://stackoverflow.com/questions/537295

Pergunta

Eu apoio uma aplicação Java legado que usa arquivos simples (texto simples) para persistência. Devido à natureza da aplicação, o tamanho desses arquivos pode chegar 100s MB por dia, e muitas vezes o fator limitante no desempenho da aplicação é o arquivo IO. Atualmente, o aplicativo usa um simples ol' java.io.FileOutputStream para gravar dados no disco.

Recentemente, tivemos vários desenvolvedores afirmam que o uso de arquivos mapeados na memória, implementados em código nativo (C / C ++) e acessadas via JNI, proporcionaria uma maior performance. No entanto, FileOutputStream já utiliza métodos nativos para os seus métodos de núcleo (isto é, escrita (byte [])), de modo que aparece um pressuposto ténue sem dados do disco ou pelo menos evidência anedótica.

Eu tenho várias perguntas sobre isso:

  1. É esta afirmação é realmente verdade? Will arquivos de memória mapeada sempre fornecer mais rápido IO comparação com Java de FileOutputStream?

  2. Será que a classe MappedByteBuffer acedido a partir de um FileChannel fornecer a mesma funcionalidade que um nativa memória biblioteca de arquivos mapeados acessada via JNI? O que é MappedByteBuffer falta que pode levar você a usar um solução JNI?

  3. Quais são os riscos de usar arquivos de memória mapeada para IO de disco em uma produção inscrição? Ou seja, aplicações que tem uptime contínuo com reboots mínimas (uma vez por mês, max). anedotas da vida real de produção aplicações Java (ou não) preferido.

Pergunta # 3 é importante - eu poderia responder a esta pergunta me parcialmente escrevendo um aplicativo de "brinquedo" que perf testa IO usando as várias opções descritas acima, mas por postagem para isso estou esperando para anedotas do mundo real / dados para mastigar.

[EDIT] Esclarecimento - cada dia de operação, o aplicativo cria vários arquivos que variam em tamanho de 100 MB para 1 giga. No total, a aplicação pode ser escrita para fora vários GB de dados por dia.

Foi útil?

Solução

Você pode ser capaz de acelerar as coisas um pouco, examinando como seus dados estão sendo tamponado durante as gravações. Esta tende a ser específica aplicação como você precisaria de uma idéia dos padrões de escrita de dados esperados. Se a consistência dos dados é importante, haverá compensações aqui.

Se você está apenas escrevendo novos dados no disco do seu aplicativo, memória mapeada I / O, provavelmente não vai ajudar muito. Não vejo qualquer razão que você gostaria de investir tempo em alguma solução nativa costume codificada. Parece apenas que muita complexidade para a sua aplicação, a partir do que você forneceu até agora.

Se você tem certeza que você realmente precisa melhor desempenho I / O - ou apenas O desempenho no seu caso, gostaria de olhar para uma solução de hardware, como uma matriz de disco atento. Jogar mais hardware para o problema é muitas vezes mais rentável do ponto de vista comercial do que gastar software de otimização do tempo. Também é geralmente mais rápido para implementar e mais confiável.

Em geral, há um monte de armadilhas na otimização ao longo de software. Você vai introduzir novos tipos de problemas para a sua aplicação. Você pode executar em problemas de memória / GC surra que levaria a mais manutenção / tuning. A pior parte é que muitas destas questões vai ser difícil de teste antes de entrar em produção.

Se fosse meu aplicativo, eu provavelmente iria ficar com o FileOutputStream com algum buffer possivelmente atento. Depois que eu usaria o tempo de solução de jogar mais hardware para ele honrado.

Outras dicas

Memória mapeada E / S não vai fazer seus discos correr mais rápido (!). Para o acesso linear parece um inútil bit.

A NIO mapeados tampão é a coisa real (advertência habitual sobre qualquer implementação razoável).

Tal como com outros NIO dirigem buffers alocados, os buffers não são normais memória e não vai conseguir GCed da forma mais eficiente. Se você criar muitos deles você pode achar que você ficar sem espaço de memória / endereço, sem esgotar o heap Java. Esta é obviamente uma preocupação com os processos de longa execução.

Da minha experiência, arquivos de memória mapeada desempenho muito melhor do que o acesso arquivo simples, tanto em tempo real e persistência casos de uso. Eu tenho trabalhado principalmente com C ++ no Windows, mas performances Linux são semelhantes, e você estiver planejando usar JNI de qualquer maneira, então eu acho que se aplica ao seu problema.

Para um exemplo de um mecanismo de persistência construído sobre arquivo de memória mapeada, consulte Metakit . Eu usei-o em um aplicativo onde os objetos foram vistas simples sobre os dados mapeados na memória, o motor teve o cuidado de todas as coisas mapeamento por trás das cortinas. Este foi rápido e eficiente de memória (pelo menos em comparação com as abordagens tradicionais, como os da versão anterior usado), e temos commit / transações de reversão para livre.

Em outro projeto que eu tinha para escrever aplicações de rede multicast. Os dados foram enviar em ordem aleatória para minimizar o impacto de perda de pacotes consecutivos (combinado com FEC e esquemas de bloqueio). Além disso os dados poderiam muito bem exceder o espaço de endereço (arquivos de vídeo foram maiores que 2 GB) para alocação de memória estava fora de questão. No lado do servidor, seções de arquivos estavam na demanda de memória mapeada e a camada de rede escolhido diretamente os dados desses pontos de vista; Como consequência, o uso de memória foi muito baixa. No lado do receptor, não havia maneira de prever a ordem em que os pacotes foram recebidos, de modo que tem que manter um número limitado de pontos de vista activas no ficheiro de destino, e os dados foram copiado directamente para estes pontos de vista. Quando um pacote teve que ser colocado em uma área não mapeada, a visão mais antiga era não mapeado (e, eventualmente, liberado para o arquivo pelo sistema) e substituído por um novo ponto de vista sobre a área de destino. Performances estavam pendentes, nomeadamente porque o sistema fez um grande trabalho em cometer dados como uma tarefa em segundo plano, e restrições de tempo real foram facilmente atendidas.

Desde então, eu estou convencido de que mesmo o melhor esquema de software bem-trabalhada não pode bater o sistema de política O com arquivo de memória mapeada padrão I /, porque o sistema sabe mais do que aplicações do espaço do usuário sobre quando e como os dados devem ser escrito. Além disso, o que é importante saber é que o mapeamento de memória é uma obrigação quando se lida com grandes volumes de dados, porque os dados nunca é alocado (daí consumindo memória), mas mapeados de forma dinâmica no espaço de endereço, e gerido pelo gestor de memória virtual do sistema, que é sempre mais rápido do que o heap. Assim, o sistema sempre usar a memória de forma otimizada, e os dados compromete sempre que precisar, por trás das costas do aplicativo, sem impactar-lo.

Hope isso ajuda.

Quanto ao ponto 3 - se a máquina deixa de funcionar e há quaisquer páginas que não foram liberadas para o disco, então eles estão perdidos. Outra coisa é o desperdício do espaço de endereços - o mapeamento de um arquivo para a memória consome espaço de endereço (e requer área contígua), e bem, em máquinas de 32 bits é um pouco limitado. Mas você disse que cerca de 100MB - por isso não deve ser um problema. E mais uma coisa -. Expandir o tamanho do arquivo mmaped requer algum trabalho

A propósito, este SO discussão também pode dar-lhe alguns insights.

Se você escrever menos bytes, será mais rápido. E se você filtrada através GZIPOutputStream, ou o que se escreveu seus dados em zipfiles ou JarFiles?

Como mencionado acima, use NIO (A.K.A. nova IO). Há também um novo, IO saindo.

O uso adequado de uma solução RAID disco rígido iria ajudá-lo, mas isso seria uma dor.

Eu realmente gosto da ideia de comprimir os dados. Ir para o gajo GZIPOutputStream! Isso iria duplicar o seu rendimento se a CPU pode manter-se. É provável que você pode tirar vantagem das máquinas double-core agora-padrão, hein?

-Stosh

scroll top