[C#] C #에서 큰 파일을 바이트 배열로 읽는 가장 좋은 방법은 무엇입니까?

큰 바이너리 파일 (수 메가 바이트)을 바이트 배열로 읽는 웹 서버가 있습니다. 서버가 동시에 여러 파일을 읽을 수 있으므로 (다른 페이지 요청) CPU에 너무 많은 부담을주지 않고이를 수행하는 가장 최적화 된 방법을 찾고 있습니다. 아래 코드가 충분합니까?

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;
}



답변

전체 내용을 간단히 다음과 같이 바꾸십시오.

return File.ReadAllBytes(fileName);

그러나 메모리 소비가 염려 되면 전체 파일을 메모리로 한 번에 읽지 마십시오 . 청크 단위로 그렇게해야합니다.


답변

나는 여기서 대답은 일반적 으로 “하지 말라”고 주장 할 수 있습니다 . 한 번에 모든 데이터 를 절대적으로 필요로 하지 않는 한, Stream기반 API (또는 일부 리더 / 반복기 변형) 사용을 고려하십시오 . 즉 , 특히 시스템 부하를 최소화하고 처리량을 최대화하기 위해 (질문에 의해 제안) 여러 병렬 작업을 할 때 중요합니다.

예를 들어, 발신자에게 데이터를 스트리밍하는 경우 :

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);
    }
}


답변

나는 이것을 생각할 것이다 :

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


답변

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;
} 

Integer.MaxValue-Read 메소드에 의해 배치 된 파일 크기 제한에 유의하십시오. 즉, 한 번에 2GB 청크 만 읽을 수 있습니다.

또한 FileStream에 대한 마지막 인수는 버퍼 크기입니다.

FileStreamBufferedStream 에 대해 읽으십시오 .

항상 가장 빠른 프로파일 링을위한 간단한 샘플 프로그램이 가장 유리합니다.

또한 기본 하드웨어는 성능에 큰 영향을 미칩니다. 대용량 캐시가있는 서버 기반 하드 디스크 드라이브와 온보드 메모리 캐시가있는 RAID 카드를 사용하고 있습니까? 아니면 IDE 포트에 연결된 표준 드라이브를 사용하고 있습니까?


답변

작업 빈도, 파일 크기 및보고있는 파일 수에 따라 고려해야 할 다른 성능 문제가 있습니다. 기억해야 할 것은 가비지 수집기의 도움으로 각 바이트 배열이 해제된다는 것입니다. 해당 데이터를 캐싱하지 않으면 많은 가비지가 발생하여 GC의 % 시간으로 대부분의 성능이 손실 될 수 있습니다. 청크가 85K보다 큰 경우 LOH (Large Object Heap)에 할당하게되며 모든 세대의 컬렉션을 해제해야합니다 (매우 비싸며 서버에서 진행되는 동안 모든 실행이 중지됨) ). 또한 LOH에 많은 개체가있는 경우 LOH 조각화 (LOH가 압축되지 않음)로 인해 성능이 저하되고 메모리 부족 예외가 발생할 수 있습니다. 특정 지점에 도달하면 프로세스를 재활용 할 수 있지만 이것이 최선의 방법인지 모르겠습니다.

요점은 모든 바이트를 메모리에 가장 빨리 읽기 전에 앱의 전체 수명주기를 고려해야한다는 것입니다. 그렇지 않으면 전반적인 성능을 위해 단기 성능을 거래 할 수 있습니다.


답변

내가 말하고 싶지만 BinaryReader괜찮지 만, 대신 버퍼의 길이를 취득하기위한 코드의 그 라인을 모든이에 리팩토링 할 수있다 :

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;
}

주석 처리기 중 하나가 600MB보다 큰 파일에 문제가 .ReadAllBytes()있음을 포함 .ReadAllBytes()하는 최상위 응답에 대한 주석에서을 사용 했기 때문에를 사용하는 것보다 낫습니다 BinaryReader. 또한,에 넣어 using문은 보장 FileStreamBinaryReader폐쇄와 배치된다.


답변

‘큰 파일’이 4GB 제한을 초과하는 경우 다음의 서면 코드 논리가 적합합니다. 주목해야 할 주요 문제는 SEEK 방법과 함께 사용되는 LONG 데이터 유형입니다. LONG은 2 ^ 32 데이터 경계를 넘어 가리킬 수 있습니다. 이 예제에서 코드는 먼저 1GB 청크로 큰 파일을 처리하고 있으며, 전체 1GB 청크가 처리 된 후 남은 (<1GB) 바이트가 처리됩니다. 이 코드를 사용하여 4GB 크기를 초과하는 파일의 CRC를 계산합니다. ( 이 예에서 crc32c 계산에 https://crc32c.machinezoo.com/ 사용 )

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;
}