繁体   English   中英

为什么我的并行性能达到顶峰?

[英]Why does my parallel performance top out?

我最近一直在使用Python,在比较许多并行化程序包时,我注意到从串行到并行的性能提升似乎达到了6个进程的顶峰,而不是MacBook Pro(OS)的8个内核X 10.8.2)拥有。

随附的图根据进程数(并行或顺序)比较不同任务的时间安排。 这个例子使用的是python内置的' multiprocessing '包'Memory'vs.'Processor'是指内存密集型(只是分配大数组)与计算密集型(许多操作)的函数。

什么原因导致8进程以下的中断?

在此处输入图片说明

(对于每个进程数,“时间”平均超过100个函数调用)

import multiprocessing as mp
import time
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt

iters       = 100
mem_num     = 1000
pro_num     = 20000
max_procs   = 10

line_width  = 2.0
legend_size = 10
fig_name    = 'timing.pdf'

def UseMemory(num):
    test1 = np.zeros([num,num])
    test2 = np.arange(num*num)
    test3 = np.array(test2).reshape([num, num])
    test4 = np.empty(num, dtype=object)
    return 

def UseProcessor(num):
    test1 = np.arange(num)
    test1 = np.cos(test1)
    test1 = np.sqrt(np.fabs(test1))
    test2 = np.zeros(num)
    for i in range(num): test2[i] = test1[i]
    return np.std(test2)

def MemJob(its): 
    for ii in range(its): UseMemory(mem_num)

def ProJob(its): 
    for ii in range(iters): UseProcessor(pro_num)


if __name__ == "__main__":

    print '\nParTest\n'    

    proc_range = np.arange(1,max_procs+1,step=1)

    test_times = np.zeros([len(proc_range),2,2])                 # test_times[num_procs][0-ser,1-par][0-mem,1-pro]
    tot_times  = np.zeros([len(proc_range),2  ])                 #  tot_times[num_procs][0-ser,1-par]

    print ' Testing %2d numbers of processors between [%d,%d]' % (len(proc_range), 1, max_procs)
    print ' Iterations %d, Memory Length %d, Processor Length %d' % (iters, mem_num, pro_num)

    for it in range(len(proc_range)):
        procs = proc_range[it]
        job_arg = procs*[iters]
        print '\n - %2d, Processes = %3d' % (it, procs)

        # --- Test Serial ---
        print ' - - Serial'
        # Test Memory
        all_start = time.time()
        start = time.time()
        map(MemJob, [procs*iters])
        ser_mem_time = time.time() - start

        # Test Processor
        start = time.time()
        map(ProJob, job_arg)
        ser_pro_time = time.time() - start

        ser_time = time.time() - all_start

        # --- Test Parallel : multiprocessing ---
        print ' - - Parallel: multiprocessing'
        pool = mp.Pool(processes=procs)
        # Test Memory
        all_start = time.time()
        start = time.time()
        pool.map(MemJob, job_arg)
        par_mem_time = time.time() - start

        # Test Processor
        start = time.time()
        pool.map(ProJob, job_arg)
        par_pro_time = time.time() - start

        par_time = time.time() - all_start

        print ' - - Collecting'
        ser_mem_time /= procs
        ser_pro_time /= procs
        par_mem_time /= procs
        par_pro_time /= procs
        ser_time     /= procs
        par_time     /= procs

        test_times[it][0] = [ ser_mem_time, ser_pro_time ]
        test_times[it][1] = [ par_mem_time, par_pro_time ]
        tot_times[it]     = [ ser_time    , par_time     ]



    fig = plt.figure()
    ax  = fig.add_subplot(111)
    ax.set_xlabel('Number of Processes')
    ax.set_ylabel('Time [s]')
    ax.xaxis.grid(True)
    ax.yaxis.grid(True)
    lines = []
    names = []

    l1, = ax.plot(proc_range, test_times[:,0,0], linewidth=line_width)
    lines.append(l1)
    names.append('Serial Memory')
    l1, = ax.plot(proc_range, test_times[:,0,1], linewidth=line_width)
    lines.append(l1)
    names.append('Serial Processor')
    l1, = ax.plot(proc_range, tot_times[:,0], linewidth=line_width)
    lines.append(l1)
    names.append('Serial')

    l1, = ax.plot(proc_range, test_times[:,1,0], linewidth=line_width)
    lines.append(l1)
    names.append('Parallel Memory')
    l1, = ax.plot(proc_range, test_times[:,1,1], linewidth=line_width)
    lines.append(l1)
    names.append('Parallel Processor')
    l1, = ax.plot(proc_range, tot_times[:,1], linewidth=line_width)
    lines.append(l1)
    names.append('Parallel')

    plt.legend(lines, names, ncol=2, prop={'size':legend_size}, fancybox=True, shadow=True, bbox_to_anchor=(1.10, 1.10))
    fig.savefig(fig_name,dpi=fig.get_dpi())
    print ' - Saved to ', fig_name
    plt.show(block=True)

从上面的讨论中,我认为您拥有所需的信息,但是我添加了一个答案以收集事实,以防其他人受益(此外,我还想自己动手做)。 (应归功于@bamboon,他首先提到了其中一些内容。)

首先,您的MacBook有一个带有四个物理核心的CPU,但是芯片的设计使得每个核心的硬件都能够运行两个线程。 这称为“同时多线程”(SMT),在这种情况下,这是由英特尔的超线程功能实现的。 因此,总共拥有8个“虚拟核心”(4 + 4 = 8)。

请注意,操作系统将所有虚拟内核视为相同,即,它不会区分物理内核提供的两个SMT线程,这就是为什么sysctl在查询时返回8的原因。 Python将做同样的事情:

>>> import multiprocessing
>>> multiprocessing.cpu_count()
8

其次,您遇到的加速限制是一个众所周知的现象,在这种现象中,并行性能达到饱和,并且无法通过添加更多处理此问题的处理器而提高。 阿姆达尔定律描述了这种影响,这是一个量化的陈述,它取决于可以并行化多少代码以及串行运行多少代码,从而期望从多个处理器获得多少加速。

通常,很多因素都会限制相对速度,包括操作系统甚至计算机体系结构的详细信息(例如,SMT在硬件内核中的工作方式),因此即使您尽可能多地并行执行代码,性能也不会扩展无限期。 了解串行瓶颈的位置可能需要对程序及其运行环境进行非常详细的分析。

这个问题上,您可以找到一个很好的例子进行讨论。

我希望这有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM