Create  Edit  Diff  Mac OS X Serverで作る 小さな計算機室  Index  Search  Changes  RSS  Login

コマンドラインからMPIのジョブを投入

Xgridのコマンドラインインターフェース

Mac OS X Tigerには、xgridというコマンドが最初からインストールされていて、それを 使ってxgridのコントローラを操ることができる。 man xgridすると、xgridコマンドの使い方だけでなく、バッチの構成法などの情報も 書いてあるので、それをまず勉強する。

MPIを使う場合には、実行ファイルの他に、xgridmpibootという一種のwrapperプログラムが 必要で、このxgridmpibootを経由して、プログラムを起動することになる。 xgridmpibootはサンプルプログラムの中に、すでにコンパイルされた状態で置かれている (ソースが欲しいが見あたらない):

/Developer/Examples/Xgrid/GridSample/MPI/xgridmpiboot

少しリバースエンジニアリングをすると、このxgridmpibootには、以下のようなオプションを 与える必要があることがわかる:

xgridmpiboot -IsMaster (YES|NO) -MasterName 名前 -MPIType MacMPI 
   -NumberOfProcesses 数字 -ExecutablePath 実行ファイル名 
   -ExecutableArguments 実行ファイルのパラメータ

つまり、やるべきことは、上記のようなジョブを必要なプロセッサの数だけ生成するような バッチファイルを記述することである。

バッチファイル(XML)を作成する

バッチファイルの書き方の資料はman xgridくらいしか見あたらないので、それを参考にXMLを 書くことにする。 が、バッチファイルの中身には、先のxgridmpibootや実行ファイルの内容、つまりバイナリー データ、をBase64エンコードして引き渡さないといけないので、 エディタでせっせと書くよりは バッチを生成するようなスクリプトを組むのが現実的だ。

ついでながらOS 10.4.2までのman xgridのXMLの例には間違いがあって、inputFilesのところのファイルのデータは <string>Base64のデータ</string>ではなくて、<data>Base64のデータ</data>で渡さなければならない (最近アップデートされた10.4.3のmanでここは直ってました)。

試しにRubyで書いてみたスクリプトがこちら:gen-xgrid-mpi-batch.rb

#!/usr/bin/ruby
#
# Copyright 2005 Yoshinori Hayakawa
# All Rights Reserved
#
require "optparse"
require "base64"

# defaults
$bootfile = "xgridmpiboot"
$exefile = "a.out"
$cmdargs = ""
$np = 1
$jobname = "job"
$email = ""

def writehex(filename)
    bcnt = 0 ;
    file = File.open(filename)
    file.binmode
    while data = file.read(1)
       $stdout.printf("%02x",data[0])
       bcnt = bcnt + 1
       if bcnt%32 == 0	   
          $stdout.printf("\n") 
       elsif bcnt%4 == 0 
          $stdout.printf(" ")
       end
    end
    file.close
end

def writebase64(filename)
    file = File.open(filename)
    file.binmode
    data = file.read
    b64 = Base64.b64encode(data)
    $stdout.print b64.chomp
    file.close
end

def printinputfile(filename,exflag)
    $stdout.puts  "      <key>#{filename}</key>"
    $stdout.puts  "      <dict>"
    $stdout.puts  "         <key>fileData</key>"
    $stdout.print "         <data>"
    writebase64(filename)
    $stdout.puts  "</data>"
    if exflag then
    $stdout.puts  "         <key>isExecutable</key>"
    $stdout.puts  "         <string>YES</string>"
    end
    $stdout.puts  "      </dict>"
end

def inputfiles(fnamehash)
    $stdout.puts "   <key>inputFiles</key>"
    $stdout.puts "   <dict>"
    fnamehash.each { |name,flag|
      printinputfile(name,flag)
    }
    $stdout.puts "   </dict>"
end

def printhead(jobname,email)
$stdout.print <<-ENDOFHEAD1
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
  <dict>
   <key>applicationIdentifier</key>
   <string>mpi.on.xgrid</string>
   <key>name</key>
   <string>#{jobname}</string>
ENDOFHEAD1

if email.length>0 then
   puts "      <key>notificationEmail</key>"
   puts "      <string>#{email}</string>"
end

end

def scheduleparam(n)
$stdout.print <<-ENDOFSCHEDULE
   <key>schedulerParameters</key>
   <dict>
      <key>tasksMustStartSimultaneously</key>
      <string>YES</string>
      <key>minimumTaskCount</key>
      <integer>#{n}</integer>
   </dict>
ENDOFSCHEDULE
end

def printtail
$stdout.print <<-ENDOFTAIL
  </dict>
