Попытка внести некоторую оптимизацию для распараллеливания в модуле pystruct и в дискуссиях, пытающихся объяснить мое мышление о том, почему я хотел бы создать пулы как можно раньше, и держать их как можно дольше, повторно используя их, я понял, что знаю, что он работает лучше всего, но я не знаю, почему.
Я знаю, что претензия в системах * nix заключается в том, что подпроцесс рабочего пула копируется при записи из всех глобалов в родительском процессе. Это, безусловно, имеет место в целом, но я думаю, что следует добавить оговорку, что, когда один из этих глобалов является особенно плотной структурой данных, такой как numpy или scipy matrix, кажется, что любые ссылки, скопированные в рабочий, на самом деле довольно даже если весь объект не копируется, и поэтому появление новых пулов в конце выполнения может вызвать проблемы с памятью. Я нашел, что лучшая практика заключается в том, чтобы порождать пул как можно раньше, чтобы любые структуры данных были небольшими.
Я знаю это некоторое время и разрабатывал его в приложениях на работе, но лучшее объяснение, которое я получил, - это то, что я написал в этом разделе:
https://github.com/pystruct/pystruct/pull/129#issuecomment-68898032
Глядя на python script ниже, по существу, вы ожидаете, что свободная память в созданном пуле шаге в первом запуске и шаг, созданный матрицей во втором, будут в основном равными, как и в завершающих вызовах окончательного пула. Но они никогда не бывают, всегда (если, конечно, что-то еще происходит на машине), больше свободной памяти при создании пула в первую очередь. Этот эффект увеличивается со сложностью (и размером) структур данных в глобальном пространстве имен во время создания пула (я думаю). У кого-нибудь есть хорошее объяснение этому?
Я сделал эту маленькую картинку с контуром bash и R script также ниже, чтобы проиллюстрировать, показывая полную свободную память после создания пула и матрицы в зависимости от порядка:
pool_memory_test.py:
import numpy as np
import multiprocessing as mp
import logging
def memory():
"""
Get node total memory and memory usage
"""
with open('/proc/meminfo', 'r') as mem:
ret = {}
tmp = 0
for i in mem:
sline = i.split()
if str(sline[0]) == 'MemTotal:':
ret['total'] = int(sline[1])
elif str(sline[0]) in ('MemFree:', 'Buffers:', 'Cached:'):
tmp += int(sline[1])
ret['free'] = tmp
ret['used'] = int(ret['total']) - int(ret['free'])
return ret
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--pool_first', action='store_true')
parser.add_argument('--call_map', action='store_true')
args = parser.parse_args()
if args.pool_first:
logging.debug('start:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
p = mp.Pool()
logging.debug('pool created:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
biggish_matrix = np.ones((50000,5000))
logging.debug('matrix created:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
print memory()['free']
else:
logging.debug('start:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
biggish_matrix = np.ones((50000,5000))
logging.debug('matrix created:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
p = mp.Pool()
logging.debug('pool created:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
print memory()['free']
if args.call_map:
row_sums = p.map(sum, biggish_matrix)
logging.debug('sum mapped:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
p.terminate()
p.join()
logging.debug('pool terminated:\n\t {}\n'.format(' '.join(['{}: {}'.format(k,v)
for k,v in memory().items()])))
pool_memory_test.sh
#! /bin/bash
rm pool_first_obs.txt > /dev/null 2>&1;
rm matrix_first_obs.txt > /dev/null 2>&1;
for ((n=0;n<100;n++)); do
python pool_memory_test.py --pool_first >> pool_first_obs.txt;
python pool_memory_test.py >> matrix_first_obs.txt;
done
pool_memory_test_plot.R:
library(ggplot2)
library(reshape2)
pool_first = as.numeric(readLines('pool_first_obs.txt'))
matrix_first = as.numeric(readLines('matrix_first_obs.txt'))
df = data.frame(i=seq(1,100), pool_first, matrix_first)
ggplot(data=melt(df, id.vars='i'), aes(x=i, y=value, color=variable)) +
geom_point() + geom_smooth() + xlab('iteration') +
ylab('free memory') + ggsave('multiprocessing_pool_memory.png')
РЕДАКТИРОВАТЬ: исправление небольшой ошибки в script, вызванное чрезмерным обнаружением/заменой и повторным запуском
EDIT2: "-0" нарезка? Вы можете сделать это?:)
EDIT3: лучший питон script, bash петли и визуализация, теперь сделайте это отверстие для кроликов на данный момент:)