Estou tentando descobrir se há alguma diferença no desempenho (ou vantagens) quando usamos o nio FileChannel
versus o normal FileInputStream/FileOuputStream
para ler e gravar arquivos no sistema de arquivos. Observei que na minha máquina ambos funcionam no mesmo nível, também muitas vezes o FileChannel
caminho é mais lento. Posso saber mais detalhes comparando esses dois métodos. Aqui está o código que eu usei, o arquivo com o qual estou testando está disponível 350MB
. É uma boa opção usar classes baseadas em NIO para E / S de arquivo, se não estiver procurando acesso aleatório ou outros recursos avançados?
package trialjavaprograms;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class JavaNIOTest {
public static void main(String[] args) throws Exception {
useNormalIO();
useFileChannel();
}
private static void useNormalIO() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
InputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
byte[] buf = new byte[64 * 1024];
int len = 0;
while((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
fos.close();
is.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
private static void useFileChannel() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
FileInputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
FileChannel f = is.getChannel();
FileChannel f2 = fos.getChannel();
ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
long len = 0;
while((len = f.read(buf)) != -1) {
buf.flip();
f2.write(buf);
buf.clear();
}
f2.close();
f.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
}
java
optimization
file
nio
operations
Keshav
fonte
fonte
transferTo
/transferFrom
seria mais convencional para copiar arquivos. Qualquer que seja a técnica, não deve tornar seu disco rígido mais rápido ou mais devagar, embora eu ache que possa haver um problema se ele ler pequenos pedaços de cada vez e fizer com que a cabeça gaste uma quantidade excessiva de tempo procurando.Respostas:
Minha experiência com tamanhos maiores de arquivos foi
java.nio
mais rápida quejava.io
. Solidamente mais rápido. Como na faixa> 250%. Dito isso, estou eliminando gargalos óbvios, os quais sugiro que seu micro-benchmark possa sofrer. Áreas potenciais para investigação:O tamanho do buffer. O algoritmo que você basicamente tem é
Minha própria experiência foi que esse tamanho de buffer está pronto para o ajuste. Decidi em 4KB para uma parte do meu aplicativo, 256 KB para outra. Eu suspeito que seu código está sofrendo com um buffer tão grande. Execute alguns benchmarks com buffers de 1 KB, 2 KB, 4KB, 8 KB, 16 KB, 32 KB e 64 KB para provar isso a si mesmo.
Não execute benchmarks java que leiam e gravem no mesmo disco.
Se você o fizer, estará realmente comparando o disco, e não o Java. Eu também sugeriria que, se sua CPU não estiver ocupada, provavelmente você está enfrentando algum outro gargalo.
Não use um buffer se não precisar.
Por que copiar para a memória se o seu destino for outro disco ou uma NIC? Com arquivos maiores, a latência incorrida não é trivial.
Como outros já disseram, use
FileChannel.transferTo()
ouFileChannel.transferFrom()
. A principal vantagem aqui é que a JVM usa o acesso do SO ao DMA ( Direct Memory Access ), se presente. (Isso depende da implementação, mas as versões modernas da Sun e da IBM em CPUs de uso geral estão prontas.) O que acontece é que os dados vão direto para / do disco, para o barramento e depois para o destino ... ignorando qualquer circuito através RAM ou a CPU.O aplicativo da web em que passei meus dias e noites trabalhando é muito pesado para o IO. Também fiz micro benchmarks e benchmarks reais. E os resultados estão no meu blog, dê uma olhada:
Use dados e ambientes de produção
Micro-benchmarks são propensos a distorção. Se puder, faça um esforço para coletar dados exatamente do que você planeja fazer, com a carga que você espera, no hardware que você espera.
Meus benchmarks são sólidos e confiáveis porque ocorreram em um sistema de produção, um sistema robusto, um sistema sob carga, reunidos em toras. Não é a unidade SATA de 7200 RPM 2,5 "do meu notebook enquanto eu assistia intensamente enquanto a JVM trabalha no meu disco rígido.
No que você está correndo? Importa.
fonte
Se você deseja comparar o desempenho da cópia de arquivos, faça o teste do canal:
Isso não será mais lento que o buffer de um canal para o outro e será potencialmente mais rápido. De acordo com os Javadocs:
fonte
Com base nos meus testes (Win7 de 64 bits, 6 GB de RAM, Java6), o NIO transferFrom é rápido apenas com arquivos pequenos e fica muito lento em arquivos maiores. O NIO databuffer flip sempre supera a IO padrão.
Copiando 1000x2MB
Copiando 100x20mb
Copiando 1x1000mb
O método transferTo () funciona em pedaços de um arquivo; não era um método de cópia de arquivo de alto nível: Como copiar um arquivo grande no Windows XP?
fonte
Respondendo à parte "utilidade" da pergunta:
Um problema bastante sutil do uso
FileChannel
excessivoFileOutputStream
é que a execução de qualquer uma de suas operações de bloqueio (por exemplo,read()
ouwrite()
) de um encadeamento em estado interrompido fará com que o canal feche abruptamentejava.nio.channels.ClosedByInterruptException
.Agora, isso pode ser bom se o que for
FileChannel
usado for parte da função principal do encadeamento, e o design levar isso em consideração.Mas também pode ser irritante se usado por algum recurso auxiliar, como uma função de registro. Por exemplo, você pode encontrar sua saída de log repentinamente fechada se a função de log for chamada por um encadeamento que também é interrompido.
É lamentável que isso seja tão sutil, porque não levar isso em consideração pode levar a erros que afetam a integridade da gravação. [1] [2]
fonte
Testei o desempenho do FileInputStream vs. FileChannel para decodificar arquivos codificados em base64. Nos meus experimentos, testei arquivos grandes e o io tradicional sempre foi um pouco mais rápido que o nio.
O FileChannel pode ter tido uma vantagem em versões anteriores da jvm devido à sobrecarga de sincronização em várias classes relacionadas ao io, mas a jvm moderna é muito boa em remover bloqueios desnecessários.
fonte
Se você não estiver usando o recurso transferTo ou os recursos que não bloqueiam, não notará diferença entre as E / S tradicionais e o NIO (2) porque o E / S tradicional mapeia para o NIO.
Mas se você pode usar os recursos da NIO como transferFrom / To ou deseja usar os Buffers, é claro que a NIO é o caminho a percorrer.
fonte
Minha experiência é que o NIO é muito mais rápido com arquivos pequenos. Mas quando se trata de arquivos grandes, o FileInputStream / FileOutputStream é muito mais rápido.
fonte
java.nio
é mais rápido com arquivos maiores do quejava.io
não menores.java.nio
é rápido, desde que o arquivo seja pequeno o suficiente para ser mapeado para a memória. Se aumentar (200 MB e mais),java.io
é mais rápido.FileChannel.read()
. Não existe apenas uma abordagem única para ler arquivos usandojava.nio
.