Потоки для быстрого создания большого количества графиков - PullRequest
2 голосов
/ 07 ноября 2010

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

def do_chart(target="IMG_BACK", xlabel="xlabel", ylabel="ylabel", title="title",       ydata=pylab.arange(1961, 2031, 1)):
    global MYRAMDICT
    MYRAMDICT = {}
    print "here"
    for i in range(70):
        MYRAMDICT[i] = cStringIO.StringIO()
        xdata = pylab.arange(1961, 2031, 1)
        pylab.figure(num=None, figsize=(10.24, 5.12), dpi=1, facecolor='w', edgecolor='k')
        pylab.plot(xdata, ydata, linewidth=3.0)
        pylab.xlabel(xlabel); pylab.ylabel(ylabel); pylab.title(i)
        pylab.grid(True)
        pylab.savefig(MYRAMDICT[i], format='png')
        pylab.close()

Эта функция (пожалуйста, игнорируйте команды pylab, они приведены только для иллюстрации) создает словарь (MYTAMDICT), который я заполнил объектами cString, которые используются для хранения диаграмм в памяти.Эти диаграммы позже динамически представляются пользователю.

Может, кто-нибудь поможет мне использовать многопоточность, чтобы я мог использовать все свои ядра и заставить эту функцию работать быстрее?Или нацелить меня на идеи по его улучшению?

Ответы [ 2 ]

3 голосов
/ 08 ноября 2010

Для описания вам было бы гораздо лучше использовать многопроцессорность, чем многопоточность ... У вас проблема "смущающей параллельности", и нет ограничений на дисковый ввод-вывод (вы записываете в память). Конечно, большая передача назади далее между процессами будет дорого, но возвращать строку, представляющую .png, не должно быть слишком плохо ..

Это можно сделать довольно просто:

import multiprocessing
import cStringIO

import matplotlib.pyplot as plt
import numpy as np

import itertools

def main():
    """Generates 1000 random plots and saves them as .png's in RAM"""
    pool = multiprocessing.Pool()
    same_title = itertools.repeat('Plot %i')
    fig_files = pool.map(plot, itertools.izip(xrange(1000), same_title))

def plot(args):
    """Make a random plot"""
    # Unfortunately, pool.map (and imap) only support a single argument to
    # the function, so you'll have to unpack a tuple of arguments...
    i, titlestring = args

    outfile = cStringIO.StringIO()

    x = np.cumsum(np.random.random(100) - 0.5)

    fig = plt.figure()
    plt.plot(x)
    fig.savefig(outfile, format='png', bbox_inches='tight')
    plt.title(titlestring % i)
    plt.close()

    # cStringIO files aren't pickelable, so we'll return the string instead...
    outfile.seek(0)
    return outfile.read()

main()

Без использования многопроцессорной обработки, это занимает ~ 250 секунд на моей машине.При многопроцессорности (8 ядер) это занимает ~ 40 секунд.

Надеюсь, это немного поможет ...

2 голосов
/ 08 ноября 2010

Threading поможет вам тогда и только тогда, когда pylab освобождает gil во время выполнения.
Более того, pylib должен быть потокобезопасным, и ваш код должен использовать его в поточно-ориентированном режиме, и это не всегда так.

Тем не менее, если вы собираетесь использовать потоки, я думаю, что это классический случай очереди заданий; поэтому я бы использовал объект очереди , который достаточно хорош, чтобы позаботиться об этом шаблоне.

Вот пример, который я привел, просто вмешиваясь в ваш код и пример, приведенный в документации очереди. Я даже не проверил это полностью, так что в нем будут ошибки; это больше дать идею, чем что-либо еще.

# "Business" code
def do_chart(target="IMG_BACK", xlabel="xlabel", ylabel="ylabel", title="title",       ydata=pylab.arange(1961, 2031, 1)):
    global MYRAMDICT
    MYRAMDICT = {}
    print "here"
    for i in range(70):
      q.put(i)
    q.join()       # block until all tasks are done

def do_work(i):
    MYRAMDICT[i] = cStringIO.StringIO()
    xdata = pylab.arange(1961, 2031, 1)
    pylab.figure(num=None, figsize=(10.24, 5.12), dpi=1, facecolor='w', edgecolor='k')
    pylab.plot(xdata, ydata, linewidth=3.0)
    pylab.xlabel(xlabel); pylab.ylabel(ylabel); pylab.title(i)
    pylab.grid(True)
    pylab.savefig(MYRAMDICT[i], format='png')
    pylab.close()


# Handling the queue
def worker():
    while True:
        i = q.get()
        do_work(i)
        q.task_done()

q = Queue()
for i in range(num_worker_threads):
     t = Thread(target=worker)
     t.daemon = True
     t.start()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...