0
0

More than 1 year has passed since last update.

Rubyで多次元ベクトルの内積を並列計算

Last updated at Posted at 2022-10-11

例として12次元ベクトルを前半、後半に分けて、前半の内積、後半の内積を2つのTheadを使って求める。2つの内積の和が求めたい内積の値となる。
前半の内積、後半の内積を計算するProc(クロージャー)を作ってQueueに放り込むコードは次の通り。
前半の内積、後半の内積を変数sumに蓄積したいが、この計算はThread安全に行いたいのでクリティカルセクションとしたい。
クリティカルセクションにしたいクロージャーは、Mutexオブジェクトのsynchromizeメソッドの引数に与えれば作ることができる。

# 内積の計算を2Threadで実行。
ary0 = [0.4, 0.2, 0.3, 0.5, 0.2, 0.4, 0.5, 0.1, 0.2, 0.4, 0.5, 0.9]
ary1 = [0.1, 0.2, 0.2, 0.2, 0.3, 0.4, 0.4, 0.4, 0.5, 0.5, 0.5, 0.9]
sum = 0
lock = Mutex.new
q = Queue.new
[{:from => 0, :to => ary0.size/2},
 {:from => ary0.size/2+1, :to => ary0.size-1}].each {|e|
  q.push proc {
    t = 0
    e[:from].upto(e[:to]) {|i|
      t = t + ary0[i] * ary1[i]
    }
    lock.synchronize { sum = sum + t }
  }
}

Queueからクロージャーを取り出して実行するThreadを2個作る。作った時に返るThreadハンドルは配列に格納しておく。
これで、main Threadとは別のThreadが作られ走り始めるが、作られたThreadが終わるのをmain Threadで待ち構えるためにThreadハンドルが使われる。

th = []
0.upto(1) {|i|
  until q.empty?
    th << Thread.start(q.pop) {|e|
      e.call
    }
  end
}
# 0.upto(1) {|i|
#   th << Thread.new {
#     until q.empty?
#       q.pop.call
#     end
#   }

2つのThreadが終わるをmain Threadで待って内積の値を表示する。

th.each {|t| t.join}
puts sum

検算のためにThreadを使わずに内積の計算をして、Threadを使った場合の結果と比較してみる。両者の値はコード末尾の2つの値で一致する。

# 2Threadで実行して得た結果と、main Threadで計算して得た結果を比較
checksum = 0 
ary0.each_with_index {|e, i|
 checksum =  checksum + ary0[i] * ary1[i]
}
puts checksum
__END__
2.0600000000000005
2.0600000000000005
0
0
3

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
0
0