Python のマルチスレッド、マルチプロセスにはまってしまったのでまとめました。
まず、おさらいです。
- 1プロセスは複数のスレッドを持つことが出来る
- マルチプロセスのメモリは共有されない、マルチスレッドのメモリは共有される
- スレッド作成よりプロセス作成の方がコストがかかりオーバーヘッド
- など...
ここまでは、一般的な違いなのですが、
Python には GIL (グローバルインタプリタロック)というものがあり、あるスレッドが他のスレッドの実行を止めてしまう機能があります。このおかげで、複数スレッドやコルーチンを実行した際も、安全に変数の代入できるなどの利点があるのですが、一方で頑張ってコーディングしたのに速くならない寧ろ遅くなるというやっかい事が起こります。
1万番目の素数を求めるプログラム
import time
def run():
start = time.clock()
primes = []
number = 1
while True:
number += 1
if not is_prime(number):
continue
primes.append(number)
if len(primes) == 10000:
print("10000th prime is " + str(number))
break
print("[python] duration is " + str(time.clock() - start) + " sec")
def is_prime(number):
for n in range(2, int(number/2)):
if number % n == 0:
return False
return True
run()
CPU Xeon (Westmere E56xx/L56xx/X56xx)で計測したところ
実行は16.61秒かかりました
一方、マルチスレッドで書くと
import time
from threading import Thread
def run():
start = time.clock()
primes = {}
max = 120000
t0 = Thread(target=worker, args=(primes,2,max))
t0.start()
t1 = Thread(target=worker, args=(primes,3,max))
t1.start()
t2 = Thread(target=worker, args=(primes,4,max))
t2.start()
t3 = Thread(target=worker, args=(primes,5,max))
t3.start()
t0.join()
t1.join()
t2.join()
t3.join()
number = 0;
for n in range(2, max, 1):
if primes[n]: number += 1
if number == 10000:
print("10000th prime is " + str(n))
break
print("[mt.python] duration is " + str(time.clock() - start) + " sec")
def worker(box, start, end):
for n in range(start, end, 4):
box[n] = is_prime(n)
def is_prime(number):
for n in range(2, int(number/2)):
if number % n == 0:
return False
return True
run()
実行には22.97秒かかりました。
他方、マルチプロセスだと、
import time
from multiprocessing import Process, Array
def run():
start = time.time()
max = 120000
primes = Array('i', range(max))
p0 = Process(target=worker, args=(primes,2,max))
p0.start()
p1 = Process(target=worker, args=(primes,3,max))
p1.start()
p2 = Process(target=worker, args=(primes,4,max))
p2.start()
p3 = Process(target=worker, args=(primes,5,max))
p3.start()
p0.join()
p1.join()
p2.join()
p3.join()
number = 0;
for n in range(2, max, 1):
if primes[n]: number += 1
if number == 10000:
print("10000th prime is " + str(n))
break
print("[mp.python] duration is " + str(time.time() - start) + " sec")
def worker(box, start, end):
for n in range(start, end, 4):
box[n] = is_prime(n)
def is_prime(number):
for n in range(2, int(number/2)):
if number % n == 0:
return False
return True
run()
実行には10.96秒かかりました。
実行速度として、
マルチプロセス > 普通 > マルチスレッド
となりました。
なぜ、実行が遅くなるわ、コードも見にくくなるわで、利点が全く感じられないマルチスレッドなのですが、いったい何のためにあるのでしょうか・・・?
もう一つ注意点として、マルチプロセスの場合、時間の計測にtime.time()を使用していますが、これには理由があります。time.clock()は自身のプロセスが実行されていた時間しか計測できず、マルチプロセスの場合は、うまく経過時間を測ることができなくなるためです。そこで精度は劣りますが、time.time()を使用しています。