두 손끝의 창조자

DirectByteBuffer 를 이용한 File Reader 구현 본문

프로그래밍언어/Java

DirectByteBuffer 를 이용한 File Reader 구현

codinglog 2020. 8. 25. 08:13

대용량 파일을 읽어 라인별 처리를 하는 것을 시도하였다.

DirectByteBuffer를 사용한 이유는 대용량 데이터를 시스템에서 읽어서 jvm에 올리는 비용을 줄이고자 함이었다.

내가 필요한 작업은 파일을 일어서 행별로 읽어들이는 거였는데 DirectByteBuffer는 행단위 Reader를 제공하지 않는다. 그래서 별도로 구현을 했는데 구현 과정에 byte배열에 복사하고 하는 과정이 제법 있어서그런지 이렇다할 성능개선은 보이지 았았고 오히려 느려졌다.

DirectByteBuffer는 데이터를 읽어 별도로 다른 buffer에 넣지 않고 즉시처리, 예를 들어서 다른 socket으로 보낸다던지 하는 경우 성능개선에 도움이 될 것같다.

import sun.nio.ch.DirectBuffer;

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class FileChannelReader implements Closeable {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FileChannelReader.class);
    private final FileChannel fileChannel;
    private final ByteBuffer fileReadBuffer;
    private final byte LINE_FEED = 0x0A;
    private final byte CARRIAGE_RETURN = 0x0D;
    private int readBufferPosition = 0;
    private long readBytes;

    public FileChannelReader(Path resource) throws IOException {
        this(resource, 100);
    }

    public FileChannelReader(Path resource, int bufferSize) throws IOException {
        this.fileChannel = FileChannel.open(resource, StandardOpenOption.READ);
        fileReadBuffer = ByteBuffer.allocateDirect(bufferSize);
    }

    public final void close() throws IOException {
        ((DirectBuffer)fileReadBuffer).cleaner().clean();
        this.fileChannel.close();
    }

    private int readFromChannel() throws IOException {
        return fileChannel.read(fileReadBuffer);
    }

    private byte beforeNewLine;

    private int newLineLength() {
        byte newLine1 = fileReadBuffer.get(readBufferPosition);

        if (newLine1 == LINE_FEED || newLine1 == CARRIAGE_RETURN) {
            if (readBufferPosition + 1 < fileReadBuffer.limit()) {
                byte newLine2 = fileReadBuffer.get(readBufferPosition + 1);
                if (newLine1 != newLine2 && (newLine2 == LINE_FEED || newLine2 == CARRIAGE_RETURN)) {
                    return 2;
                }
            }
            if (beforeNewLine != 0 && beforeNewLine != newLine1) {
                return 0;
            }
            beforeNewLine = newLine1;
            return 1;
        } else {
            beforeNewLine = 0;
            return -1;
        }
    }

    public String readLine() throws IOException {
        byte[] lineBuffer = new byte[0];

        while (true) {
            //fileReadBuffer 에 읽고 있는 것이 있는지 확인한다.
            if (fileReadBuffer.position() == 0) {
                readBytes = fileChannel.read(fileReadBuffer);
                if (readBytes < 0)
                    break;
                fileReadBuffer.flip();
            }

            while (readBufferPosition < readBytes) {
                int newLineLength = newLineLength();
                if(newLineLength == 0){
                    fileReadBuffer.position(++readBufferPosition);
                }
                if (newLineLength > 0) {
                    fileReadBuffer.limit(readBufferPosition);
                    readBufferPosition = readBufferPosition + newLineLength;
                    if (lineBuffer.length == 0) {
                        byte[] remainingBytes = new byte[fileReadBuffer.remaining()];
                        fileReadBuffer.get(remainingBytes);
                        fileReadBuffer.limit((int) readBytes);
                        fileReadBuffer.position(readBufferPosition);
                        return new String(remainingBytes);
                    } else {
                        byte[] c = new byte[lineBuffer.length + fileReadBuffer.remaining()];
                        System.arraycopy(lineBuffer, 0, c, 0, lineBuffer.length);
                        fileReadBuffer.get(c, lineBuffer.length, fileReadBuffer.remaining());
                        fileReadBuffer.limit((int) readBytes);
                        fileReadBuffer.position(readBufferPosition);
                        return new String(c);
                    }
                } else {
                    readBufferPosition++;
                }

            }
            if (fileReadBuffer.remaining() > 0) {
                if (lineBuffer.length != 0) {
                    byte[] c = new byte[lineBuffer.length + fileReadBuffer.remaining()];
                    System.arraycopy(lineBuffer, 0, c, 0, lineBuffer.length);
                    fileReadBuffer.get(c, lineBuffer.length, fileReadBuffer.remaining());
                    lineBuffer = c;
                } else {
                    lineBuffer = new byte[fileReadBuffer.remaining()];
                    fileReadBuffer.get(lineBuffer);
                }
            }

            readBufferPosition = 0;
            fileReadBuffer.clear();
        }
        return null;
    }
}
반응형
Comments