Pergunta

Eu tenho um método que cria um MessageDigest (um hash) a partir de um arquivo, e eu preciso fazer isso com um monte de arquivos (> = 100.000). Como grande eu deveria fazer o buffer usado para ler a partir dos arquivos para maximizar o desempenho?

A maioria das pessoas está familiarizada com o código básico (que eu vou repetir aqui apenas no caso):

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

Qual é o tamanho ideal do buffer para maximizar a produtividade? Eu sei que isto é dependente do sistema, e eu tenho certeza que seu sistema operacional, sistema de arquivos, e HDD dependente, e talvez haja outro hardware / software na mistura.

(Gostaria de salientar que eu sou um pouco novo para Java, de modo que este pode ser apenas alguns chamam de API Java Eu não sei sobre.)

Editar: eu não sei de antemão os tipos de sistemas Isto será usado, então eu não pode assumir um lote inteiro. (Estou usando Java por esse motivo.)

Editar: O código acima está faltando coisas como try..catch para fazer a pós menor

Foi útil?

Solução

tamanho do buffer Optimum está relacionada a uma série de coisas:. Tamanho do bloco de sistema de arquivo, tamanho do cache da CPU e latência de cache

A maioria dos sistemas de arquivos são configurados para tamanhos de bloco uso de 4096 ou 8192. Em teoria, se você configurar o tamanho do buffer para que você está lendo alguns bytes mais do que o bloco de disco, as operações com o sistema de arquivos pode ser extremamente ineficiente ( ou seja, se você configurou o tampão para ler 4100 bytes de cada vez, cada leitura exigiria 2 bloco lê pelo sistema de arquivos). Se os blocos já estão em cache, então você acaba pagando o preço da RAM -> L3 / L2 cache de latência. Se você é azarado e os blocos não são no cache, no entanto, o que você paga o preço do disco-> RAM latência também.

É por isso que você vê a maioria dos buffers de tamanho como uma potência de 2, e geralmente maior do que (ou igual a) o tamanho do bloco de disco. Isto significa que um de seu fluxo lê pode resultar em múltiplos bloco de disco lê - mas aqueles lê sempre usará um bloco completo -. Não desperdiçados lê

Agora, isso é compensado um pouco em um cenário de streaming típico porque o bloco que é lido do disco vai ser ainda na memória quando você bate a próxima leitura (estamos fazendo leituras seqüenciais aqui, depois de tudo) - assim você acaba pagando o RAM -> L3 / L2 cache de latência preço na próxima leitura, mas não o disk> RAM latência. Em termos de ordem de grandeza, disk> latência de memória RAM é tão lento que praticamente pântanos qualquer outro latência que você pode estar lidando.

Então, eu suspeito que se você executou um teste com diferentes tamanhos de cache (não ter feito isso mesmo), provavelmente você vai encontrar um grande impacto do tamanho do cache até o tamanho do bloco de sistema de arquivos. Acima disso, eu suspeito que as coisas iriam nivelar muito rapidamente.

Há um tonelada de condições e exceções aqui - as complexidades do sistema são realmente bastante surpreendente (apenas recebendo uma alça sobre L3 -> transferências cache L2 é mente bogglingly complexa, e isso muda com cada tipo de CPU).

Isso leva à resposta 'mundo real': Se seu aplicativo é como 99% lá fora, defina o tamanho do cache para 8192 e seguir em frente (ainda melhor, escolha encapsulamento sobre o desempenho eo uso BufferedInputStream para esconder os detalhes). Se você está no 1% de aplicativos que são altamente dependentes do débito do disco, elaborar a sua implementação para que você possa trocar diferentes estratégias de interação disco, e fornecer os botões e mostradores para permitir que seus usuários para testar e otimizar (ou chegar a algum auto sistema de otimização).

Outras dicas

Sim, é provavelmente dependente de várias coisas - mas eu duvido que ele vai fazer muita diferença. I tendem a optar por 16K ou 32K como um bom equilíbrio entre o uso de memória e desempenho.

