Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python mmap 완벽 가이드: 메모리 매핑으로 파일 읽기 성능 극대화하기

들어가며: mmap이란 무엇인가?

mmap은 메모리 매핑(memory mapping)의 약자입니다. 파일을 메모리에 매핑하면 운영체제의 가상 메모리를 활용해 일반적인 I/O 함수 대신 파일 시스템의 데이터에 직접 접근할 수 있습니다. 이 방식은 접근할 때마다 별도의 시스템 콜을 호출하거나 버퍼 간에 데이터를 복사할 필요가 없기 때문에 I/O 성능이 크게 향상됩니다.

실제로 메모리에 존재하는 데이터는 디스크 기반보다 빠릅니다. 예를 들어 SQLite 데이터베이스를 인메모리(in-memory)로 생성하면 디스크에 저장했을 때보다 훨씬 좋은 성능을 보입니다.

메모리 매핑된 파일은 용도에 따라 변경 가능한 문자열(mutable string) 또는 파일과 유사한 객체(file-like object)로 취급할 수 있습니다.

또한 mmap은 close(), flush(), read(), readline(), seek(), tell(), write() 등 다양한 메서드를 지원하며, 슬라이스 연산은 물론 정규 표현식(regular expression)과도 손쉽게 함께 사용할 수 있습니다.

실습 준비: 샘플 텍스트 파일 만들기

1단계. input.txt 파일 생성

아래 내용이 담긴 텍스트 파일이 있다고 가정합니다. 구글에서 샘플 텍스트를 검색해 동일한 내용을 얻을 수 있으며, 이 내용을 input.txt 파일에 저장합니다.

Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.

Id porro facete cum. No est veritus detraxit facilisis, sit ea clita decore essent. Ut eam labores fuisset menandri, ex sit brute viderer eleifend, altera argumentum vel ex. Duo at zril sensibus, eu vim ullum assentior, quando possit at his.

Te nam tempor posidonium scripserit, eam mundi reprimique dissentias ne. Vim te soleat offendit democritum. Nam an diam elaboraret, quaeque dissentias an has. Autem legendos dignissim ad vis, sea ex amet petentium reprehendunt, inermis constituam philosophia ne mel. Esse noster lobortis usu ne.

Nec reque postea urbanitas ut, mea in nulla invidunt ocurreret. Ei duo iuvaret numquam. Ferri nemore audire te est, mel et detracto noluisse. Nec eu habeo justo, id pro posse apeirian volutpat. Mea sonet quaestio ne.

Atqui quaeque alienum te vim. Graeco aliquip liberavisse pro ut. Te similique reformidans usu, te mundi aliquando ius. Meis scripta minimum quo no, meis prima fabellas eu eam, laoreet delicata forensibus ut vim. Et quo vocibus mediocritatem, atqui summo an eam.

mmap() 함수로 메모리 매핑 파일 읽기

2단계. 파일 매핑 및 읽기

mmap() 함수를 사용해 메모리 매핑 파일을 생성합니다. 파일명은 파일 객체의 fileno() 메서드나 os.open()을 통해 전달할 수 있습니다.

참고: mmap()을 호출하기 전에 파일을 열고, 작업이 끝난 후 닫는 것은 사용자의 책임입니다.

mmap()의 두 번째 인수는 매핑할 파일 영역의 크기를 바이트 단위로 지정합니다. 값이 0이면 파일 전체가 매핑됩니다. 그리고 선택적으로 접근 모드를 지정할 수 있는데, ACCESS_READ(읽기 전용), ACCESS_WRITE(쓰기 반영), ACCESS_COPY(쓰기 시 복사) 세 가지가 있습니다.

import mmap

input_text = """Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.

Id porro facete cum. No est veritus detraxit facilisis, sit ea clita decore essent. Ut eam labores fuisset menandri, ex sit brute viderer eleifend, altera argumentum vel ex. Duo at zril sensibus, eu vim ullum assentior, quando possit at his.

Te nam tempor posidonium scripserit, eam mundi reprimique dissentias ne. Vim te soleat offendit democritum. Nam an diam elaboraret, quaeque dissentias an has. Autem legendos dignissim ad vis, sea ex amet petentium reprehendunt, inermis constituam philosophia ne mel. Esse noster lobortis usu ne.

Nec reque postea urbanitas ut, mea in nulla invidunt ocurreret. Ei duo iuvaret numquam. Ferri nemore audire te est, mel et detracto noluisse. Nec eu habeo justo, id pro posse apeirian volutpat. Mea sonet quaestio ne.

Atqui quaeque alienum te vim. Graeco aliquip liberavisse pro ut. Te similique reformidans usu, te mundi aliquando ius. Meis scripta minimum quo no, meis prima fabellas eu eam, laoreet delicata forensibus ut vim. Et quo vocibus mediocritatem, atqui summo an eam.

"""

# 텍스트가 담긴 입력 파일 생성
input_file = 'input.txt'
f = open(input_file, "w+")
f.write(input_text)
f.close()

# 읽기 모드로 파일 열기
with open(input_file, 'r') as f:
    with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
        print(f"Output \n*** Output first 5 bytes of the {input_file} is {m.read(5)} ")
        print(f"*** Output Next 10 bytes of the {input_file} is {m.read(10)} ")

실행 결과

