Sun Grid Engine を使ってみた

某所で使えたので、手を染めてみました。

まず、実行するプログラムの用意。最近のコンピュータは速いので、ちょっとした計算は直ぐに実行が終わってしまっておもしろくない。以下の様な(超無駄な)乱数を持った行列生成スクリプト make_rand_matrix.rb を用意。2行目(#$の行)は、後でqsubに実行シェルを渡すおまじない。
使い方

./make_rand_matrix.rb 3 500

結果。3x500の行列(各要素は1か-1)を生成して、各行の平均と標準偏差を計算する

0       -0.16   0.99713876380658
1       -0.16   0.99713876380658
2       -0.08   1.00691486781178
Start: Fri Apr 17 13:24:57 +0900 2009
End: Fri Apr 17 13:24:57 +0900 2009
Time: 0.000339

スクリプト

#!/usr/bin/env ruby
#$ -S /usr/local/bin/ruby

def rand_matrix(n, m)
  matrix = []
  1.upto(n) do 
    row = Array.new(m).fill(0.0)
    row_new = row.map do
      v = rand()
      if v < 0.5
        -1.0
      else
        1.0
      end
    end
    matrix << row_new
  end
  return matrix
end

def row_stat(matrix)
  row_num = 0
  matrix.each do |row|
    sum = 0.0
    row.each do |v|
      sum += v
    end
    avg = sum / row.size
    sd = 0.0
    row.each do |v|
      sd += (v - avg) * (v - avg)
    end
    sd = Math.sqrt(sd / (row.size - 1))
    print "#{row_num.to_s}\t#{avg.to_s}\t#{sd.to_s}\n"
    row_num += 1
  end
end

row_num = ARGV.shift
col_num = ARGV.shift

start_time = Time.now
matrix = rand_matrix(row_num.to_i,col_num.to_i)
row_stat(matrix)
end_time = Time.now
diff_time = end_time - start_time

print "Start: #{start_time.to_s}\n"
print "End: #{start_time.to_s}\n"
print "Time: #{diff_time}\n"

これをSun Grid Engineに投入して実行してみる。

% qsub -o `pwd`/stdout.txt -e `pwd`/stderr.txt ./make_rand_matrix.rb 3 50
  • o は実行時の標準出力を書き出すファイル。-eはエラー出力のもの。出力がどこで実行しても$HOMEに行くので、カレントディレクトリを頭に付けている。暫くすると実行が終わる。qstat で状態監視できる。
% less stdout.txt                                                   [sesejun@gw10]
0       -0.12   1.00285307284481
1       0.12    1.00285307284481
2       -0.04   1.0093440990089
Start: Fri Apr 17 13:30:51 +0900 2009
End: Fri Apr 17 13:30:51 +0900 2009
Time: 0.002694

もっとでかいのも、投入してみる

% qsub -o `pwd`/stdout.txt -e `pwd`/stderr.txt ./make_rand_matrix.rb 3000 5000
(略)
2999    -0.0108 1.00004168746872
Start: Fri Apr 17 13:32:51 +0900 2009
End: Fri Apr 17 13:32:51 +0900 2009
Time: 608.556413

以上は、シングルプロセスだけの話なので、いろいろなサイズの行列を作る作業を並列化する(実行時間にばらつきを持たせたいので)。qsub下では、$HOMEからのパスで指定しないといけないことに注意。

% less make_rand_matrix.sh 
#!/usr/local/bin/zsh
#$ -S /usr/local/bin/zsh

for i in {1..10}; do
  /path/to/qsub -o $WORK_DIR/avg_$i.txt $WORK_DIR/make_rand_matrix.rb `expr $i \* 200` 6000;
done;

各実行時間は、以下の感じ。数字が小さい方が実行時間が速いはず何だけと、jobが投入されたマシンによってばらつきがある。同じマシンで、他の重たいjobでも動いていたか?単独で動かしても 1600 x 6000の行列は遅いので、何か別の要因かも。

% tail -n1 avg_{1..10}.txt
==> avg_1.txt <==
Time: 19.025231

==> avg_2.txt <==
Time: 72.483549

==> avg_3.txt <==
Time: 163.00908

==> avg_4.txt <==
Time: 294.959056

==> avg_5.txt <==
Time: 472.732914

==> avg_6.txt <==
Time: 704.010809

==> avg_7.txt <==
Time: 964.453544

==> avg_8.txt <==
Time: 1267.160671

==> avg_9.txt <==
Time: 708.957818

==> avg_10.txt <==
Time: 238.609282

全体の実行時間は、20分強(avg_8.txtと同じくらい)でした。単に並列でうごかしただけなので、一番遅いプロセスに引きずられました。