Pythonのmultiprocessingを使って、行列の積を計算しよう。
multiprocessingの使い方を学ぶために、行列の積を計算しました。結果は、絶望的に遅いです。そもそもプログラムに問題があるかも知れません。60x60の行列の積でも数秒かかってしまいます。
以下、コードを部分的に説明していきます。
必要なモジュールのインポート
#!/usr/bin/env python3 from multiprocessing import Process, cpu_count,current_process, Array
ここで必要なものをインポートします。
- Process:プロセスの生成に使う。
- cpu_count:使用できるCPUの上限を返す関数。
- current_process:プロセス内で、現プロセスの番号を返す関数。
- Array:共有メモリ上の配列を定義するための関数。
配列の初期化、共有メモリ上の配列の確保
size = 16 A = [[i+j*size for i in range(size)] for j in range(size)] B = [[1 for i in range(size)] for j in range(size)] C = [0 for j in range(size*size)] sheardC = Array('f',C) """ A = [[0,1,2, ... ,9,10,11], [12,13,14, ... ,21,22,23], ... [132,133,134, ... ,141,142,143]] B = [[1,1,1, ... ,1,1,1], [1,1,1, ... ,1,1,1], ... [1,1,1, ... ,1,1,1]] C = [ 0,0,0, ... 0,0,0] # size*size """
Array(type,配列)はC言語ライクな配列を生成するための関数です。これにより、生成された配列はプロセス間で共有されます。
各processで実行される関数を定義する。
number_of_cpus = cpu_count() def calc_mat(mat1,mat2,sheardmat): cpuindex = int(current_process().name.split("-")[1]) ### 実行されているプロセスを取得(ex:current_process()はProcess-1と出力するので、1だけを抽出する) row = len(mat1)/number_of_cpus ### この場合、行をcpu数で割ることで、各プロセスが担当するデータを定義する。 start = int(row*(cpuindex-1)) end = int(row*cpuindex) ### 24x24の行列の計算に、12processを使用した場合、process-1が1,2行目を担当する。 for i in range(start,end): for j in range(size): for k in range(size): sheardmat[i*size+j] += mat1[i][k]*mat2[k][j]
number_of_cpus:使用できるCPUを確保
プロセスの生成、実行、終了。
calc_list = [] for i in range(number_of_cpus): calc = Process(target=calc_mat,args=(A,B,sheardC)) ## Process(target=関数、args=関数の引数) calc.start() calc_list.append(calc) [icalc.join() for icalc in calc_list] """ for i,data in enumerate(sheardC.get_obj()): print("%6.1lf" % (data),end='') if (i+1)%size==0: print() """
各プロセスの生成、スタート実行する。生成した各プロセスは、各プロセスでjoinを実行しなければならい。joinをしない場合、親プロセスを終了しても子プロセスが終了せず、ソンビ化してしまう。
感想
遅すぎる...。多分コードに問題があるので、後々、修正します。
- 問題点:各プロセスの実行率が40%くらいで頭打ちになる。
おまけ
OpenMPを使った場合、
#include <stdio.h> #include <omp.h> #define SIZE 1000 int main(void){ int i,j,k; double A[SIZE][SIZE],B[SIZE][SIZE],C[SIZE][SIZE]; for(i=0;i<SIZE;i++){ for(j=0;j<SIZE;j++){ A[i][j]=1; B[i][j]=1; C[i][j]=0; } } #pragma omp parallel num_threads(12) { #pragma omp for for(i=0;i<SIZE;i++){ printf("%d->%d\n",i,omp_get_thread_num()); for(j=0;j<SIZE;j++){ for(k=0;k<SIZE;k++){ C[i][j]+=A[i][k]*B[k][j]; } } } } //対角要素のみを出力 for(i=0;i<SIZE;i++){ printf("%7.1lf",C[i][i]); } return(0); }