LoginSignup
10
17

More than 5 years have passed since last update.

Python マルチスレッド/マルチプロセス

Posted at

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()を使用しています。

PrimeNumberリポジトリ

10
17
1

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
10
17