线程快速创建大量图表

时间:2010-11-07 20:12:05

标签: python multithreading optimization matplotlib

我一直在努力寻找使下面一段代码执行得更快的方法:

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对象,用于在memmory上存储图表。这些图表稍后会动态呈现给用户。

有人请帮助我使用线程,以便我可以使用所有核心并使此功能更快地执行吗?或者指出改进它的想法?

2 个答案:

答案 0 :(得分:3)

对于描述,使用多处理比使用线程要好得多......你有一个“令人尴尬的并行”问题,并且没有磁盘IO约束(你正在写入内存)当然,传回大量的东西这些进程之间的代价很高,但是返回一个表示.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秒。

希望有所帮助...

答案 1 :(得分:2)

当且仅当pylab在执行时释放gil时,线程将帮助您 此外,pylib必须是线程安全的,并且您的代码必须以线程安全的方式使用它,并且情况可能并非总是如此。

那就是说,如果你打算使用线程,我认为这是一个典型的作业队列案例;因此,我会使用queue object,这足以照顾这种模式。

这是我通过干预您的代码和队列文档中给出的示例而提出的示例。我甚至没有仔细检查过,所以它会有虫子;提出一个想法比什么都重要。

# "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()