2011-02-10 7 views
21

cProfile으로 상속 한 레거시 코드를 프로파일했습니다. simplejson의 C 확장을 사용하는 것과 같이 도움이 된 많은 변화가있었습니다.파일 쓰기 속도 향상

기본적으로이 스크립트는 한 시스템에서 ASCII 고정 폭 파일로 데이터를 내보내고 있습니다. 각 행은 레코드이고 많은 값을가집니다. 각 줄은 7158 자이며 1 톤의 공백이 있습니다. 총 레코드 수는 150 만 레코드입니다. 각 행은 한 번에 하나씩 생성되며 잠시 (5-10 행을 초 단위로) 소요됩니다.

각 행이 생성되면 가능한 한 간단하게 디스크에 기록됩니다. 프로파일 링은 전체 시간의 19-20 %가 file.write()에 소비되었음을 나타냅니다. 1,500 행의 테스트 케이스의 경우 20 초입니다. 그 숫자를 줄이고 싶습니다.

이제는 디스크에 쓰는 데 드는 시간이 줄어들 것입니다. 가능하다면 그것을 줄이고 싶습니다. 메모리 캐시를 보관할 수는 있지만 끝날 때까지 기다릴 수는 없으며 한번에 모두 덤프 할 수 있습니다.

fd = open(data_file, 'w') 
for c, (recordid, values) in enumerate(generatevalues()): 
     row = prep_row(recordid, values) 
     fd.write(row) 
     if c % 117 == 0: 
       if limit > 0 and c >= limit: 
         break 
       sys.stdout.write('\r%s @ %s' % (str(c + 1).rjust(7), datetime.now())) 
       sys.stdout.flush() 

내 첫 번째 생각은 목록의 레코드 캐시를 유지하고 일괄 적으로 작성하는 것입니다. 그게 더 빠를까요? 뭔가 같은 :

rows = [] 
for c, (recordid, values) in enumerate(generatevalues()): 
     rows.append(prep_row(recordid, values)) 
     if c % 117 == 0: 
      fd.write('\n'.join(rows)) 
      rows = [] 

내 생각은 또 다른 스레드를 사용하는 것입니다,하지만 그 안에 죽고 싶어.

+0

응용 프로그램의 병목 현상은 무엇입니까? –

+2

나는 내가 분명하다고 생각했다. 한 번에 한 행씩 디스크에 쓰는 시간의 20 %를 소비합니다. – chmullig

+0

글쎄, 변경하고 프로필? 파일 I/O가 일반적으로 버퍼에 저장되어 있기 때문에 거의 효과가 없을 것으로 예상됩니다. – delnan

답변

19

500 개 그룹으로 쓰기를 일괄 처리하면 실제로 쓰기가 상당히 빨라졌습니다. 이 테스트 케이스의 경우, 쓰기 행은 I/O에서 개별적으로 21.051 초가 걸렸지 만, 117의 일괄 쓰기에서는 동일한 수의 행을 쓰는 데 5.685 초가 걸렸습니다. 500 개의 배치는 총 0.266 초 밖에 걸리지 않았습니다.

+3

이제 asynhronus 쓰기를 위해 파일 쓰기에 스레딩을 추가 할 수 있습니다. – ted

+0

더 나은 방법은 stdout으로 출력하고 파이프로 파일을 처리 한 다음 OS에서 버퍼링 및 배치 쓰기를 수행하도록하는 것입니다. :) –

+0

@LesterCheung, 정교하게 만들 수 있습니까? – blindguy

32

사실, 귀하의 문제는 file.write()에 20 %의 시간이 걸리는 것이 아닙니다. 그 시간의 80 %는 file.write()에 없습니다!

디스크에 쓰기가 느립니다. 당신이 그것에 대해 할 수있는 일은 정말로 없습니다. 디스크에 내용을 기록하는 데 아주 많은 시간이 걸립니다. 속도를 높이기 위해 할 수있는 일은 거의 없습니다.

원하는 것은 I/O 시간이 프로그램의 가장 큰 부분이므로 속도가 하드 디스크의 처리 속도가 아닌 속도에 의해 제한됩니다. 이상적인 것은 file.write()가 100 % 사용법입니다!

+0

+1 그리고 일반적으로 나는 당신과 동의 할 것입니다. 그러나이 경우 데이터를 생성하는 데 사용하는 외부 프로세스는 매우 느리고 가능한 한 모든 것을 최소화하려고합니다. 나는 몇 초안에 이야기해야하고 그것을 줄이는데 집중해야한다고 생각합니다. # 초를 명확히하기 위해 편집합니다. – chmullig

+0

1,500 개의 레코드를 쓰는 데 20 초가 걸리도록 업데이트되었습니다. 나는 그 숫자에 대해 정말로 신경을 쓴다. 나는 단지 그것이 실제로 가치가 있다는 것을 증명하기 위해 퍼센트를 사용하고 있었다. – chmullig

+0

@chmullig, 어떤 %가 외부 프로세스를 기다리고 있습니까? –

1

파이썬에서 mmap을 할 수 있습니다. 이 도움이됩니다. 그러나 20 초 동안 7k * 1500이 약 0.5 Mbytes/s이기 때문에 프로파일 링하는 동안 실수를 한 것으로 의심됩니다. 같은 길이의 임의의 선을 쓰는 테스트를 해보면 훨씬 빠른 속도를 보입니다.