내가 어디에서 왔는지에 대한 몇 가지 맥락. 코드 스 니펫이 끝났습니다.
가능하면 H2O와 같은 오픈 소스 도구를 사용하여 초 고성능 병렬 CSV 파일 읽기를 선호하지만이 도구는 기능 세트에 제한이 있습니다. 지도 학습을 위해 H2O 클러스터에 공급하기 전에 데이터 과학 파이프 라인을 만들기 위해 많은 코드를 작성하게되었습니다.
멀티 프로세싱 라이브러리의 풀 객체 및 맵 기능으로 많은 병렬 처리를 추가하여 UCI 리포지토리에서 8GB HIGGS 데이터 세트와 40GB CSV 파일과 같은 파일을 데이터 과학 목적으로 훨씬 빠르게 읽었습니다. 예를 들어 가장 가까운 이웃 검색을 사용하는 클러스터링과 DBSCAN 및 Markov 클러스터링 알고리즘은 심각하게 어려운 메모리 및 월 클럭 시간 문제를 우회하기 위해 병렬 프로그래밍 기술이 필요합니다.
나는 보통 gnu 도구를 사용하여 파일을 행 단위로 나누고 python 프로그램에서 병렬로 파일을 찾아서 읽을 수 있도록 glob-filemask합니다. 나는 일반적으로 1000+ 부분 파일과 같은 것을 사용합니다. 이러한 트릭을 수행하면 처리 속도 및 메모리 제한에 크게 도움이됩니다.
pandas dataframe.read_csv는 단일 스레드이므로 병렬 실행을 위해 map ()을 실행하여 팬더를 훨씬 빠르게 만들기 위해 이러한 트릭을 수행 할 수 있습니다. htop을 사용하여 일반 오래된 순차 팬더 dataframe.read_csv를 사용하면 하나의 코어에서 100 % CPU가 디스크가 아닌 pd.read_csv의 실제 병목 현상임을 알 수 있습니다.
SATA6 버스에서 회전하는 HD가 아니라 16 개의 CPU 코어가 아닌 빠른 비디오 카드 버스에서 SSD를 사용하고 있습니다.
또한 일부 응용 프로그램에서 훌륭하게 작동하는 것으로 밝혀진 또 다른 기술은 하나의 큰 파일을 여러 부분 파일로 사전 분할하는 대신 하나의 거대한 파일 내에서 병렬 CSV 파일 읽기를 수행하여 파일마다 다른 오프셋에서 각 작업자를 시작하는 것입니다. 각 병렬 작업자에서 python의 seek () 및 tell ()을 사용하여 큰 텍스트 파일을 큰 파일의 다른 바이트 오프셋 시작 바이트 및 끝 바이트 위치에서 동시에 동시에 읽을 수 있습니다. 바이트에서 정규식 찾기를 수행하고 줄 바꿈 수를 반환 할 수 있습니다. 이것은 부분 합계입니다. 마지막으로 작업자가 완료 한 후지도 함수가 반환 될 때 전체 합계를 얻기 위해 부분 합계를 합산합니다.
다음은 병렬 바이트 오프셋 트릭을 사용하는 예제 벤치 마크입니다.
나는 2 개의 파일을 사용합니다 : HIGGS.csv는 8GB입니다. UCI 기계 학습 저장소에서 제공됩니다. all_bin .csv는 40.4GB이며 현재 프로젝트에서 가져온 것입니다. 나는 리눅스와 함께 제공되는 GNU wc 프로그램과 내가 개발 한 순수한 python fastread.py 프로그램의 두 가지 프로그램을 사용한다.
HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv
-rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv
HP-Z820:/mnt/fastssd$ ls -l all_bin.csv
-rw-rw-r-- 1 40412077758 Feb 2 09:00 all_bin.csv
ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2
2367496
real 0m8.920s
user 1m30.056s
sys 2m38.744s
In [1]: 40412077758. / 8.92
Out[1]: 4530501990.807175
약 4.5GB / s (45Gb / s) 파일 슬러 핑 속도입니다. 내 친구 친구, 하드 디스크가 회전하지 않습니다. 실제로 삼성 Pro 950 SSD입니다.
다음은 순수한 C 컴파일 프로그램 인 gnu wc에 의해 동일한 파일에 대한 속도 벤치 마크입니다.
멋진 점은 순수한 파이썬 프로그램 이이 경우 gnu wc 컴파일 된 C 프로그램의 속도와 본질적으로 일치한다는 것을 알 수 있습니다. 파이썬은 해석되지만 C는 컴파일되므로 속도가 매우 흥미 롭습니다. 나는 당신이 동의 할 것이라고 생각합니다. 물론, wc는 실제로 병렬 프로그램으로 변경되어야하며, 실제로 파이썬 프로그램에서 양말을 이길 것입니다. 그러나 오늘날에도 gnu wc는 순차적 인 프로그램입니다. 당신은 당신이 할 수있는 일을하고, 파이썬은 오늘날 병렬 할 수 있습니다. Cython 컴파일은 나를 도울 수 있습니다 (다른 시간 동안). 또한 메모리 매핑 파일은 아직 탐색되지 않았습니다.
HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv
2367496 all_bin.csv
real 0m8.807s
user 0m1.168s
sys 0m7.636s
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000
real 0m2.257s
user 0m12.088s
sys 0m20.512s
HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv
real 0m1.820s
user 0m0.364s
sys 0m1.456s
결론 : 속도는 C 프로그램에 비해 순수한 파이썬 프로그램에 적합합니다. 그러나 최소한 라인 계산 목적으로 C 프로그램보다 순수한 파이썬 프로그램을 사용하는 것만으로는 충분하지 않습니다. 일반적 으로이 기술은 다른 파일 처리에 사용될 수 있으므로이 파이썬 코드는 여전히 좋습니다.
질문 : 정규식을 한 번만 컴파일하고 모든 근로자에게 전달하면 속도가 향상됩니까? 답 : 정규식 사전 컴파일은이 응용 프로그램에서 도움이되지 않습니다. 그 이유는 모든 작업자의 프로세스 직렬화 및 생성 오버 헤드가 지배적이라고 생각합니다.
하나 더. 병렬 CSV 파일 읽기가 도움이 되나요? 디스크에 병목 현상이 있습니까, 아니면 CPU입니까? 스택 오버 플로우에 대한 많은 소위 최상위 답변에는 파일을 읽는 데 하나의 스레드 만 필요하다는 공통의 지혜가 포함되어 있습니다. 그래도 확실합니까?
알아 보자:
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000
real 0m2.256s
user 0m10.696s
sys 0m19.952s
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000
real 0m17.380s
user 0m11.124s
sys 0m6.272s
예, 그렇습니다. 병렬 파일 읽기는 아주 잘 작동합니다. 잘가요!
추신. 일부 작업자가 알고 싶어하는 경우 단일 작업자 프로세스를 사용할 때 balanceFactor가 2 인 경우 어떻게해야합니까? 글쎄, 끔찍하다.
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2
11000000
real 1m37.077s
user 0m12.432s
sys 1m24.700s
fastread.py 파이썬 프로그램의 주요 부분 :
fileBytes = stat(fileName).st_size # Read quickly from OS how many bytes are in a text file
startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
p = Pool(numProcesses)
partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
globalSum = sum(partialSum)
print(globalSum)
def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'): # counts number of searchChar appearing in the byte range
with open(fileName, 'r') as f:
f.seek(startByte-1) # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
bytes = f.read(endByte - startByte + 1)
cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
return cnt
PartitionDataToWorkers의 def는 일반적인 순차 코드입니다. 다른 사람이 병렬 프로그래밍이 어떤지 연습하고 싶어하는 경우에 대비하여 생략했습니다. 학습의 이점을 위해 테스트 및 병렬 코드를 제공하는 더 어려운 부분을 무료로 제공했습니다.
덕분에 : Arno와 Cliff와 H2O 직원의 오픈 소스 H2O 프로젝트는 훌륭한 소프트웨어 및 교육용 비디오를 제공하여 위와 같이이 순수한 파이썬 고성능 병렬 바이트 오프셋 리더에 영감을주었습니다. H2O는 java를 사용하여 병렬 파일 읽기를 수행하고, Python 및 R 프로그램에서 호출 할 수 있으며, 큰 CSV 파일을 읽을 때 지구상에서 가장 빠릅니다.