복붙노트

[PYTHON] Python readlines () 읽기 및 사용법

PYTHON

Python readlines () 읽기 및 사용법

폴더에서 ~ 1000KB의 텍스트 파일 (~ 400KB 크기의 각 파일에서 약 3000 줄)을 파싱하는 데 문제가 있습니다. 나는 readlines를 사용하여 그것들을 읽었고,

   for filename in os.listdir (input_dir) :
       if filename.endswith(".gz"):
          f = gzip.open(file, 'rb')
       else:
          f = open(file, 'rb')

       file_content = f.readlines()
       f.close()
   len_file = len(file_content)
   while i < len_file:
       line = file_content[i].split(delimiter) 
       ... my logic ...  
       i += 1  

이것은 내 입력 (50,100 파일)의 샘플을 위해 완벽하게 작동합니다. 전체 입력에서 5K 파일 이상을 실행했을 때 시간은 선형 증가분에 가깝지 않았습니다. 성능 분석을 계획하고 Cprofile 분석을 수행했습니다. 입력이 7K 파일에 도달했을 때 더 많은 파일이 급격하게 증가하는 데 걸리는 시간.

다음은 누적 리드 타임의 소요 시간입니다. 첫 번째 -> 354 개 파일 (입력 샘플) 및 두 번째 -> 7473 파일 (전체 입력)

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 354    0.192    0.001    **0.192**    0.001 {method 'readlines' of 'file' objects}
 7473 1329.380    0.178  **1329.380**    0.178 {method 'readlines' of 'file' objects}

이 때문에 입력 코드가 늘어남에 따라 코드에 걸리는 시간이 선형 적으로 조정되지 않습니다. 나는 사람들이 readlines ()가 전체 파일 내용을 메모리로 읽어들이므로 일반적으로 readline () 또는 read ()에 비해 더 많은 메모리를 소비한다고 주장하는 readlines ()에 대한 의사 노트를 읽습니다.

나는이 점에 동의하지만 가비지 수집기가 루프의 끝에서 메모리에서로드 된 내용을 자동으로 지워야하므로 즉석에서 내 메모리는 현재 처리 된 파일의 내용 만 가져야합니다. 그러나, 여기에 약간의 캐치가 있습니다. 누군가이 문제에 대해 통찰력을 줄 수 있습니까?

이것은 readlines ()의 고유 한 동작인가 파이썬 가비지 컬렉터에 대한 저의 잘못된 해석입니까? 알게되어 기뻐.

또한, 메모리와 시간 효율적인 방식으로 동일한 작업을 수행 할 수있는 몇 가지 대안을 제안하십시오. TIA.

해결법

  1. ==============================

    1.짧은 버전은 : readlines ()를 사용하는 효율적인 방법은 그것을 사용하지 않는 것입니다. 이제까지.

    짧은 버전은 : readlines ()를 사용하는 효율적인 방법은 그것을 사용하지 않는 것입니다. 이제까지.

    readlines ()에 대한 문서는 전체 파일을 메모리로 읽어 들여이를 행으로 구문 분석하고 그 행에서 문자열로 가득 찬 목록을 작성한다는 것을 명시 적으로 보장합니다.

    그러나 read ()에 대한 문서는 마찬가지로 전체 파일을 메모리로 읽어 들여 문자열을 작성하므로 도움이되지 않습니다.

    더 많은 메모리를 사용하는 것 외에는 모든 것을 읽을 때까지 작업을 수행 할 수 없다는 것을 의미합니다. 가장 순진한 방식으로 읽기와 처리를 번갈아 수행한다면, 적어도 일부 파이프 라이닝 (OS 디스크 캐시, DMA, CPU 파이프 라인 등 덕택입니다.)에서 이점을 얻을 수 있습니다. 따라서 다음 배치가 실행되는 동안 한 배치에서 작업하게됩니다 읽는 중입니다. 그러나 컴퓨터가 전체 파일을 읽도록 강제하고 전체 파일을 구문 분석 한 다음 코드를 실행하면 읽기 당 하나의 중복 된 영역 대신 전체 파일에 대해 중복되는 영역 하나만 가져옵니다.

    세 가지 방법으로이 문제를 해결할 수 있습니다.

    예를 들어, 이것은 한번에 foo를 모두 읽어야합니다 :

    with open('foo') as f:
        lines = f.readlines()
        for line in lines:
            pass
    

    그러나 이것은 한 번에 약 8K 만 읽습니다.

    with open('foo') as f:
        while True:
            lines = f.readlines(8192)
            if not lines:
                break
            for line in lines:
                pass
    

    그리고 한 번에 한 줄만 읽습니다. 파이썬은 일을 더 빠르게하기 위해 좋은 버퍼 크기를 선택할 수 있습니다.

    with open('foo') as f:
        while True:
            line = f.readline()
            if not line:
                break
            pass
    

    그리고 이것은 이전과 똑같은 일을 할 것입니다 :

    with open('foo') as f:
        for line in f:
            pass
    

    그 동안에:

    파이썬은 가비지 수집에 대해 그러한 보장을하지 않습니다.

    CPython 구현은 GC에서 refcounting을 사용합니다. 즉, 코드에서, file_content가 리바운드되거나 사라지 자마자 거대한 문자열 목록과 그 안에있는 모든 문자열이 freelist로 해제됩니다. 즉, 동일한 메모리를 다음 번 통과시 다시 사용할 수 있습니다.

    그러나 할당, 복사 및 할당 해제는 모두 무료가 아닙니다. 수행하지 않는 것이 훨씬 더 빠릅니다.

    그 중에서도 동일한 작은 메모리 덩어리를 반복적으로 재사용하는 대신 문자열을 여러 메모리에 분산시켜 캐시 동작을 손상시킬 수 있습니다.

    게다가, 메모리 사용량이 일정 할 수도 있지만 (파일 크기의 합계가 아닌 가장 큰 파일의 크기가 선형 임), mallocs를 처음으로 확장하는 것은 가장 느린 것 중 하나가 될 것입니다 당신이하는 일들 (성능 비교를하기가 훨씬 어려워집니다).

    이 모든 것을 한 데 모으면 다음과 같이 프로그램을 작성합니다.

    for filename in os.listdir(input_dir):
        with open(filename, 'rb') as f:
            if filename.endswith(".gz"):
                f = gzip.open(fileobj=f)
            words = (line.split(delimiter) for line in f)
            ... my logic ...  
    

    아니면, 아마도 :

    for filename in os.listdir(input_dir):
        if filename.endswith(".gz"):
            f = gzip.open(filename, 'rb')
        else:
            f = open(filename, 'rb')
        with contextlib.closing(f):
            words = (line.split(delimiter) for line in f)
            ... my logic ...
    
  2. ==============================

    2.전체 파일이 아니라 한 줄씩 읽습니다.

    전체 파일이 아니라 한 줄씩 읽습니다.

    for line in open(file_name, 'rb'):
        # process line here
    

    자동으로 파일을 닫을 때 사용하는 것이 더 좋습니다.

    with open(file_name, 'rb') as f:
        for line in f:
            # process line here
    

    위의 예제는 한 번에 한 줄씩 반복자를 사용하여 파일 객체를 읽습니다.

  3. from https://stackoverflow.com/questions/17246260/python-readlines-usage-and-efficient-practice-for-reading by cc-by-sa and MIT license