*** Output first 5 bytes of the input.txt is b'Lorem'
*** Output Next 10 bytes of the input.txt is b' ipsum dol'

3단계. 파일 포인터의 동작 이해

위 예제에서는 파일을 메모리에 매핑한 뒤 .read()로 처음 5바이트를 읽었습니다. 첫 번째 read 호출 후 파일 포인터는 자동으로 앞으로 이동하므로, 이어서 read(10)을 호출하면 6~15번째 바이트가 반환됩니다. 즉, 일반 파일 객체처럼 순차적으로 읽기가 진행됩니다.

메모리 매핑 파일 내용 수정하기

4단계. 'r+' 모드로 열어 원본 파일 직접 수정

메모리 매핑 파일을 수정 가능한 상태로 만들려면 매핑하기 전에 'w'가 아닌 'r+' 모드로 파일을 열어야 합니다. 'w' 모드로 열면 파일 내용이 초기화되므로 주의하세요.

아래 예제는 파일의 특정 부분을 제자리(in-place)에서 수정하는 방법을 보여줍니다.

import mmap
import shutil

input_file = 'input.txt'
input_copy = input_file.replace('input','input_copy')

# 원본이 수정되지 않도록 파일 복사본 생성
shutil.copyfile(input_file,input_copy)

# 찾을 단어
word = b'ipsum'

# 수정할 단어 (문자열 뒤집기)
modified_word = word[::-1]

# 수정을 위해 파일 열기
with open(input_copy, 'r+') as f:
    with mmap.mmap(f.fileno(), 0) as m:
        print(f"output \n *** Line before updates \n {m.readline().rstrip()}")

        # seek으로 포인터 되감기
        m.seek(0)

        # 단어를 찾아 뒤집기
        loc = m.find(word)
        m[loc:loc + len(word)] = modified_word
        m.flush()

        # seek으로 포인터 되감기
        m.seek(0)
        print(f" \n *** Line after updates \n {m.readline().rstrip()}")

    f.seek(0)
    print(f" \n *** Final file \n {f.readline().rstrip()}")

실행 결과

*** Line before updates
b'Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.'

*** Line after updates
b'Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.'

*** Final file
Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.

5단계. 결과 확인

첫 번째 줄 중간에 있던 단어 "ipsum"이 메모리와 실제 파일 모두에서 "muspi"로 교체된 것을 확인할 수 있습니다. m.flush()를 호출했기 때문에 메모리의 변경 사항이 디스크의 파일에도 그대로 반영됩니다.

디스크 파일은 그대로 두고 메모리에서만 수정하기

6단계. ACCESS_COPY 옵션 활용

어떤 이유로든 변경 사항을 메모리에서만 확인하고 싶고, 디스크의 원본 파일은 수정하고 싶지 않다면 ACCESS_COPY 옵션을 사용하세요. 이 모드에서는 쓰기 시 복사(copy-on-write) 방식으로 동작하여 변경 내용이 원본 파일에 저장되지 않습니다.

import mmap
import shutil

input_file = 'input.txt'
input_copy = input_file.replace('input','input_copy')

# 원본이 수정되지 않도록 파일 복사본 생성
shutil.copyfile(input_file,input_copy)

# 찾을 단어
word = b'ipsum'

# 수정할 단어 (문자열 뒤집기)
modified_word = word[::-1]

# 수정을 위해 파일 열기
with open(input_copy, 'r+') as f:
    with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY) as m:
        print(f"output \n *** Line before updates \n {m.readline().rstrip()}")

        # seek으로 포인터 되감기
        m.seek(0)

        # 단어를 찾아 뒤집기
        loc = m.find(word)
        m[loc:loc + len(word)] = modified_word
        m.flush()

        # seek으로 포인터 되감기
        m.seek(0)
        print(f" \n *** Line after updates \n {m.readline().rstrip()}")

    f.seek(0)
    print(f" \n *** Final file \n {f.readline().rstrip()}")

실행 결과

*** Line before updates
b'Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.'

*** Line after updates
b'Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.'

*** Final file
Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.

7단계. 결과 비교

출력 결과를 자세히 살펴보면 흥미로운 차이를 발견할 수 있습니다. 메모리 내에서는 단어가 "muspi"로 변경되었지만, 최종 파일(Final file)에는 여전히 원래의 "ipsum"이 그대로 남아 있습니다. 즉, 변경 사항이 메모리에만 적용되고 디스크의 파일은 전혀 수정되지 않았습니다.

마무리 정리

지금까지 파이썬의 mmap 모듈을 활용해 파일을 메모리에 매핑하고 읽기 성능을 개선하는 방법을 알아보았습니다. 핵심을 요약하면 다음과 같습니다.

  • 성능 향상: 시스템 콜과 버퍼 복사 없이 가상 메모리로 파일에 직접 접근하므로 대용량 파일 처리에 유리합니다.
  • 유연한 접근 모드: ACCESS_READ(읽기 전용), ACCESS_WRITE(쓰기 반영), ACCESS_COPY(메모리 전용 수정) 상황에 맞게 선택할 수 있습니다.
  • 편리한 조작: 슬라이싱, find(), seek(), flush() 등 문자열·파일 객체처럼 직관적으로 다룰 수 있습니다.

대용량 로그 분석, 바이너리 파일 처리, 빈번한 임의 접근(random access)이 필요한 작업이라면 mmap 도입을 적극 고려해 보시기 바랍니다.