並列プログラムの作り方メモ
「並列プログラムの作り方」を西那須野町図書館経由宇都宮市立図書館より借りました。返す前にメモを残そうと思います。(その後、結局手元に置きたくなって購入しました。)
並列プログラムの作り方

How to write Parallel Programs - A First Course
並列プログラムの作り方 N. Carriero/D.Gelernter 共立出版
読んだことないけど気になる本
おすすめがあったら教えて下さいな。
- 並列計算法入門
- Linuxで並列処理をしよう―SCoreで作るスーパーコンピュータ
- 並列プログラミング入門―ネットワーク結合UNIXマシンによる並列処理
- PCクラスタ構築法―Linuxによるベオウルフ・システム
協調作業のための基本パラダイム
パラダイム
- 結果並列法 (result parallelism)
- 手順並列法 (agenda parallelism)
- 専門家並列法 (specialist parallelism)
プログラミングの方法
- メッセージパッシング
- ライブデータ構造
- 分散データ構造
基本データ構造
基本分散データ構造体
ロック、セマフォ
# カウンティングセマフォ ts.write(['sem']) # n回実行 ts.take(['sem']) # タスクのバッグ ts.write(['task', 'task-description']) ts.take(['task', nil])
Rinda::TupleSpaceにはevalがありません。 速度を気にしなければThreadで代用できなくもない。
# eval??
10.times do |n|
Thread.new(n) { |x| ts.write(['this loop', x]) }
end
10.times do |n|
ts.take(['this loop', n])
end
名前によってアクセスされる構造体
# [name, value] ts.read(['name', nil]) # 更新 ts.take(['name', nil]) ts.write(['name', 'new value']) # bariier-37でバリア同期 ts.write(['barrier-37', n]) name, value = ts.take(['barrier-37', nil]) ts.write(['barrier-37', value - 1])
位置によってアクセスされる構造体。分散配列。
10.times do |n| tuple = ts.read(['A', i, n, nil]) row_band[n] = tuple[3] end
inストリームとrdストリーム
消費して行く方がinストリーム。rdストリームは読んでも減らない。読む位置をずらすだけ。
ストリーム'strm'はこんなタプル群
['strm', 1, value1] ['strm', 2, value2] ...
最後の要素のインデックスはtail tuple
['strm', 'tail', 14]
'strm'に要素を追加するには…
tuple = ts.take(['strm', 'tail', nil]) index = tuple[2] ts.write(['strm', 'tail', index+1]) ts.write(['strm', index, 'new element'])
inストリームには先頭(head tuple)も必要
tuple = ts.take(['strm', 'head', nil]) index = tuple[2] ts.write(['strm', 'head', index+1]) tuple = ts.take(['strm', index, nil]) value = tuple[2]
readストリームにはhead tupleは必要ない
ライブデータ構造体
writeのかわりにeval(はないのでThread.new)を使う。promise, futureとかそんなの?
require 'rinda/tuplespace'
$ts = Rinda::TupleSpace.new
n = 100
$ts.write(['live stream', 0, 1])
(1..n).each do |i|
Thread.new(i) do |x|
tuple = $ts.read(['live stream', x-1, nil])
$ts.write(['live stream', x, tuple[2] * x])
end
end
p $ts.read(['live stream', n, nil])
詳細をすべてまとめると
結果並列法による素朴な作戦。優雅だけど効率悪いよ
require 'rinda/tuplespace'
$ts = Rinda::TupleSpace.new
def is_prime(n)
return true if n == 2
limit = Math.sqrt(n.to_f) + 1
i = 2
while (i < limit)
tuple = $ts.read(['primes', i, nil])
return false if tuple[2] && (n % i == 0)
i += 1
end
return true
end
def main(n)
(2..n).each do |i|
Thread.new(i) do |x|
$ts.write(['primes', x, is_prime(x)])
end
end
(2..n).each do |i|
tuple = $ts.read(['primes', i, nil])
puts(i) if tuple[2]
end
end
str = ARGV.shift || '10'
main(str.to_i)
協調されたプログラム
なんとなくOOPでないコード。かっこわる。
require 'rinda/tuplespace'
class Phil
def initialize(ts, size)
@ts = ts
@size = size
@status = []
end
def periodic
while true
sleep(0.5)
p @status
end
end
def think(i)
sleep(rand(5))
end
def eat(i)
@status[i] = '|@|'
sleep(rand(5))
end
def phil(left)
right = (left + 1) % @size
while true
@status[left] = '<O>'
think(left)
@ts.take(['room ticket'])
@status[left] = ' o '
@ts.take(['chopstick', left])
@status[left] = '|o '
@ts.take(['chopstick', right])
@status[left] = '|o|'
eat(left)
@status[left] = '|O|'
@ts.write(['chopstick', left])
@status[left] = ' O|'
@ts.write(['chopstick', right])
@status[left] = ' O '
@ts.write(['room ticket'])
end
end
def start
Thread.new { periodic }
@size.times do |i|
Thread.new(i) { |x| phil(x) }
end
@size.times do |i|
@ts.write(['chopstick', i])
@ts.write(['room ticket']) if i < @size-1
end
end
end
Phil.new(Rinda::TupleSpace.new, 10).start
gets
感想
基本パラダイムのように整理して考えたことなかったので、勉強になりました。
inしてoutすることでアトミックな操作を実現できるんだなぁ、としみじみ思うのでした。
car, cdr, consを実装しよう!という演習問題もありました。
参考資料
- 並列プログラムの作り方 N. Carriero/D.Gelernter 共立出版
- <URL:http://www.hlla.is.tsukuba.ac.jp/%7eyas/sie/pdsoft-2001/2001-12-06/> - 並列プログラミングスタイル
- Rinda2



