LoginSignup
60
56

More than 5 years have passed since last update.

スクリプト言語のマルチプロセス処理まとめ

Last updated at Posted at 2015-08-25

概要

並列・並行処理、関数型、リアクティブなど、スケーラビリティを意識したプログラミングや言語が最近注目を集めている印象。そこでWeb開発でよく使われれるスクリプト言語3種を使ったマルチプロセス処理の基本的な書き方についてまとめ

環境

言語

  • Ruby 2.2
  • Python 3.1
  • PHP 5.6

実行環境

  • Vagrant
  • CentOS6.4
  • memory 1024MB
  • CPU core 4
Vagrantfile
config.vm.provider :virtualbox do |vb|
    vb.customize ["modifyvm", :id, "--memory", "1024", "--cpus", "4", "--ioapic", "on"]
  end

CPU確認コマンド

top -d1
# 1を押して全CPUを表示

Ruby

サンプルコード

require "digest/md5"
require "securerandom"
require 'benchmark'

pcount = 4

def single(pcount)
    (pcount - 1).times do
        arr = []
        100000.times do
            arr << Digest::MD5.digest(SecureRandom.uuid)
        end
    end
end

def multi(pcount)
    pids = []
    (pcount - 1).times do
        # プロセス生成
        pids << fork do
            arr = []
            100000.times do
                arr << Digest::MD5.digest(SecureRandom.uuid)
            end
        end
    end

    Process.waitall

end

single_time = Benchmark.realtime do
    single(pcount)
end
print single_time.to_s() + "\n"

multi_time = Benchmark.realtime do
    multi(pcount)
end
print multi_time.to_s() + "\n"

CPU稼働率

シングルプロセス

Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

マルチプロセス

Cpu0  : 99.0%us,  1.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  : 99.0%us,  1.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  : 99.0%us,  1.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

結果

  • シングルプロセス:4.066100899999924
  • マルチプロセス:1.2609145170000602

Python

サンプルコード

import multiprocessing as mp
import hashlib
import uuid
import time
import os

def create_hash(res=[]):
    print('created: ' + str(os.getpid()))
    for _ in range(100000):
        res.append(hashlib.md5(str(uuid.uuid4()).encode('utf8')).hexdigest())
    return res

def single(pcount):
    start_time = time.time()
    print('parent: ' + str(os.getpid()))
    res = []
    for _ in range(pcount):
        res = create_hash(res)
    end_time = time.time()
    print('all finished : ' + str(end_time - start_time))

def multi(pcount):
    start_time = time.time()
    print('parent: ' + str(os.getpid()))
    processes = []
    for _ in range(pcount):
        processes.append(mp.Process(target=create_hash, args=()))

    for process in processes:
        process.start()

    for process in processes:
        process.join()

    end_time = time.time()
    print('all finished : ' + str(end_time - start_time))

if __name__ == '__main__':
    pcount = 4
    single(pcount)
    multi(pcount)

CPU稼働率

シンプルプロセス

Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

マルチプロセス

Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  : 99.0%us,  1.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

結果

  • シングルプロセス:11.234153509140015
  • マルチプロセス:3.374380588531494

PHP

サンプルコード

// 親プロセス + 子プロセス = 4プロセス
$pcount = 3;

function single($pcount) {
    $time_start = microtime(true);
    $res = [];
    echo 'parent : '.getmypid()."\n";

    foreach(range(0, $pcount - 1) as $i) {
        echo 'created : '.getmypid()."\n";

        foreach(range(0, 100000) as $ii) {
            $res[] = md5(uniqid(mt_rand(), true));
        }
    }

    $time_end = microtime(true);
    $time = $time_end - $time_start;

    echo "all finished : $time\n";
}

function multi($pcount) {
    $processes = [];
    $time_start = microtime(true);
    echo 'parent : '.getmypid()."\n";

    foreach(range(0, $pcount - 1) as $p) {
        // ここで親プロセスと子プロセスに分かれる。
        // 親プロセスは子プロセスの生成に成功したら、そのプロセスのPID_IDを
        // 失敗したら-1を取得
        // 子プロセスは0を取得
        $pid = pcntl_fork();

        // 子プロセスの生成に失敗
        if ($pid === -1) {
            echo 'Failed process fork';
            exit;
        }

        // 子プロセスの処理
        if ($pid === 0) {
            $res = [];

            foreach(range(0, 100000) as $i) {
                $res[] = md5(uniqid(mt_rand(), true));
            }
            echo 'created : '.getmypid()."\n";
            exit;
        }

        // 親プロセスの処理
        $processes[] = $pid;
    }

    foreach($processes as $process) {
        pcntl_waitpid($process, $status);
    }

    $time_end = microtime(true);
    $time = $time_end - $time_start;

    echo "all finished : $time\n";
}

single($pcount);
multi($pcount);

CPU稼働率

シングルプロセス

Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  1.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  1.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

マルチプロセス

Cpu0  :  1.0%us,  0.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  : 99.0%us,  0.0%sy,  0.0%ni,  1.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  : 97.0%us,  1.0%sy,  0.0%ni,  2.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  : 96.1%us,  1.0%sy,  0.0%ni,  2.0%id,  0.0%wa,  0.0%hi,  1.0%si,  0.0%st

結果

  • シングルプロセス:3.6972289085388
  • マルチプロセス:1.7085299491882

まとめ

  • スクリプト言語でもマルチプロセスで処理する方法はある。
  • OSがマルチコアでなければマルチプロセス処理にしても高速化されなかった(あたりまえ)。
  • 各CPUコア数の環境下で、最も処理が高速化されるのはCPUコア数と同じプロセス数の時で、それ以上プロセスを増やしても高速化されない(むしろ遅くなる)。
  • マルチプロセスではプロセス毎にメモリが割り当てられる。
  • Elixir と Reactive System に関する考察並行処理に有効なメッセージパッシングにあるようなアーキテクチャを意識した実装をするには、Rubyはprocess、Pythonはgeventを使うのがよさそうなので別途調査。PHPはpthreadsってモジュールがあるが、もともとPHPはシングルスレッドが前提になっているので無理やり動かすくらいなら他の言語を選択したほうがよさそう。
60
56
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
60
56