おいも貴婦人ブログ

生物系博士課程満期退学をしたAIエンジニアのブログ。

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);
}