Параллельный анализ файлов, несколько ядер процессора

Я задал ранее связанный, но очень общий вопрос (см. особенно этот ответ).

Этот вопрос очень конкретный. Это все код, который мне очень нравится:

result = {}
for line in open('input.txt'):
  key, value = parse(line)
  result[key] = value

Функция parse полностью автономна (т.е. не использует общие ресурсы).

У меня есть процессор Intel i7-920 (4 ядра, 8 потоков, я думаю, что потоки более актуальны, но я не уверен).

Что я могу сделать, чтобы моя программа использовала все параллельные возможности этого процессора?

Я предполагаю, что я могу открыть этот файл для чтения в 8 разных потоках без большого снижения производительности, поскольку время доступа к диску невелико относительно общего времени.

Ответ 1

cPython не предоставляет модель потоков, которую вы ищете легко. Вы можете получить что-то подобное с помощью модуля multiprocessing и пула процессов

такое решение может выглядеть примерно так:

def worker(lines):
    """Make a dict out of the parsed, supplied lines"""
    result = {}
    for line in lines.split('\n'):
        k, v = parse(line)
        result[k] = v
    return result

if __name__ == '__main__':
    # configurable options.  different values may work better.
    numthreads = 8
    numlines = 100

    lines = open('input.txt').readlines()

    # create the process pool
    pool = multiprocessing.Pool(processes=numthreads)

    # map the list of lines into a list of result dicts
    result_list = pool.map(worker, 
        (lines[line:line+numlines] for line in xrange(0,len(lines),numlines) ) )

    # reduce the result dicts into a single dict
    result = {}
    map(result.update, result_list)

Ответ 2

  • разделить файл на 8 небольших файлов
  • запустить отдельный script для обработки каждого файла
  • присоединиться к результатам

Почему это лучший способ...

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

Ответ 3

Вы можете использовать модуль multiprocessing, но если функция parse() выполняется быстро, вы не получите большого улучшения производительности, выполнив это.

Ответ 4

Как сказал TokenMacGuy, вы можете использовать модуль multiprocessing. Если вам действительно нужно разобрать массивный объем данных, вы должны проверить проект .

Дискотека - это распределенные вычисления основываясь на MapReduce парадигма. Дискотека с открытым исходным кодом; разработанный Nokia Research Center для решать реальные проблемы при обработке огромное количество данных.

Он действительно масштабируется для заданий, где ваша задача parse() является "чистой" (т.е. не использует общие ресурсы) и имеет интенсивность процессора. Я протестировал работу на одном ядре, а затем сравнил ее с тремя хостами с 8 ядрами. Фактически он работал в 24 раза быстрее, когда запускался в кластере Disco (ПРИМЕЧАНИЕ: проверено на неоправданно интенсивную работу ЦП).

Ответ 5

  • создать распределенную архитектуру с помощью rabbitMQ, одна задача продюсера читать файл по строкам и отправлять строки рабочим через rabbitMQ
  • использовать консольную утилиту, такую ​​как unix/parallel, xargs
    $ python makelist.py | parallel -j+2 'wget "{}" -O - | python parse.py'
    
    или этот стиль
$ ls *.wav | xargs -n1 --max-procs=4 -I {} lame {} -o {}.mp3

В любом случае вам нужно реализовать парадигму map/reduce

Ответ 6

Это можно сделать с помощью Ray, библиотеки для написания параллельного и распределенного Python.

Чтобы запустить приведенный ниже код, сначала создайте input.txt следующим образом.

printf "1\n2\n3\n4\n5\n6\n" > input.txt

Затем вы можете обработать файл параллельно, добавив декоратор @ray.remote в функцию parse и выполнив несколько копий параллельно, как @ray.remote ниже.

import ray
import time

ray.init()

@ray.remote
def parse(line):
    time.sleep(1)
    return 'key' + str(line), 'value'

# Submit all of the "parse" tasks in parallel and wait for the results.
keys_and_values = ray.get([parse.remote(line) for line in open('input.txt')])
# Create a dictionary out of the results.
result = dict(keys_and_values)

Обратите внимание, что оптимальный способ сделать это будет зависеть от того, сколько времени потребуется для запуска функции parse. Если это занимает одну секунду (как указано выше), то имеет смысл разбирать одну строку на задачу Ray. Если это занимает 1 миллисекунду, то, вероятно, имеет смысл проанализировать несколько строк (например, 100) для задачи Ray.

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

Смотрите документацию Ray.