잊지 않겠습니다.

어제 만들던  log 파일 분석기에서 Text를 처리하는 것이 문제가 아닌, 대용량 파일 처리에 대해서 생각하지 못했던 문제가 발생되었다. 

가장 큰 문제는 File를 한번에 얻어서 byte를 얻어낼 경우에 StringBuilder의 최대 사이즈를 넘어가기 때문에 사용할 수 없다는 점. 그리고 FileStream.Read 에서 읽어낼 수 있는 양 자체도 시스템에 따른 특정 사이즈에 결정이 되어있다는 것이 대용량 처리에서 가장 큰 문제가 되었다. 

이 경우에 전에 C++에서는 가상 메모리 또는 File Buffer를 사용해서 일정 크기로 잘라서 읽었던 경험이 있는데. .NET에서는 어떤 방법이 좋을지 다양한 방법으로 실험을 해봤다.;

* 제약 사항
: byte 단위로 읽어야 된다. 
: IIS에서 사용되고 있는 log file이기 때문에 공유 형태로 읽어야지 된다. 
: 크기에 제약이 없이 처리가 가능해야지 된다. 

생각했던 방법들은 다음과 같다. 

1. StringBuilder를 이용한 byte 값 저장. 
: 가장 처음에 생각한 방법이다. 이 방법의 경우에 파일의 크기가 커질 경우에 StringBuilder에서 OutOfMemory Exception을 발생한다. 큰 파일을 열어야지 되기 때문에 바로 퇴장. 
참고로 개발 시스템에서는 2147483647 character만이 string builder의 capacity로 사용 가능. 

2. 1Byte 씩 계속해서 읽어가면서 문자열을 만들어간다. 
: 매우 안정적이지만, 엄청나게 느리다. 
FileStrema.ReadByte() 를 계속해서 호출하기 때문에 느리게 되는데. 상당히 비효율적인 코드로 보인다.;

3. Divide and Conquer
: 거의 방법은 첫번째 방법과 비슷하지만, 특정 BufferSize를 잡아서 그 BufferSize만큼 반복해서 읽어준다. FileStream.Read() 를 호출하는 것으로 약 4096 * 100 byte 씩 한번에 읽어서 처리하는 경우에 약 130 Mbyte의 text를 처리하는데 7sec 도 안걸리게 된다. (속도에 놀라버렸다. 같은 크기의 2번째 방법은 40여분이 걸려도 끝나지 않았다.; )
File의 끝까지 크기를 잡아주기 위해서 언제나 File의 Position과 BufferSize의 비교를 통해서 안정되게 문자열을 뽑아오는데 성공했다. T-T

List<string> messages = new List<string>();
using(FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    Console.WriteLine("Read Text");
    byte[] btDatas = new byte[BufferSize];
    List<byte> readBytes = new List<byte>();
    for(long index = readStartPosition; index < readEndPosition; )
    {
        fs.Position = index;
        int readLength = Math.Min(BufferSize, (int)(readEndPosition - index));
        int readCount = fs.Read(btDatas, 0, readLength);
        index += readCount;
 
       // 가장 불만인 코드. 한글자씩 얻어와서 찾는 방법 이외에는 없을까.. 라는 생각이 무척 많이 든다.
        for(int i = 0 ; i < readCount ; i++)
        {
            Byte readByte = btDatas[i];
            if(readByte == (byte)'\n')
            {
                string message = Encoding.UTF8.GetString(readBytes.ToArray());
                messages.Add(message);
                readBytes.Clear();
            }
            else
            {
                readBytes.Add(readByte);
            }
        }
    }
}
Posted by Y2K
,