Bioblogs

DRM을 이용한 대량의 데이터 분석을 손쉽게

hongiiv 2008. 7. 7. 12:29
반응형
본 문서는 Grid Engine의 "Simple-Job-Array-Howto" 문서를 기반으로 만들어졌으며, 이전에 포스팅한 "스케줄러 - 기본으로 돌아가기"와 밀접한 관계가 있습니다. ^^

DRM에서의 Serial 프로그램 실행하기


많은 수의 job들을 실행하기 위해서는 어떻게 해야 할까? 1,000개의 데이터셋이 있고, 이것을 하나의 프로그램이 실행한다고 한다면, 모두 1,000개의 Shell 스크립트를 작성해서 queue에 넣어야 할것이다. 바로 이러한 자잘한? 문제를 해결하기 위해서 Grid Engine에서는 Array job이라는 해결책을 제시해 주고 있다.

-i 옵션의 인자를 입력으로 받고, -o 인자의 파일에 program의 수행결과를 쓰는 프로그램을 Grid Engine을 통해 제출한다고 하면 아래와 같은 형식의 job 제출 스크립트를 사용할 것이다.

#!sh
#$ -S /bin/bash
~/programs/program -i ~/data/input -o ~/results/output

그런데, 여기서 input1, input2, ... input.1000의 총 1,000개의 input이 존재한다면, 각각의 1,000개의 스크립트를 vi를 통해 작성하거나, 좀 배운 사람은 perl이나 python 스크립트를 통해 1,000 개의 별도의 스크립트를 자동으로 생성해주는 스크립트를 만들어 사용할 것이다. ^^

Array job을 이용한 Serial job 수행

그러나, Grid Engine의 Arry jobs를 이용하면 간단하게 한줄의 추가로 이러한 작업을 모두 해결해 줄 수 있다.

#!sh
#$ -S /bin/bash
#$ -t 1-1000
~/programs/program -i ~/data/input.$SGE_TASK_ID -o ~/results/output.$SGE_TASK_ID

Grid Engine의 -t start-end 옵션을 통해서 간단히 1,000개의 개별적인 job을 한번에 수행할 수가 있다. 별도의 작업 없이도 말이다. ^^ -t 를 통해 수행할 Array job의 갯수를 지정하면, 각각의 job은 -t에서 지정한 갯수로 나뉘면서 각 노드에 할당되고 각 노드는 현재 자신이 몇번째 작업을 수행하는지를 $SGE_TASK_ID 변수에 가지고 있어, 이를 통해 Grid Engine이 할당해 준 array를 수행하고 결과를 내게 된다.

이것만으로도 serial 한 job들을 손쉽게 수행할 수 있지만, 좀 더 응용해서 복잡한 문제들을 해결해 보도록 하자.

#!sh
#$ -S /bin/bash
#$ -t 1-1000
if [ ! -e ~/results/output.$SGE_TASK_ID ]
then
~/programs/program -i ~/data/input.$SGE_TASK_ID -o ~/results/output.$SGE_TASK_ID
fi

bash shell의 if를 통해서 job을 할당받은 노드는 결과 파일의 유무를 확인하고 program을 수행하게 된다. 이렇게 스크립트를 통해 해당 결과가 없을때 수행하게 한다면, 후에 하나의 노드를 통해 해당 program을 수행하거나 하는 경우나 실수로 엔터를 잘못 눌러 다시 수행될때( 있으려나 ^^)에 어느 정도 유연성을 줄 수가 있다.

이처럼 아주 간단한 -t 옵션과 $SGE_TASK_ID이지만, bash shell과 만나면서 더욱더 다양하게 응용될 수 있다. 이제 하나씩 살펴 보도록 하자.

각 프로그램마다 옵션을 달리하고자 하는 경우

위의 예제와는 달리 각 실행되는 프로그램마다 옵션을 달리해야 하는 일이 발생한다면 어떻게 해야 할까? cat과 head, tail이 그 문제의 해답이다. 먼저 각 옵션만을 별도의 파일로 작성한다. simulation이라는 프로그램이 있고, 여기서 -s 옵션의 인자를 각각 달리 해주고 싶다면,  SEEDFILE을 하나 만들어서 각각의 라인에는 옵션의 값들을 적어준다.

seeds 파일
=======================
45
90
.
.
.
1,000번째의 옵션값
=======================

#!/bin/sh
#$ -S /bin/bash -t 1-1000
SEEDFILE=~/data/seeds
SEED=$(sed -n -e "$SGE_TASK_ID p" $SEEDFILE)
~/programs/simulation -s $SEED -o ~/results/output.$SGE_TASK_ID

이렇게 되면 -s의 값으로는 45, 90...의 값들이 각각 들어가게 되어 하나의 프로그램이지만 인자가 서로 다른 프로그램들이 Grid Engine을 통해서 실행되게 된다.

파일의 순서가 1이 아닌 0부터 시작한다면, Array job 설정시 -t 0-999 이거~ 안된단다,, 0을 인식 못한단다. 그렇다고 기존에 0부터 매겨진 파일이름을 1부터 시작하도록 바꿔야 하는것인가?
아니다. 간단한 bash로 0부터 시작해 보자.

#!sh
#$ -S /bin/bash
#$ -t 1-1000
let i=$SGE_TASK_ID-1
if [ ! -e ~/results/output.$i ]
then
~/programs/program -i ~/data/input.$i -o ~/results/output.$i
fi


R과 같은 대화형 프로그램을 수행하고자 한다면

-1만 해주면 되는것을,,, ^^ 그럼 이제 마지막으로 좀 더 고급스럽게 array job을 사용해 보도록 하자. R과 같은 대화형? 프로그램을 사용하고자 하는 경우에는 어떻게 처리해야할까? 이런 경우 R을 실행한 후 "EOF", "/dev/null" 등을 사용해서 각 파일의 값을 R에서 읽고 결과 파일을 생성해 보자.

#!sh
#$ -S /bin/bash
#$ -t 1-10
WORKDIR=/Users/jl566/testing
INFILE=$WORKDIR/data.$SGE_TASK_ID
OUTFILE=$WORKDIR/data.$SGE_TASK_ID.out
# R 실행파일의 경로를 PATHTOR에 지정해준다.
PATHTOR=/common/bin
if [ -e $OUTFILE ]
then
rm -f $OUTFILE
fi
# "EOF" 사이에 원하는 R 커맨드를 입력하고 그 결과 파일을 OUTFILE에 쓴다.
$PATHTOR/R --quiet --no-save > /dev/null <<EOF
x<-read.table("$INFILE")
write(mean(x\$V1),"$OUTFILE")
EOF

여러가지 상황에 대해서 Grid Engine의 Array job을 이용해서 serial job을 간단하게 수행하여, 생산성을 높일 수가 있다. 비록 Grid Engine에 국한되어서 설명되어 있지만, 각각의 상황들은 손쉽게 다른 DRM(PBS, OpenPBS, Torque, LoadLeveler)들에 응용되어 사용될 수 있다.

잘 이해가 되지 않는다면, 참고한 원본 문서(http://wiki.gridengine.info/wiki/index.php/Simple-Job-Array-Howto)를 살펴 보기를 권한다. 다시 한번 말하지만, 여러가지 상황에 맞도록 적절한 스크립트와 DRM을 사용한다면, 대량의 분석작업이 간단하고 빠르게 수행할 수 있을뿐 아니라, 논문도 ^^;; 가능하다.



반응형