파이썬 프로그램에서 파일 객체를 읽어 들인 후 내용을 수정하려면 크게 두 가지 방법이 있습니다. 첫 번째는 파일이 실제로 저장된 물리적 드라이브의 내용을 수정하는 방식이고, 두 번째는 시스템의 메모리(RAM)에 있는 데이터를 직접 수정하는 방식입니다. 이 글에서는 파이썬에서 기본 제공되는 mmap 모듈을 사용해 파일 객체를 읽고, 검색하고, 수정하는 방법을 살펴보겠습니다.
일반적으로 파일을 다룰 때는 open, read, lseek 같은 시스템 호출을 반복해서 사용해야 하지만, 메모리 매핑(memory-mapping)은 파일 데이터를 그대로 메모리에 올리기 때문에 마치 메모리상의 객체를 다루듯 파일을 직접 조작할 수 있다는 큰 장점이 있습니다. 특히 대용량 파일을 처리할 때 성능 차이가 두드러집니다.
메모리 매핑된 파일 읽기
아래 예제는 파일 전체를 한 번에 메모리로 읽어 들여 파일 객체 형태로 유지한 뒤, 읽기 모드로 접근하는 방식입니다. 파일 전체가 하나의 객체처럼 동작하므로, 슬라이싱(slicing)으로 원하는 위치의 텍스트를 손쉽게 추출할 수 있습니다.
예제 코드
import mmap
def read_mmap(fname):
with open(fname, mode="r", encoding="utf8") as fobj:
with mmap.mmap(fobj.fileno(), length=0, access=mmap.ACCESS_READ) as mmap_obj:
print(mmap_obj[4:26])
read_mmap('E:\\test.txt')실행 결과
'emissions from gaseous'
mmap으로 문자열 검색하기
메모리 매핑의 진가는 검색 성능에서 드러납니다. 아래 예제는 일반적인 파일 입출력 방식과 mmap 방식으로 각각 특정 문자열을 찾는 데 걸리는 시간을 비교합니다. 일반 방식은 파일 전체를 먼저 읽어야 하지만, mmap은 이미 메모리에 올라온 데이터를 바로 탐색하므로 실행 시간이 크게 단축됩니다.
예제 코드
import mmap
import time
def regular_io_find(fname):
with open(fname, mode="r", encoding="utf-8") as fobj:
text = fobj.read()
text.find("Death ")
def mmap_io_find(fname):
with open(fname, mode="r", encoding="utf-8") as fobj:
with mmap.mmap(fobj.fileno(), length=0, access=mmap.ACCESS_READ) as mmap_obj:
mmap_obj.find(b"Death ")
start_time_r = time.time()
regular_io_find('E:\\emissions.txt')
end_time_r = time.time()
print("Regular read start time :", start_time_r)
print("Regular read end time :", end_time_r)
print('Regular read time to find : {0}'.format(end_time_r - start_time_r))
start_time_m = time.time()
mmap_io_find('E:\\emissions.txt')
end_time_m = time.time()
print("mmap read start time :", start_time_m)
print("mmap read end time :", end_time_m)
print('mmap read time to find : {0}'.format(end_time_m - start_time_m))실행 결과
2013 Regular read start time : 1609812463.2718163 Regular read end time : 1609812463.2783241 Regular read time to find: 0.00650787353515625 mmap read start time : 1609812463.2783241 mmap read end time : 1609812463.2783241 mmap read time to find : 0.0
결과를 보면 일반 입출력 방식은 약 0.0065초가 걸린 반면, mmap 방식은 거의 0에 수렴하는 시간 안에 검색을 완료했습니다.
파일에 데이터 쓰기
mmap은 쓰기 작업도 지원합니다. 아래 예제에서는 파일을 r+ 모드(읽기와 쓰기 모두 허용)로 연 뒤, mmap.ACCESS_WRITE 접근 권한으로 메모리 매핑 객체를 생성합니다. 그다음 슬라이싱으로 원하는 위치를 지정하고, 해당 영역에 새 문자열을 덮어쓴 후 flush()를 호출해 변경 사항을 실제 파일에 반영합니다.
예제 코드
import mmap
def mmap_io_write(fname):
with open(fname, mode="r+") as fobj:
with mmap.mmap(fobj.fileno(), length=0, access=mmap.ACCESS_WRITE) as mmap_obj:
mmap_obj[20:26] = b"Hello!"
mmap_obj.flush()
mmap_io_write('E:\\emissions.txt')위 코드를 실행한 후 파일을 열어 보면, 20번째 바이트부터 26번째 바이트 위치까지 "Hello!"라는 문자열이 기록된 것을 확인할 수 있습니다.
정리하면, mmap 모듈은 대용량 파일을 반복적으로 읽거나 검색·수정해야 하는 상황에서 시스템 호출 오버헤드를 줄여 주는 강력한 도구입니다. 다만 운영체제마다 동작 세부 사항이 다를 수 있으므로, 실제 프로젝트에 적용하기 전에 사용 환경에서 충분히 테스트해 보는 것이 좋습니다.