Разгон по левой полосе.

Мы проводим много времени в ожидании завершения некоторой задачи по подготовке данных - вы бы сказали, что это удел специалистов по данным. Что ж, мы можем ускорить процесс. Вот два метода, которые вам пригодятся: файлы с отображением памяти и многопоточность.

Данные

Недавно мне пришлось извлекать термины и их частоту из Корпуса Google Книг Ngram, и я задумался, есть ли способы ускорить выполнение задачи. Корпус состоит из двадцати шести файлов общим объемом 24 ГБ. Каждый из интересующих меня файлов содержит термин и другие метаданные, разделенные табуляцией. Подход грубой силы к чтению этих файлов как фреймов данных pandas был… медленным. Поскольку нам нужны были только уникальные термины и их количество совпадений, я подумал, что постараюсь сделать это быстрее :-)

Файлы с отображением памяти

Эта техника не нова. Он существует уже давно и возник в Unix (до Linux!). Вкратце, mmap обходит обычную буферизацию ввода-вывода, загружая содержимое файла в страницы памяти. Это очень хорошо работает для компьютеров с большим объемом памяти. Это в основном нормально для современных настольных компьютеров и ноутбуков, где 32 ГБ памяти больше не относятся к сфере эзотерики. Библиотека Python имитирует большую часть функций Unix и предлагает удобную функцию readline() для извлечения байтов по одной строке за раз.

# map the entire file into memory
mm = mmap.mmap(fp.fileno(), 0)
# iterate over the block, until next newline
for line in iter(mm.readline, b""):
    # convert the bytes to a utf-8 string and split the fields
    term = line.decode("utf-8").split("\t")

fp - это указатель на файл, который ранее был открыт с атрибутом доступа r+b. Итак, с помощью этой простой настройки вы сделали чтение файлов в два раза быстрее (ну, точное улучшение будет зависеть от многих вещей, таких как HW диска и т. Д.).

Многопоточность

Следующий метод, который всегда помогает ускорить работу, - это добавление параллелизма. В нашем случае задача была связана с вводом-выводом. Это хорошо подходит для увеличения масштаба - т. Е. добавления потоков. Вы найдете хорошие дискуссии о том, когда лучше выполнять горизонтальное масштабирование (множественную обработку) в поисковых системах.

Python3 имеет отличную стандартную библиотеку для управления пулом потоков и динамического назначения им задач. И все это с невероятно простым API.

# use as many threads as possible, default: os.cpu_count()+4
with ThreadPoolExecutor() as threads:
   t_res = threads.map(process_file, files)

Значение по умолчанию max_workers для ThreadPoolExecutor - 5 потоков на ядро ​​ЦП (начиная с Python v3.8). map()API получит функцию, которая будет применяться к каждому члену списка, и запустит функцию автоматически, когда потоки станут доступными. Вот это да. Так просто. Менее чем за пятьдесят минут я преобразовал входные 24 ГБ в удобный набор данных 75 МБ для анализа с помощью pandas - вуаля.

Полный код находится на GitHub. Комментарии и замечания всегда приветствуются.

PS: Я добавил индикатор выполнения с tqdm для каждого потока. Я действительно не знаю, как им удается избежать зацикливания строк на экране ... Работает как шарм.

Обновление: я только что узнал о библиотеке vaex. Он реализует отображение памяти из коробки. Выглядит отлично.