Pergunta

Eu tenho um servidor web que irá ler arquivos binários grandes (vários megabytes) em matrizes de bytes. O servidor pode estar lendo vários arquivos ao mesmo tempo (diferentes solicitações de página), então eu estou procurando a maneira mais otimizada para fazer isso sem sobrecarregar a CPU muito. É o código abaixo bom o suficiente?

public byte[] FileToByteArray(string fileName)
{
    byte[] buff = null;
    FileStream fs = new FileStream(fileName, 
                                   FileMode.Open, 
                                   FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    long numBytes = new FileInfo(fileName).Length;
    buff = br.ReadBytes((int) numBytes);
    return buff;
}
Foi útil?

Solução

Basta substituir a coisa toda com:

return File.ReadAllBytes(fileName);

No entanto, se você está preocupado com o consumo de memória, você deve não ler o arquivo inteiro na memória de uma só vez a todos. Você deve fazer isso em pedaços.

Outras dicas

Eu poderia argumentar que a resposta aqui geralmente é "não". A menos que você absolutamente necessário todos os dados ao mesmo tempo, considerar o uso de uma API baseada em Stream (ou alguma variante do leitor / iterator). Isso é especialmente importante quando você tem várias operações paralelas (como sugerido pela pergunta) para minimizar a carga do sistema e maximizar a produção.

Por exemplo, se estiver transmitindo dados para um chamador:

Stream dest = ...
using(Stream source = File.OpenRead(path)) {
    byte[] buffer = new byte[2048];
    int bytesRead;
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
        dest.Write(buffer, 0, bytesRead);
    }
}

Eu acho que isso:

byte[] file = System.IO.File.ReadAllBytes(fileName);

Seu código pode ser tomada a esse (em vez de File.ReadAllBytes):

public byte[] ReadAllBytes(string fileName)
{
    byte[] buffer = null;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, (int)fs.Length);
    }
    return buffer;
} 

Observe o Integer.MaxValue - limitação de tamanho de arquivo colocado pelo método Read. Em outras palavras, você pode apenas ler um pedaço de 2GB ao mesmo tempo.

Observe também que o último argumento para o FileStream é um tamanho do buffer.

Também gostaria de sugerir a leitura sobre FileStream BufferedStream .

Como sempre um programa de exemplo simples de perfil que é mais rápido será mais benéfico.

Também o seu hardware subjacente terá um grande efeito sobre o desempenho. Você está usando discos rígidos baseados em servidor com grandes caches e um cartão de RAID com cache de memória onboard? Ou você está usando uma unidade padrão conectado à porta IDE?

Dependendo da frequência das operações, o tamanho dos arquivos, e o número de arquivos que você está olhando, existem outros problemas de desempenho a ter em consideração. Uma coisa a lembrar é que cada uma de suas matrizes de bytes será lançado à mercê do coletor de lixo. Se você não está cache de qualquer desses dados, você pode acabar criando um monte de lixo e estar perdendo a maior parte de seu desempenho para % Time in GC . Se os pedaços são maiores do que 85K, você estará alocando à Large Object Heap (LOH) que vai exigir uma coleção de todas as gerações para liberar (isto é muito caro, e no servidor vai parar toda a execução enquanto ele está acontecendo ). Além disso, se você tem uma tonelada de objetos no LOH, você pode acabar com LOH fragmentação (LOH nunca é compactado) que leva a um desempenho ruim e fora de memória exceções. Você pode reciclar o processo uma vez que você atingiu um certo ponto, mas eu não sei se isso é uma boa prática.

O ponto é, você deve considerar o ciclo de vida do seu aplicativo antes necessariamente apenas ler todos os bytes na memória a maneira mais rápida possível, ou você pode estar negociando desempenho de curto prazo para o desempenho global.

Eu diria BinaryReader é bom, mas pode ser reformulado para isso, em vez de todas aquelas linhas de código para obter o comprimento do buffer:

public byte[] FileToByteArray(string fileName)
{
    byte[] fileData = null;

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
        using (BinaryReader binaryReader = new BinaryReader(fs))
        {
            fileData = binaryReader.ReadBytes((int)fs.Length); 
        }
    }
    return fileData;
}

Deve ser melhor do que usar .ReadAllBytes(), desde que eu vi nos comentários sobre a resposta de topo que inclui .ReadAllBytes() que um dos comentadores teve problemas com arquivos> 600 MB, uma vez que um BinaryReader é destinado a esse tipo de coisa. Além disso, colocá-lo em um comunicado using garante a FileStream e BinaryReader estão fechados e eliminados.

Em caso de 'um grande arquivo' é significado para além do limite de 4 GB, então a minha seguinte lógica código escrito é apropriado. A questão-chave a notar é o tipo de dados LONG usado com o método Seek. Como tempo é capaz de apontar para além de 2 ^ 32 limites de dados. Neste exemplo, o código está processando primeiro processamento do arquivo grande em pedaços de 1GB, depois de toda a grandes pedaços de 1GB são processados, os que sobraram (<1GB) bytes são processados. Eu uso este código com o cálculo do CRC dos arquivos além do tamanho de 4 GB. (Usando https://crc32c.machinezoo.com/ para o cálculo crc32c neste exemplo)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}

Use a classe BufferedStream em C # para melhorar o desempenho. Um tampão é um bloco de bytes de memória usada para armazenar dados, reduzindo assim o número de chamadas para o sistema operativo. Buffers melhoram a ler e desempenho de gravação.

Consulte o seguinte para um exemplo de código e uma explicação adicional: http://msdn.microsoft.com/en-us/ biblioteca / system.io.bufferedstream.aspx

usar este:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;

Eu recomendaria tentar o método Response.TransferFile() em seguida, um Response.Flush() e Response.End() para servir seus arquivos grandes.

Se você está lidando com arquivos acima de 2 GB, você verá que os métodos acima falhar.

É muito mais fácil apenas para entregar o fluxo de fora para MD5 e permitir que a fatia seu arquivo para você:

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top