2

私は最近、Python をいじくり回しており、多数の並列化パッケージを比較していると、シリアルからパラレルへのパフォーマンスの向上は、私の MacBook Pro (OS X 10.8.2) があります。

添付のプロットは、さまざまなタスクのタイミングをプロセス数 (並列または順次) の関数として比較しています。この例では、Python の組み込み ' multiprocessing ' パッケージを使用しています。

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)
4

1 に答える 1

1

上記の議論から、あなたは必要な情報を持っていると思いますが、他の人に利益をもたらす場合に備えて、事実を収集するための回答を追加しています (さらに、私は自分でそれを処理したかったのです)。(これについて最初に言及した @bamboon の功績によるものです。)

まず、MacBook には 4 つの物理コアを備えた CPU が搭載されていますが、チップの設計では、各コアのハードウェアが 2 つのスレッドを実行できるようになっています。これは「同時マルチスレッディング」(SMT) と呼ばれ、この場合、Intel のハイパースレッディング機能によって具現化されます。したがって、合計すると、8 つの「仮想コア」 (4 + 4 = 8) になります。

OS はすべての仮想コアを同じように扱うことに注意してください。つまり、物理コアによって提供される 2 つの SMT スレッドを区別しないため、sysctlクエリを実行すると 8 が返されます。Python も同じことを行います。

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

第二に、あなたが直面しているスピードアップの限界はよく知られた現象であり、問​​題に取り組むプロセッサを追加しても並列パフォーマンスが飽和し、改善されません。この効果は、アムダールの法則によって説明されます。これは、並列化できるコードの量とシリアル実行の量に応じて、複数のプロセッサに期待される速度向上に関する定量的な記述です。

通常、OS の詳細やコンピュータのアーキテクチャ (ハードウェア コアで SMT がどのように機能するかなど) など、さまざまな要因によって相対的な速度向上が制限されるため、コードをできるだけ多く並列化しても、パフォーマンスは向上しません。無期限に。シリアルのボトルネックがどこにあるかを理解するには、プログラムとその実行環境を非常に詳細に分析する必要があります。

この質問で議論のある良い例を見つけることができます。

これが役立つことを願っています。

于 2013-06-05T22:16:41.593 に答える