2013-04-06 26 views
2

最近我一直在玩Python,並且在比較大量的並行化軟件包時,我注意到從串行到並行的性能提高似乎在6個進程而不是8個 - 我的MacBook Pro核心數量(OS X 10.8.2)。爲什麼我的並行性能最高?

附圖將不同任務的時間作爲進程數量(並行或順序)的函數進行比較。這個例子是使用python built-int'multiprocessing'軟件包'Memory'與'Processor'是指內存密集型(僅分配大型數組)與計算密集型(許多操作)函數。

8進程以下的頂端出現的原因是什麼?

enter image description here

(「時間的在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) 
+0

你忘了顯示代碼:) – 2013-04-06 21:40:29

+3

@LevLevitsky但顯示代碼就像舉起你的衣服! ... 添加。 – DilithiumMatrix 2013-04-06 21:45:06

+5

您的筆記本電腦沒有8個內核,它有4個內核+4個SMT線程,加速可能以某種方式加起來/上到/在〜6。看到這裏有一個很好的鏈接相關的http://superuser.com/questions/279629/how-much-speedup-does-a-hyper-thread-give-in-theory/279803#279803 – inf 2013-04-06 21:47:39

回答

1

從上面的討論,我認爲你有你需要的信息,但我加入一個收集事實的答案,如果它有利於他人(加上我想通過自己工作)。 (應該歸功於@bamboon,他首先提到了這一點)

首先,您的MacBook有一個帶有四個物理內核的CPU,但是該芯片的設計使得每個內核的硬件都能夠運行兩個線程。這被稱爲「同時多線程」(SMT),在這種情況下由英特爾的hyperthreading功能體現。所以你們有8個「虛擬核心」(4 + 4 = 8)。

請注意,OS將所有虛擬內核視爲相同,即它不區分物理內核提供的兩個SMT線程,這就是爲什麼sysctl在查詢時返回8。 Python會做同樣的事情:

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

其次,您遇到的加速限度是在並行性能飽和和不加處理問題更多的處理器的改進衆所周知的現象。這種效果由Amdahl's Law描述,這是一個關於多個處理器期望加速多少的定量說明,具體取決於可以並行多少代碼以及多少個串行運行。

通常,許多因素限制相對加速,包括操作系統甚至計算機架構的細節(例如,SMT如何在硬件核心中工作),以便即使盡可能多地並行化代碼,您的性能不會無限縮放。瞭解系列瓶頸的位置可能需要對程序及其運行環境進行非常詳細的分析。

你可以在this question的討論中找到一個很好的例子。

我希望這會有所幫助。

相關問題