</array>
</plist>
ENDOFTAIL
end

def randomstr(len)
  srand
  chars16 = "0123456789ABCDEF"
  str=""
  for i in 1..len
     str << chars16[rand(16)] 
  end
  return str
end

def taskspec(n,command,cmdargs)
   mastername = randomstr(24)
   $stdout.puts "   <key>taskSpecifications</key>"
   $stdout.puts "   <dict>"
   for id in 0..n-1 do
      $stdout.puts "      <key>#{id}</key>"   
      $stdout.puts "      <dict>"
      $stdout.puts "        <key>command</key>"
      $stdout.puts "        <string>xgridmpiboot</string>"
      $stdout.puts "        <key>arguments</key>"
      $stdout.puts "        <array>"
      $stdout.puts "           <string>-IsMaster</string>"
      if id==0 
      $stdout.puts "           <string>YES</string>"
      else
      $stdout.puts "           <string>NO</string>"
      end
      $stdout.puts "           <string>-MasterName</string>"
      $stdout.puts "           <string>#{mastername}</string>"
      $stdout.puts "           <string>-MPIType</string>"
      $stdout.puts "           <string>MacMPI</string>"
      $stdout.puts "           <string>-NumberOfProcesses</string>"
      $stdout.puts "           <string>#{n}</string>"
      $stdout.puts "           <string>-ExecutablePath</string>"
      $stdout.puts "           <string>#{command}</string>"
      if cmdargs.length>0 then
      args = cmdargs.delete('"')
      $stdout.puts "           <string>-ExecutableArguments</string>"
      $stdout.puts "           <string>#{args}</string>"
      end
      $stdout.puts "        </array>"
      $stdout.puts "      </dict>"
   end
   $stdout.puts "   </dict>"
end

def genbatch(jobname,email,n,fnamehash,command,cmdargs)
  printhead(jobname,email)
  scheduleparam(n)
  inputfiles(fnamehash)
  taskspec(n,command,cmdargs)
  printtail
end

# main 

$fnamehash = Hash.new

# to handle command line options
optparser = OptionParser.new
optparser.banner = "Usage: #{File.basename($0)} [options] inputfiles... "
optparser.on("-n NP","--np=NP",String,"Number of processors") { |arg|
    $np = arg.to_i
}
optparser.on("-e EXEFILE","--exe=EXEFILE",String,"Object file for execution") { |arg|
    $exefile = arg
}
optparser.on("-a CMDARGS","--arg=CMDARGS",String,"Options") { |arg|
    $cmdargs = arg.inspect
}
optparser.on("-j JOBNAME","--job=JOBNAME",String,"Job name") { |arg|
    $jobname = arg
}
optparser.on("-m EMAIL","--email=EMAIL",String,"Email address") { |arg|
    $email = arg
}
optparser.on("-h","--help","Help") {
   puts optparser.help
   exit 0
}

begin 
   optparser.parse!
   $fnamehash.store($bootfile,false)
   $fnamehash.store($exefile,true)
   ARGV.each do |datafile|  
     $fnamehash.store(datafile,false)
   end
 
   $fnamehash.each do |key,value|
      if !FileTest.exist?(key) then
         $stderr.puts "#{key} not found"
         exit 1
      end
   end
   genbatch($jobname,$email,$np,$fnamehash,$exefile,$cmdargs)
rescue OptionParser::ParseError => err
   $stderr.puts err.message
   $stderr.puts optparser.help
   exit 1
end

xgridmpibootと実行ファイル(a.out)が置いてあるディレクトリ内で

gen-xgrid-mpi-batch.rb -j job1 -n 10 -e a.out > batch1.xml

のようにして使う。

作ったバッチファイルを少しだけ手直ししたいときは、 /Developer/Applications/Utilities/Property List Editor.app でいじると便利かもしれない。

Xgridにバッチを投入する

バッチファイルができてしまえば、バッチの投入は簡単で(パスワードで認証している場合は)

xgrid -h ホスト名 -auth Password -p コントローラのパスワード -job batch バッチファイル

でOK。

ジョブの状態を確認したり、結果を引き出すのも、もちろんxgridコマンドで可能。

  • Anchor to the attached file is {{attach_anchor(file name [, page name])}}.
  • Indication of the attached file is {{attach_view(file name [, page name])}}.
  • List of the attached pages and files is {{attach_map}}.
Last modified:2005/11/05 10:15:10
Keyword(s):
References:[mpionxgrid] [Xgrid上でMPIを使う] [Mac OS X Serverで作る 小さな計算機室] [frontpage]