Такая же производительность у разных работников в многопроцессорной обработке

У меня очень простые случаи, когда работа, которая должна быть выполнена, может быть разбита и распределена между рабочими. Я попробовал очень простой многопроцессорный пример из здесь:

import multiprocessing
import numpy as np
import time

def do_calculation(data):
    rand=np.random.randint(10)
    print data, rand
    time.sleep(rand)
    return data * 2

if __name__ == '__main__':
    pool_size = multiprocessing.cpu_count() * 2
    pool = multiprocessing.Pool(processes=pool_size)

    inputs = list(range(10))
    print 'Input   :', inputs

    pool_outputs = pool.map(do_calculation, inputs)
    print 'Pool    :', pool_outputs

Вышеуказанная программа выводит следующий результат:

Input   : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0 7
1 7
2 7
5 7
3 7
4 7
6 7
7 7
8 6
9 6
Pool    : [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Почему одно и то же случайное число печатается? (У меня 4 процессора в моей машине). Это лучший/самый простой способ продвижения вперед?

Ответ 1

Думаю, вам нужно повторно засеять генератор случайных чисел, используя numpy.random.seed в вашей функции do_calculation.

Моя догадка заключается в том, что генератор случайных чисел (RNG) получает сеяние при импорте модуля. Затем, когда вы используете многопроцессорную обработку, вы форкируете текущий процесс с уже засеянным RNG. Таким образом, все ваши процессы используют одно и то же начальное значение для RNG и поэтому будут генерировать те же последовательности чисел.

например:.

def do_calculation(data):
    np.random.seed()
    rand=np.random.randint(10)
    print data, rand
    return data * 2

Ответ 2

В этом блоге приведен пример хорошей и плохой практики при использовании numpy.random и мультиобработки. Более важно понять, когда создается начальное число вашего генератора псевдослучайных чисел (PRNG):

import numpy as np
import pprint
from multiprocessing import Pool

pp = pprint.PrettyPrinter()

def bad_practice(index):
    return np.random.randint(0,10,size=10)

def good_practice(index):
    return np.random.RandomState().randint(0,10,size=10)

p = Pool(5)

pp.pprint("Bad practice: ")
pp.pprint(p.map(bad_practice, range(5)))
pp.pprint("Good practice: ")
pp.pprint(p.map(good_practice, range(5)))

выход:

'Bad practice: '
[array([4, 2, 8, 0, 1, 1, 6, 1, 2, 9]),
 array([4, 2, 8, 0, 1, 1, 6, 1, 2, 9]),
 array([4, 2, 8, 0, 1, 1, 6, 1, 2, 9]),
 array([4, 2, 8, 0, 1, 1, 6, 1, 2, 9]),
 array([4, 2, 8, 0, 1, 1, 6, 1, 2, 9])]
'Good practice: '
[array([8, 9, 4, 5, 1, 0, 8, 1, 5, 4]),
 array([5, 1, 3, 3, 3, 0, 0, 1, 0, 8]),
 array([1, 9, 9, 9, 2, 9, 4, 3, 2, 1]),
 array([4, 3, 6, 2, 6, 1, 2, 9, 5, 2]),
 array([6, 3, 5, 9, 7, 1, 7, 4, 8, 5])]

В хорошей практике начальное число создается один раз для каждого потока, в то время как в плохой практике начальное число создается только один раз при импорте модуля numpy.random.