LoginSignup
1
3

More than 3 years have passed since last update.

python#Multiprocessing

Posted at

Multiprocessing について話ししましょう。

この間threadingのこと書きましたが、ではいつmultiprocessing使うのか、いつthreading を使うのかも前の記事で書いておきました。multiprocessing はあなたのプログラムをspeed up するために使われていて、じゃどうやってspeed up かどういうといろんなタスクを同時に走られるってことです。

threading↓
https://qiita.com/soup01/items/42810b541fc3adc09542

最初またdo_something()を見てみよう。

結果はもちろん1秒まって終わるでしょう。

import time

start=time.perf_counter()

def do_something():
    print('sleeping 1 s..')
    time.sleep(1)
    print('Wake up!')

do_something()
do_something()

finish=time.perf_counter()
print('finish on {}'.format(finish-start))

タイムチャートは以下のようになります。
image.png

じゃmultiprocessing を使うとどうになるの?
このmodule が複数のタスクを切り分けてほかのcpus に走らせる。ここでcpu bounds が出てきましたね。cpu bounds はたくさんの計算がありcpuに負荷かかるタスクであります。
タイムチャートはこうになります。
image.png

cpu bounds をthreading を使ってもこんなに早くならないのはそれらのthreading はまだ"一つのプロセス"だけ走ってます。なのでまた繰り返しになりますが、multiprocessing があなたのタスクを切り分けてマルチに走らせるんです。でも、結局もあなたのコンピュータのハードウェアの能力によりますけどねー
ここでmultiprocessingのモジュールを使ってみよう。

import time
import multiprocessing

start=time.perf_counter()

def do_something():
    print('sleeping 1 s..')
    time.sleep(1)
    print('Wake up!')

#create 2 process
p1=multiprocessing.Process(target=do_something)
p2=multiprocessing.Process(target=do_something)

p1.start()
p2.start()

finish=time.perf_counter()
print('finish on {}'.format(finish-start))

あれ?なんか0秒で終わっちゃったと気がするが…それはスクリプトの中にstart()し、sleeping()してる間に私たちのプログラムが走り続けて勝手に2つのプロセスが終わるまで終わるの時間の計算のラインを走ってしまった。
image.png

この現象をなくすためにjoin()を使えましょう。start ()のあとにjoin()。

import time
import multiprocessing

start=time.perf_counter()

def do_something():
    print('sleeping 1 s..')
    time.sleep(1)
    print('Wake up!')

#create 2 process
p1=multiprocessing.Process(target=do_something)
p2=multiprocessing.Process(target=do_something)

p1.start()
p2.start()

p1.join()
p2.join()

finish=time.perf_counter()
print('finish on {}'.format(finish-start))

ちゃんとできましたね!
image.png

なんとかできましたが、あまり効果見えませんね。だってただ1秒まってるだけだもん。今度10回を回ったらどうになるかな?
その前になんでjoin()はlooping の中に使えないでしょうか?もし全てのプロセスがstart()するまえに勝手にjoin()をしたら、もう普通の同期操作と同じになる。
あときになるのはわたしのパソコンは10coresがないのに10のタスク走るのはどういうこと?と思いませんか?それはモジュールが勝手にタスクの間にスイッチしてタスクを切り替えるからです。

import time
import multiprocessing

start=time.perf_counter()

def do_something():
    print('sleeping 1 s..')
    time.sleep(1)
    print('Wake up!')

processes=[]

for _ in range(10):
    p=multiprocessing.Process(target=do_something)
    p.start()
    processes.append(p)


for process in processes:
    process.join()


finish=time.perf_counter()
print('finish on {}'.format(finish-start))

結果はこうになります:
image.png

じゃsleepの時間をパラメータとして入れよう。

import time
import multiprocessing

start=time.perf_counter()

def do_something(s):
    print('sleeping {} s..'.format(s))
    time.sleep(s)
    print('Wake up!')

processes=[]

for _ in range(10):
    p=multiprocessing.Process(target=do_something,args=[1.5])
    p.start()
    processes.append(p)


for process in processes:
    process.join()


finish=time.perf_counter()
print('finish on {}'.format(finish-start))

結果はこうになりますね:
image.png

Python 3.2 からもっと簡単な方法でmultiprocessing をするの方法が誕生しました。じゃ最初からこの方法紹介すればいいじゃない?と思いかもしれませんが、やっはりまず手で従来あるモジュールを使って動きをある程度把握してからしたほうがいいかなって。submit() method がdo_something()というfunction を実行するタイミングをスケジュールしてfuture objectを返してくれるんです。なのでこのfuture object がfunction の実行状態などを管理するんです。終わったとか走ってるとか。

import time
import concurrent.futures

start=time.perf_counter()

def do_something(s):
    print('sleeping {} s..'.format(s))
    time.sleep(s)
    return 'Wake up!'


with concurrent.futures.ProcessPoolExecutor() as executor:
    f1=executor.submit(do_something,1)
    print(f1.result())

finish=time.perf_counter()
print('finish on {}'.format(finish-start))

では10回をLoopingしてみよう。

import time
import concurrent.futures

start=time.perf_counter()

def do_something(s):
    print('sleeping {} s..'.format(s))
    time.sleep(s)
    return 'Wake up!'


with concurrent.futures.ProcessPoolExecutor() as executor:

    results=[executor.submit(do_something,1) for _ in range(10)]

    for f in concurrent.futures.as_completed(results):
        print(f.result())


finish=time.perf_counter()
print('finish on {}'.format(finish-start))

今度はSleepの秒数それぞれ違うものをいれましょう。

import time
import concurrent.futures

start=time.perf_counter()

def do_something(s):
    print('sleeping {} s..'.format(s))
    time.sleep(s)
    return 'Wake up!{} s..'.format(s)


with concurrent.futures.ProcessPoolExecutor() as executor:
    seconds=[5,4,3,2,1]
    results=[executor.submit(do_something,second) for second in seconds]

    for f in concurrent.futures.as_completed(results):
        print(f.result())


finish=time.perf_counter()
print('finish on {}'.format(finish-start))

結果はこうになりますね:
image.png

最後はMap()を使ってやります。基本的にはmap functionはリスト数の分でdo_something()を走ります。subbmit()はfutures objectが戻り値でmapはResultしか返せない。

import time
import concurrent.futures

start=time.perf_counter()

def do_something(s):
    print('sleeping {} s..'.format(s))
    time.sleep(s)
    return 'Wake up!{} s..'.format(s)


with concurrent.futures.ProcessPoolExecutor() as executor:
    seconds=[5,4,3,2,1]
    results=executor.map(do_something,seconds)

    for result in results:
        print(result)

finish=time.perf_counter()
print('finish on {}'.format(finish-start))

ふーお疲れさま。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3