$$ \def\bra#1{\mathinner{\left\langle{#1}\right|}} \def\ket#1{\mathinner{\left|{#1}\right\rangle}} \def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}} $$

◇◇AWS EC2でマラソンマッチの並列テストを回すメモ◇◇


AWSの勉強も兼ねて、マラソンマッチの並列テスト環境を作ってみることにした。多分忘れるので自分用にメモを残す(いいやり方を誰か教えてください)

○問題をきめる

問題は何でもいいので、いつもの「Hokkaido Univ.& Hitachi 1st New-concept Computing Contest 2017 Probrem 1」を使うことにした。シンプルな問題設定なのでマラソンマッチで遊ぶのに向いている(テスターもついている(これは重要))

○適当にコードを書く

順番に並べて、ランダムで2点入れ替える山登りを書いてみた。 当然弱い。このソースコードを使って並列実行をしてみる。

○ローカルで並列実行してみる

ローカル環境はUbuntu 18.04.5 LTS。CPUは4コア4スレッドなので、4並列までは走らせられそうな感じがする。Python3を使ってマルチプロセスでテスターを走らせるスクリプトを書く。

・まずは直列

シングルプロセスで5つのseedを順番にテストして結果を出力するコードを書いてみた。コマンドを順番に実行しているだけ。

                
import subprocess

toolkit_dir = "./problem1_toolkit_JP/scripts/"
input_dir = "./input/"
output_dir = "./output/"
for seed in range(5):
    input_filename = "input_" + str(seed) + ".in"
    output_filename = "output_" + str(seed) + ".out"
    print("seed = " + str(seed))
    # テストケース生成
    subprocess.run([toolkit_dir + "graph_generator.out " + input_dir +
                    input_filename + " 0 " + str(seed)], shell=True)
    # プログラム実行
    subprocess.run(["./a.out < " + input_dir + input_filename +
                    " > " + output_dir + output_filename], shell=True)
    # 評価
    subprocess.run([toolkit_dir + "score_evaluator.out " + input_dir +
                    input_filename + " " + output_dir + output_filename], shell=True)
    # ファイルを削除
    subprocess.run(["rm " + input_dir + input_filename + " " +
                    output_dir + output_filename], shell=True)
                
            

・並列にする

並列にした。オプションでテスト数と並列度を指定できるようにして、スコアを取り出す部分も雑に書いてみた。コンテストによってテスト方法や出力のフォーマットが違うので、適当にうまく書く。全部同じ時間で終わるので、並列実行は$N$プロセス起動→終了したら次の$N$プロセス起動でよい。

                
import subprocess
import argparse

toolkit_dir = "./problem1_toolkit_JP/scripts/"
input_dir = "./input/"
output_dir = "./output/"
test_num = 10
parallel_num = 2

parser = argparse.ArgumentParser(description = "--testNum and --parallelNum")
parser.add_argument("--testNum", type=int)
parser.add_argument("--parallelNum", type=int)
args = parser.parse_args()

if args.testNum != None:
    test_num = args.testNum
if args.parallelNum != None:
    parallel_num = args.parallelNum

# テストケース生成
for seed in range(test_num):
    input_filename = "input_" + str(seed) + ".in"
    subprocess.run(toolkit_dir + "graph_generator.out " + input_dir +
                    input_filename + " 0 " + str(seed), shell=True)

# プログラム実行
running_procs = []
for seed in range(test_num):
    print("seed = " + str(seed))
    input_filename = "input_" + str(seed) + ".in"
    output_filename = "output_" + str(seed) + ".out"
    proc = subprocess.Popen("./a.out < " + input_dir + input_filename +
                            " > " + output_dir + output_filename, shell=True)
    running_procs.append(proc)

    if len(running_procs) == parallel_num or seed + 1 == test_num:
        for p in running_procs:
            p.communicate()
        running_procs.clear()

# 評価, ファイルの削除
totalScore = 0
for seed in range(test_num):
    input_filename = "input_" + str(seed) + ".in"
    output_filename = "output_" + str(seed) + ".out"
    proc = subprocess.run(toolkit_dir + "score_evaluator.out " + input_dir +
                        input_filename + " " + output_dir + output_filename, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    out = proc.stdout.decode("utf-8").strip()
    print(out)
    scores = out[14:].replace(" ", "").split("/")
    score, sum = int(scores[0]), int(scores[1])  # スコア、合計
    totalScore += score / sum * 10000 / test_num
    subprocess.run("rm " + input_dir + input_filename + " " +
                output_dir + output_filename, shell=True)
    
print(totalScore)
                
            

○AWSでサーバを借りて回す

次に、AWSでサーバを借りて回す。スポットインスタンスとして借りると安くすむ。

  1. AWSコンソールを開いて「EC2」→「インスタンス」を選択する。
  2. 「インスタンスを起動」を選択する。
  3. AMIを選択する。とりあえず「Amazon Linux 2 AMI (HVM), SSD Volume Type」が要件を満たすことは確認済。
  4. インスタンスタイプを選択する。vCPUの数字が大きいものを選ぶと並列度を高くできる(高いけど)
  5. インスタンスの詳細の設定で、「スポットインスタンスのリクエスト」にチェックする。最高価格は自分が許せる金額を入れる。「永続的リクエスト」にチェックする必要はなく(ワンタイムリクエスト)、したがって「リクエスト有効期間の終了」は「任意の時刻」でよい。
  6. VPCはデフォルトのやつがあったらそれで良さそう。ない場合は作成する。「アクション」→「デフォルトVPCの作成」?試してないのでわからず。
  7. それ以外は特に触る必要なし、ストレージもそのままでOK。タグの追加で名前を適当に決める。
  8. セキュリティグループの設定では、新しいセキュリティグループを作成する。すでに作成したものがあればそれを使って良い。ソース(送信元)を0.0.0.0/0にしても問題ないが、どこからでもアクセス可能になるのでセキュリティ的に問題ありらしい(鍵がないと入れないので入り放題ではない)
  9. キーペアを指定して作成。インスタンスができたらパブリックIPを使ってアクセスする。
  10. あとは好きに使う
  11. 終わったらちゃんとインスタンスを終了する!

戻る