Note que você deve ter um try / finally bloco no código para garantir que o fluxo está fechado, mesmo se uma exceção é lançada.

Na maioria dos casos, isso realmente não importa muito. Basta escolher um bom tamanho, como 4K ou 16K e ficar com ela. Se você é positivo que este é o gargalo na sua aplicação, então você deve começar a perfilar para encontrar o tamanho do buffer ideal. Se você escolher um tamanho que é muito pequeno, você vai perder tempo a fazer operações extras de I / O e chamadas de função extra. Se você escolher um tamanho que é muito grande, você começará a ver um monte de erros de cache que realmente vai te atrapalhar. Não use um buffer maior do que o tamanho do cache L2.

No caso ideal, devemos ter memória suficiente para ler o arquivo em uma operação de leitura. Isso seria o melhor intérprete, porque deixamos o sistema gerenciar arquivos do sistema, unidades de alocação e HDD à vontade. Na prática, você têm a sorte de conhecer os tamanhos de arquivo de antecedência, basta usar o tamanho médio de arquivo arredondado para 4K (unidade de alocação padrão em NTFS). E o melhor de tudo: criar um ponto de referência para testar várias opções.

Você pode usar os BufferedStreams / leitores e, em seguida, usar os seus tamanhos de buffer.

Eu acredito que os BufferedXStreams estiver usando 8192 como o tamanho do buffer, mas como Ovidiu disse, você provavelmente deve executar um teste em um grupo inteiro de opções. Sua realmente vai depender das configurações de sistemas de arquivos e de disco, como o que os melhores tamanhos são.

Lendo arquivos usando FileChannel e MappedByteBuffer resultado será mais provável de Java NIO em uma solução que será muito mais rápido do que qualquer solução que envolva FileInputStream. Basicamente, memória de mapear arquivos grandes, e usar tampões diretos para os pequenos.

fonte de In BufferedInputStream você vai encontrar: private DEFAULT_BUFFER_SIZE static int = 8192;
Por isso é ok para você usar esse valor padrão.
Mas se você pode descobrir mais alguma informação que você vai obter respostas mais valueable.
Por exemplo, seu adsl talvez preffer um buffer de 1454 bytes, isso porque carga de TCP / IP. Para discos, você pode usar um valor que corresponder ao tamanho do bloco do seu disco.

Como já mencionado em outras respostas, use BufferedInputStreams.

Depois disso, eu acho que o tamanho do buffer realmente não importa. Ou o programa é I / O ligado, e crescente tamanho do buffer ao longo BIS padrão, não vai fazer qualquer grande impacto no desempenho.

Ou o programa está vinculado à CPU dentro do MessageDigest.update (), e maior parte do tempo não é gasto no código do aplicativo, de modo ajustes que não vai ajudar.

(Hmm ... com múltiplos núcleos, fios pode ajudar.)

1024 é apropriado para uma ampla variedade de circunstâncias, embora na prática você pode ver o desempenho melhor com um tamanho de buffer maior ou menor.

Isso dependerá de uma série de fatores, incluindo o bloco de sistema de arquivos tamanho e CPU hardware.

Também é comum para escolher uma potência de 2 para o tamanho do buffer, já que a maioria subjacente hardware está estruturada com bloco e cache de tamanhos fle que são uma potência de 2. O Buffered aulas permitem que você especifique o tamanho do buffer no construtor. Se nenhum for fornecido, eles usar um valor padrão, que é uma potência de 2 na maioria dos JVMs.

Independentemente de qual tamanho do buffer que você escolher, o maior aumento de desempenho que você vai Veja está se movendo de nonbuffered para acesso a arquivos tamponada. Ajustando o tamanho da memória intermédia pode melhorar o desempenho um pouco, mas a menos que você estiver usando uma extremamente pequeno ou extremamente grande tamanho do buffer, é pouco provável que tenha um impacto signifcativo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top