2016-02-13 3 views
1

У меня проблемы с многопроцессорностью в Matplotlib с версии 1.5. Шрифты случайным образом прыгают вокруг своего исходного положения. Пример здесь: enter image description hereMatplotlib многопроцессорные шрифты с использованием savefig

Простой пример, чтобы воспроизвести эту ошибку здесь:

import multiprocessing 
import matplotlib.pyplot as plt 

fig = plt.figure() 

def plot(i): 
    fig = plt.gcf() 
    plt.plot([],[]) 
    fig.savefig('%d.png' % i) 

plot(0) 
pool = multiprocessing.Pool(4) 
pool.map(plot, range(10)) 

если порядок многопроцессорной и простого черчения сторнируется

pool = multiprocessing.Pool(4) 
plot(0) 
pool.map(plot, range(10)) 

, то это работает, но это обходной путь бесполезен для моих целей.

спасибо.

+2

Вы считаете, что одновременный доступ к логике построения является безопасным. Я был бы очень удивлен, если это на самом деле. – cel

+0

Наверное, это не так, но он работал с более старым matplotlib, и он иногда работает с новым. Мне просто нужен способ создания и сохранения графиков путем многопроцессорности. – Tomas

+0

Рисованные сюжеты очень похожи на картины в реальной жизни. Наличие нескольких художников часто не улучшает результат. :) – cel

ответ

2

Я недавно столкнулся с этой проблемой при тестировании методов параллельного построения большого количества графиков. Хотя я не нашел решение с использованием модуля многопроцессорности, я обнаружил, что не вижу одинаковых ошибок с использованием пакета Parallel Python (http://www.parallelpython.com/). Кажется, что на 50% медленнее, чем модуль многопроцессорности в моих ранних тестах, но все же значительно ускорился по сравнению с серийным графиком. Это также немного затруднительно по отношению к импорту модулей, поэтому я в конечном итоге предпочел бы найти решение с использованием многопроцессорности, но пока это проворное обходное решение (по крайней мере для меня). Тем не менее, я довольно новичок в параллельной обработке, поэтому могут быть некоторые нюансы двух подходов, которые мне здесь не хватает.

############################################################################### 
import os 
import sys 
import time 
#import numpy as np 
import numpy # Importing with 'as' doesn't work with Parallel Python 
#import matplotlib.pyplot as plt 
import matplotlib.pyplot # Importing with 'as' doesn't work with Parallel Python 
import pp 
import multiprocessing as mp 
############################################################################### 
path1='./Test_PP' 
path2='./Test_MP' 
nplots=100 
############################################################################### 
def plotrandom(plotid,N,path): 
    numpy.random.seed() # Required for multiprocessing module but not Parallel Python... 
    x=numpy.random.randn(N) 
    y=x**2 
    matplotlib.pyplot.scatter(x,y) 
    matplotlib.pyplot.savefig(os.path.join(path,'test_%d.png'%(plotid)),dpi=150) 
    matplotlib.pyplot.close('all') 
############################################################################## # 
# Parallel Python implementation 
tstart_1=time.time() 
if not os.path.exists(path1): 
    os.makedirs(path1) 

ppservers =() 

if len(sys.argv) > 1: 
    ncpus = int(sys.argv[1]) 
    job_server = pp.Server(ncpus, ppservers=ppservers) 
else: 
    job_server = pp.Server(ppservers=ppservers) 

print "Starting Parallel Python v2 with", job_server.get_ncpus(), "workers" 

jobs = [(input_i, job_server.submit(plotrandom,(input_i,10,path1),(),("numpy","matplotlib.pyplot"))) for input_i in range(nplots)] 

for input_i, job in jobs: 
    job() 

tend_1=time.time() 
t1=tend_1-tstart_1 
print 'Parallel Python = %0.5f sec'%(t1) 
job_server.print_stats() 
############################################################################## # 
# Multiprocessing implementation 
tstart_2=time.time() 
if not os.path.exists(path2): 
    os.makedirs(path2) 

if len(sys.argv) > 1: 
    ncpus = int(sys.argv[1]) 
else: 
    ncpus = mp.cpu_count() 

print "Starting multiprocessing v2 with %d workers"%(ncpus) 

pool = mp.Pool(processes=ncpus) 
jobs = [pool.apply_async(plotrandom, args=(i,10,path2)) for i in range(nplots)] 
results = [r.get() for r in jobs] # This line actually runs the jobs 
pool.close() 
pool.join() 

tend_2=time.time() 
t2=tend_2-tstart_2 
print 'Multiprocessing = %0.5f sec'%(t2) 
############################################################################### 
0

Я нашел решение. Основной причиной проблем является кэширование шрифтов в словаре _fontd в /matplotlib/backends/backend_agg.py

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

Если кто-нибудь знает более элегантное решение, которое не требует модификации файлов matplotlib, просто дайте мне знать.