Немного контекста заранее о том, откуда я иду. Фрагменты кода в конце.
Когда я могу, я предпочитаю использовать инструмент с открытым исходным кодом, такой как H2O, для высокопроизводительного параллельного чтения файлов CSV, но этот инструмент ограничен в наборе функций. Я заканчиваю тем, что пишу много кода для создания конвейеров данных, прежде чем подавать его в кластер H2O для обучения под наблюдением.
Я читал файлы, такие как набор данных HIGGS 8 ГБ из репозитория UCI и даже файлы CSV 40 ГБ для целей обработки данных, значительно быстрее, добавляя много параллелизма с помощью объекта пула и карты функции многопроцессорной библиотеки. Например, кластеризация с поиском ближайшего соседа, а также алгоритмами кластеризации DBSCAN и Маркова требует некоторого изощренного параллельного программирования, чтобы обойти некоторые серьезные проблемы с памятью и временем настенных часов.
Я обычно люблю разбивать файл по строкам на части, используя инструменты gnu, а затем glob-filemask их все, чтобы найти и прочитать их параллельно в программе python. Я обычно использую что-то вроде 1000+ частичных файлов. Выполнение этих приемов очень помогает в скорости обработки и ограничении памяти.
Pandas dataframe.read_csv является однопоточным, поэтому вы можете сделать эти трюки, чтобы сделать панды более быстрыми, запустив map () для параллельного выполнения. Вы можете использовать htop, чтобы увидеть, что с обычными старыми последовательными пандами dataframe.read_csv 100% ЦП только на одном ядре - это фактическое узкое место в pd.read_csv, а не на диске вообще.
Я должен добавить, что я использую SSD на быстрой шине видеокарты, а не вращающийся HD на шине SATA6, плюс 16 процессорных ядер.
Кроме того, другой метод, который я обнаружил, прекрасно работает в некоторых приложениях - это параллельное чтение файла CSV в одном гигантском файле, начиная с каждого рабочего с разным смещением в файле, вместо предварительного разделения одного большого файла на множество файлов деталей. Используйте Python для поиска файлов () и Tell () в каждом параллельном работнике, чтобы читать большой текстовый файл в виде полос, с разными байтовыми смещениями начальных и конечных байтов в большом файле, одновременно в одно и то же время. Вы можете выполнить поиск по всем регулярным выражениям в байтах и вернуть счетчик перевода строки. Это частичная сумма. Наконец, суммируйте частичные суммы, чтобы получить глобальную сумму, когда функция карты возвращается после того, как рабочие закончили.
Ниже приведены некоторые примеры тестов с использованием трюка с параллельным байтовым смещением:
Я использую 2 файла: HIGGS.csv составляет 8 ГБ. Это из хранилища машинного обучения UCI. all_bin .csv имеет размер 40,4 ГБ и является результатом моего текущего проекта. Я использую 2 программы: GNU wc, которая поставляется вместе с Linux, и программу на 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,5 ГБ / с, или 45 ГБ / с, скорость записи файла. Это не вращающийся жесткий диск, мой друг. Это на самом деле Samsung Pro 950 SSD.
Ниже приведен тест скорости для того же файла, который был посчитан строкой gnu wc, скомпилированной программой на чистом C.
Круто то, что вы можете видеть, что моя программа на чистом Python в этом случае практически соответствовала скорости скомпилированной программы gnu wc. Python интерпретируется, но C компилируется, так что это довольно интересный подвиг скорости, я думаю, вы согласитесь. Конечно, wc действительно нужно заменить на параллельную программу, и тогда он действительно побьет все мои программы на python. Но в нынешнем виде gnu wc - это просто последовательная программа. Вы делаете то, что можете, и Python может делать параллельное сегодня. Компиляция 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
Вывод: скорость хороша для чистой программы на Python по сравнению с программой на Си. Тем не менее, недостаточно использовать чистую программу на Python поверх программы на C, по крайней мере, для целей линейного монтажа. Обычно этот метод может использоваться для обработки других файлов, поэтому этот код на Python все еще хорош.
Вопрос: Будет ли компиляция регулярного выражения только один раз и передача его всем работникам улучшать скорость? Ответ: Предварительная компиляция Regex НЕ помогает в этом приложении. Я предполагаю, что причина в том, что накладные расходы на сериализацию и создание процессов для всех работников являются доминирующими.
Еще кое-что. Параллельное чтение файла CSV даже помогает? Является ли диск узким местом или процессором? Многие так называемые ответы с самым высоким рейтингом в stackoverflow содержат общую мудрость разработчиков, согласно которой вам нужен только один поток для чтения файла, говорят они, лучше всего. Хотя они уверены?
Давайте выясним:
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
Ах да, да, это так. Параллельное чтение файлов работает довольно хорошо. Ну вот и все!
Ps. В случае, если некоторые из вас хотят знать, что если 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
Ключевые части программы python 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 - это просто обычный последовательный код. Я пропустил это на тот случай, если кто-то еще захочет немного попрактиковаться в том, что такое параллельное программирование. Я бесплатно раздал более сложные части: проверенный и работающий параллельный код, для вашего удобства обучения.
Спасибо: Проект H2O с открытым исходным кодом, разработанный Арно и Клиффом и сотрудниками H2O за их великолепное программное обеспечение и обучающие видео, которые вдохновили меня на этот высокопроизводительный параллельный считыватель байтовых смещений на python, как показано выше. H2O выполняет параллельное чтение файлов с использованием Java, может вызываться программами на Python и R и быстро сходит с ума, быстрее, чем что-либо на планете, при чтении больших файлов CSV.