Pergunta

Eu estou tentando descobrir se existe alguma diferença de desempenho (ou vantagens) quando usamos FileChannel nio contra FileInputStream/FileOuputStream normal a ler e escrever arquivos de sistema de arquivos. Observei que na minha máquina ambos executar no mesmo nível, também muitas vezes a maneira FileChannel é mais lento. Por favor, posso saber mais detalhes comparando estes dois métodos. Aqui está o código que usei, o arquivo que estou testando com é de cerca de 350MB. É uma boa opção de usar classes com base NIO para File I / O, se eu não estou olhando para 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");
    }
}
Foi útil?

Solução

A minha experiência com tamanhos de arquivos maiores tem sido que java.nio é mais rápido que java.io. Solidamente mais rápido. Como na faixa> 250%. Dito isto, eu estou eliminando gargalos óbvias, o que eu sugiro que o seu micro-referência pode sofrer de. As áreas potenciais para a investigação:

O tamanho do buffer. O algoritmo basicamente você tem é

  • copiar do disco para o buffer
  • copiar do buffer para o disco

A minha própria experiência tem sido que este tamanho do buffer é madura para o ajuste. I tenha resolvido em 4KB para uma parte da minha candidatura, 256KB para outro. Eu suspeito que seu código está sofrendo com um tampão tão grande. Executar alguns benchmarks com buffers de 1 KB, 2KB, 4KB, 8KB, 16KB, 32KB e 64KB para provar isso a si mesmo.

Não execute java benchmarks que ler e escrever para o mesmo disco.

Se você fizer isso, então você está realmente avaliação comparativa do disco, e não Java. Também gostaria de sugerir que, se o seu CPU não está ocupado, então provavelmente você está experimentando algum outro gargalo.

Não use um buffer se você não precisa.

Por que copiar a memória se seu alvo é outro disco ou uma placa de rede? Com arquivos maiores, a latência incured não é trivial.

Tal como outros já disseram, o uso FileChannel.transferTo() ou FileChannel.transferFrom(). A principal vantagem aqui é que a JVM usa o acesso do sistema operacional para DMA ( Memória de Acesso Direto ), se presente. (Esta é dependente de implementação, mas modernas versões Sun e IBM em CPUs de propósito geral é bom para ir.) O que acontece é que os dados vai direto de / para o disco, para o ônibus, e depois para o destino ... ignorando qualquer circuito através de RAM ou CPU.

O aplicativo web eu passei meus dias e noites trabalhando é muito IO pesado. Eu fiz micro benchmarks e pontos de referência do mundo real também. E os resultados são no meu blog, ter um olhar-ver:

Usar dados de produção e ambientes

Micro-benchmarks são propensas a distorção. Se você puder, fazer um esforço para coletar dados a partir exatamente o que você pretende fazer, com a carga que você espera, no hardware que você espera.

As minhas referências são sólidos e confiáveis ??porque ocorreu em um sistema de produção, um sistema robusto, um sistema sob carga, reunidos em logs. não 7200 RPM 2.5" drive SATA de meu notebook enquanto eu observava intensamente como o trabalho JVM meu disco rígido.

O que você está executando em? É importante.

Outras dicas

Se a coisa que você quer comparar é o desempenho de cópia de arquivos, em seguida, para o teste de canal que você deve fazer isso em vez disso:

final FileInputStream inputStream = new FileInputStream(src);
final FileOutputStream outputStream = new FileOutputStream(dest);
final FileChannel inChannel = inputStream.getChannel();
final FileChannel outChannel = outputStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
inputStream.close();
outputStream.close();

Esta não será mais lenta do que amortecer-se de um canal para o outro, e potencialmente vai ser maciçamente mais rápido. De acordo com o Javadocs:

Muitos sistemas operacionais pode transferir bytes diretamente do cache do sistema de arquivos para o canal alvo sem realmente copiar-los.

Com base em meus testes (64bit Win7, 6GB RAM, Java6), NIO transferFrom é rápido apenas com pequenos arquivos e torna-se muito lento em arquivos maiores. NIO databuffer aleta sempre supera padrão IO.

  • Copiar 1000x2MB

    1. NIO (transferFrom) ~ 2300ms
    2. NIO (dirigir datababuffer aleta 5000B) ~ 3500ms
    3. IO padrão (tampão 5000B) ~ 6000ms
  • Copiar 100x20mb

    1. NIO (dirigir datababuffer aleta 5000B) ~ 4000ms
    2. NIO (transferFrom) ~ 5000ms
    3. IO padrão (tampão 5000B) ~ 6500ms
  • Copiar 1x1000mb

    1. NIO (direta aleta datababuffer 5000B) ~ 4500S
    2. IO padrão (tampão 5000B) ~ 7000ms
    3. NIO (transferFrom) ~ 8000ms

O transferTo () método funciona em pedaços de um arquivo; não foi concebido como um método de cópia de arquivos de alto nível: Como copiar um arquivo grande no Windows XP?

Respondendo a parte "utilidade" da questão:

Um apanhado em vez subtil de utilizando FileChannel sobre FileOutputStream é que a realização de qualquer das suas operações de bloqueio (por exemplo read() ou write()) a partir de um fio que está em interrompida estado fará com que o canal para perto abruptamente com java.nio.channels.ClosedByInterruptException .

Agora, isso poderia ser uma coisa boa se qualquer que seja a FileChannel foi usado para é parte do principal função do segmento, e design levou isso em conta.

Mas também poderia ser irritante se usado por alguma característica auxiliar, como uma função de registro. Por exemplo, você pode encontrar a sua saída de registro de repente fechado se a função de registro passa a ser chamado por um segmento que também está interrompido.

É lamentável isso é tão sutil, porque não respondendo por isso pode levar a erros que afetam a integridade de escrita. [1] [2]

Eu testei o desempenho de FileInputStream vs. FileChannel para decodificar base64 arquivos codificados. Em meus experients Eu testei bastante grande arquivo e io tradicional foi sempre um pouco mais rápido do que nio.

FileChannel poderia ter tido uma vantagem em versões anteriores do JVM por causa de synchonization sobrecarga em várias classes io relacionados, mas JVM moderna são muito bons na remoção de bloqueios desnecessários.

Se você não estiver usando os recursos recurso transferTo ou sem bloqueio você não vai notar a diferença entre o tradicional IO e NIO (2) porque o tradicional IO mapeia para NIO.

Mas, se você pode usar o NIO características como transferFrom / To ou quiser usar tampões, então é claro NIO é o caminho a percorrer.

A minha experiência é que NIO é muito mais rápido com arquivos pequenos. Mas quando se trata de grandes arquivos FileInputStream / FileOutputStream é muito mais rápido.

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