В какой ситуации нам нужно использовать `multiprocessing.Pool.imap_unordered`?

Порядок получения результатов из возвращаемого итератора imap_unordered является произвольным, и он не работает быстрее, чем imap (который я проверяю со следующим кодом), так зачем использовать этот метод?

from multiprocessing import Pool
import time

def square(i):
    time.sleep(0.01)
    return i ** 2

p = Pool(4)
nums = range(50)

start = time.time()
print 'Using imap'
for i in p.imap(square, nums):
    pass
print 'Time elapsed: %s' % (time.time() - start)

start = time.time()
print 'Using imap_unordered'
for i in p.imap_unordered(square, nums):
    pass
print 'Time elapsed: %s' % (time.time() - start)

Ответ 1

Использование pool.imap_unordered вместо pool.imap не окажет большого влияния на общее время работы вашего кода. Это может быть немного быстрее, но не слишком.

Однако это может сделать интервал между значениями, доступными на вашей итерации, более четкими. То есть, если у вас есть операции, которые могут занимать очень разные промежутки времени (а не последовательные 0.01 секунды, которые вы использовали в вашем примере), imap_unordered может сгладить ситуацию, получив более быстрые расчетные значения перед более медленными вычислениями значения. Регулярный imap задерживает получение более быстрых, пока не будут вычислены более медленные из них (но это не задерживает рабочие процессы, переходящие на большее количество вычислений, просто время, чтобы вы их увидели).

Попробуйте спящий режим работы i*0.1 секунд, перетасовывание списка ввода и печать i в ваших циклах. Вы сможете увидеть разницу между двумя версиями imap. Здесь моя версия (функция main и шаблон шаблона if __name__ == '__main__' должна была корректно работать в Windows):

from multiprocessing import Pool
import time
import random

def work(i):
    time.sleep(0.1*i)
    return i

def main():
    p = Pool(4)
    nums = range(50)
    random.shuffle(nums)

    start = time.time()
    print 'Using imap'
    for i in p.imap(work, nums):
        print i
    print 'Time elapsed: %s' % (time.time() - start)

    start = time.time()
    print 'Using imap_unordered'
    for i in p.imap_unordered(work, nums):
        print i
    print 'Time elapsed: %s' % (time.time() - start)

if __name__ == "__main__":
    main()

Версия imap будет иметь длинные паузы, в то время как значения, такие как 49, обрабатываются (беря 4,9 секунды), тогда он будет летать над множеством других значений (которые были рассчитаны другими процессами, пока мы ждали 49 обрабатываться). Напротив, цикл imap_unordered обычно не останавливается почти так же долго. Он будет иметь более частые, но более короткие паузы, и его результат будет более плавным.

Ответ 2

imap_unordered также, похоже, использует меньше памяти во времени, чем imap. По крайней мере, то, что я испытал с помощью итератора над миллионами вещей.