並列プログラミングスタイル

並列分散ソフトウェア

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2001-12-06
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/sie/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html

■正誤表

(1) Linda のタプルには変数は含まれない。

正: ("a string", 10.01, 17, 100)
誤: ("a string", 10.01, 17, x)

(1) read ストリームでは、in() ではなくて rd() を使う

正:rd("stream",index++,?element);
誤:in("stream",index++,?element);

■成績の付け方

次の3つを合算する。

■今日の重要な話

並列プログラムの作り方 資料

■並列と分散

◆逐次処理と並列分散処理

並列
速く解きたい
分散
離れていても心は1つ
逐次処理は非常に特殊。 共通の話

◆ハードウェア

分類 態度

◆ソフトウェアの記述

2つの言語が必要
計算言語
個々の活動を記述
協調言語
統一されたプログラムに組み立てるための「糊」

■協調作業のための基本パラダイム

-- 並列/分散プログラムのパタン。

◆並列プログラムを構築する手順

  1. パラダイムを選ぶ
  2. パラダイムに合った手法(Methods)で書く
  3. プログラム変換する 性能が十分でない場合には、他のパラダイムを使って書き直す

    ◆パラダイム1:結果並列法(Result Parallelism)

    並列処理の最終結果に焦点を当る。

    例: 家の建築:

    ◆パラダイム2:専門家並列法 (Specialist Parallelism)

    並列処理を行う作業者に焦点を当る。

    例: 家の建築:

    ◆パラダイム3:手順並列法 (Agenda Parallelism)

    並列処理の手順に焦点をあてる 例: 家の建築:

    ◆どのパラダイムを使うか

    問題にあったものを使う。

    実際の家の建築では、全部の方法が使われている。

    ◆結果並列法の使い方

    簡単。 結果のデータ構造を得るための依存関係が予めわかっている時に便利。

    うまく行く例:ベクトルの足し算: S[i] = A[i] + B[i]

    1. n 要素のベクトルを作る
    2. S[i] は、A[i] と B[i] を加えればわかる。
    出力データが不明なもの、結果が1つのもには適していない。テキスト処理、 OS、実時間LU分解(繰り返しがある)、

    ◆手順書並列法の使い方

    マスタ・ワーカ
    1. マスターが計算を始める
    2. 同一のワーカ・プロセスの集合を作る
    3. ワーカ、どの仕事でもやる。
    4. 仕事がなくなれば終わり
    うまく行く例: データベースの項目から、ある関数の値が最小のものを探す

    1. マスターは、袋(task bag)の中にデータ・レコードを全部入れる
    2. 各ワーカは、袋から取り出して(複雑な)関数の計算を行い、 結果をマスターに返す
    3. マスターは、ワーカから送られてきたものの最小のものを記録する。
    4. 全部が終われば終わり。

    ◆専門家並列法の使い方

    プロセスのネットワークが作られる

    ◆その他の並列パラダイム

    ■プログラミングの手法

    視点

    ◆手法1:メッセージパッシング(Message Passing)

    TSD (Thread Specific Data)

    ◆手法2:ライブデータ構造体(Live Data Structure)

    関数型言語

    ◆手法3:分散データ構造体(Distributed Data Structure)

    共有メモリのマルチスレッドは、TSD をのぞいてだいたいこれ。

    ◆N-体問題シミュレータ

    N個の物体の位置をq時間単位分計算する。

    ◆N-体問題:結果並列法

    位置の配列: M[i][j]、i=0..N-1, j=0..q

    M[][0] に最初の位置。

    position(i,j), 繰り返し j での 物体 i の位置を計算する。

    ◆N-体問題:手順並列法

    手順(ワーカの仕事):集合に含まれている全ての物体について、次の位置を 計算する。

    マスタで、N 個の物体を作る。

    ワーカを作る。ワーカの数は、N 個ではなくて、もっと少ない(CPU数と同じに する)。

    物体の位置を分散データ構造体(共有メモリ)に置く。

    ◆N-体問題:専門家並列法

    物体に対応したプロセスを作る。

    ◆並列プログラムをかく方法

    1. 問題に適したパラダイムわ選ぶ
    2. パラダイムに適した手法でプログラムを書く
    3. 必要なら効率のよい手法で書き直す
    (粒度の調整)

    ◆手法の相互変換

    アブストラクション
    データとプロセスの結び付きを切る
    スペシャリゼーション
    データとプロセスから分離して分散データ構造体におく
    クランピング
    通信を明示的に行うようにする
    デクランピング
    通信を暗黙的にに行うようにする
    問題1: ライブデータ構造体でプログラムを書いたらプロセス生成が多くて 性能が出ない。

    解決1:ライブデータ構造体を、受動的な構造体に書き換える。プロセスを複数 の構造体に対応させる。

    問題2:分散データ構造体で書いたプログラム(共有空間が必要)が、NORMA で うまく動かない。

    解決:メッセージ・パッシングに変換する。

    Carriero と Gelernter の主張:分散データ構造体がいい。

    Java では、スレッド、RMI、Javaspaces の順に導入された。

    ◆プログラミング言語とライブラリ

    1. ライブデータ構造体
    データフロー: Id Nouveau
    関数型言語: Multilisp, Sisal
    2. 分散データ構造体
    言語:HPF
    共有メモリとロック/セマフォ
    DSM: Orca, Munin, Midway, Linda
    3. メッセージパッシング
    特殊言語:CSP/Occam
    RPC:Ada
    ライブラリ:PVM, MPI
    モニタ:Concurrent Pascal, Modula

    モニタは、メッセージパッシングの仲間か分散データ構造体か。

    ■Linda

    協調言語(<−>計算言語)。

    タプルペースモデル(tupple space model)で分散データ構造体を支援。(メッ セージ・パッシングやライブデータ構造体的なプログラムも書ける。)

    ◆タプルスペース

    タプルの集合である。

    タプルは、型付きの値の並び。

    タプルの例:

    ("a string", 10.01, 17, 10)
    (0,1)

    2種類のタプル

    データタプル
    受動的
  4. ライブタプル(プロセスタプル)
  5. 全部が同時に実行している。 データタプルを読み書きする。 終了するとデータタプルに変化する。
  6. ◆Lindaの命令

    タプルスペースを操作する基本4命令
    out
    タプルをタプル空間内に生成する。(受動的なタプル)
    in
    タプルを取り去る
    rd (read)
    in と似ているが、タプルがタプルスペースに残る。
    eval
    out と似ているが、新たにプロセスが作られて、そのプロセスによりタ プルが評価される。(ライブタプル)
    predicate つき(デットロック対策で使う)
    inp
    in と同じだが、見つからなければ 0 を返す
    rdp
    rd と同じだが、見つからなければ 0 を返す

    out("a string", 10.01, 17, x)

    in("a string", ?f, ?i, y)

    「?」付のものは、formal。型が同じものとマッチする。 ついていないものは、actual。型と値が同じものとマッチする。

    eval("e", 7, exp(7))

    in(), out() で同期がとられる。

    ◆名前付の構造体(連想メモリ)

    スクリプト言語(Perl、awk)の連想配列と同じ。

    
    タプルの形式: (name,val)
    
    読込み:
    rd(name,?val)
    
    変更:
    in(name,?val)
    val = ... val ... ;
    out(name,val)
    

    ◆計数セマフォ

    P命令: in("sem")

    V命令: out("sem");

    同じタプルを out したら溜る。

    ◆Task Bag

    仕事を入れる: out("task",TaskDescription)

    仕事を取り出す: in("task",?NewTask)

    ◆並列DOループ

    
    
    逐次:
    
    for( i=0 ; i<N; i++ )
    {
        func(i,args);
    }
    
    並列:
    for( i=0 ; i<N; i++ )
    {
        eval ("loop-33", func(i,args) );
    }
    for( i=0 ; i<N; i++ )
    {
        in("loop-33", 1 );
    }
    
    func(i,args)
    {
       ...
       return( 1 );
    }
    
    
    

    ◆バリア同期

    全部のプロセスが到着してから次に進む。

    n プロセスのバリア

    初期化: out("barrier-37",n)

    各プロセス:
    in("barrier-37",?val)
    out("barrier-37",val-1)
    rd("barrier-37",0)
    

    ◆配列

    A[10][10];
    
    ("A",0,0,val00)
    ("A",0,1,val01)
    ("A",0,2,val02)
    ...
    ("A",9,9,val99)
    

    ◆NUMAでのタプルスペースの実現

    ローカルのメモリと遅い大域的な共有メモリ

    ◆ストリーム(inストリーム)

    1人が読むとデータが消える。
    ("stream",0,val0)
    ("stream",1,val1)
    ("stream",2,val2)
    ...
    
    ポインタ
    ("stream","head",0)
    ("stream","tail",0)
    
    ストリームに要素を追加:
    int index;
    in("stream","tail",?index);
    out("stream","tail",index+1);
    out("stream",index,new_element);
    
    ストリームから要素を取り出す:
    int index;
    in("stream","head",?index);
    out("stream","head",index+1);
    in("stream",index,?element);
    
    
    これは、複数source、複数sink。

    source、sinkが1つなら、head, tail をタプルスペースに置かなくてもよい。

    ◆ストリーム(readストリーム)

    1つのストリームを、複数の読み手でアクセスできる(マルチキャスト)。

    head の代わりに、局所変数でアクセスする。

    ストリームから要素を取り出す:
    
    int index=0 ;
    while( ... )
    {
       rd("stream",index++,?element);
    }
    

    ◆メッセージ・パッシングのプログラムの書き方

    問題: ランデブは、どうするか。 RPC はどうするか。

    ◆ライブ・データ構造体のプログラムの書き方

    ■まとめ

    並列プログラムの作り方
    ↑[もどる] ・[12月06日] ・[12月13日]
    Last updated: 2001/12/07 20:05:12
    Yasushi Shinjo / <yas@is.tsukuba.ac.jp>