Parallel Execution

make 명령을 실행할 때 -j 옵션을 이용하면 다음과 같은 경우에 rule 들이 동시에 병렬로 실행됩니다.

# prerequisites 에 여러개의 타겟이 존재할 경우. (foo bar zoo 는 동시에 실행된다.)
all : foo bar zoo

------------------

all : foo    # 이 형태는 위와 같은 것임
all : bar
all : zoo

------------------

# 명령행 상에서 여러개의 타겟을 전달할 경우. (foo bar zoo 는 동시에 실행된다.)
sh$ make -j8 foo bar zoo

병렬 실행시 출력 동기화 하기

여러 타겟의 recipe 가 병렬로 동시에 실행되면 문제가 출력이 서로 섞여서 보기가 어려워진다는것입니다. 따라서 make 은 -O ( --output-sync ) 출력 동기화 옵션을 제공해서 문제를 해결합니다. 이기능은 출력이 발생할때 마다 바로 터미널에 출력하는것이 아니라 메시지를 특정 그룹단위로 모아서 한번에 출력합니다.

다음은 옵션에 사용할 수 있는 type 인데 값을 지정하지 않으면 디폴트로 target 이 사용됩니다.

none 출력 동기화 옵션을 사용하지 않았을 때 디폴트 동작 방식입니다. 각 타겟의 recipe 에서 출력이 발생할 때마다 바로 출력됩니다.
line recipe 라인 단위로 출력을 모아서 출력합니다. 한 라인에 여러 개의 명령이 있을 경우 출력이 모두 모아진 후에 한번에 출력됩니다. 라인 단위이므로 A 룰의 recipe 라인이 출력된 후에 B 룰의 recipe 라인이 올수 있습니다.
target target 단위 그러니까 해당 rule 의 전체 recipe 출력을 모았다가 한번에 출력합니다. target 단위이므로 A 룰의 recipe 출력이 완료된 후에 다른 룰의 recipe 가 출력됩니다.
recurse 이것은 recursive make 에서 사용되는 sub-make 단위로 출력을 모아서 한번에 프린트합니다.
all : one two three four

one two :
    @echo $@ start
    @sleep 1
    @echo $@ middle
    @echo $@ end

three four :
    @echo $@ start
    @sleep 2
    @echo $@ middle
    @echo $@ end

########  실행 결과  ########
sh$ make -j8                      sh$  make -j8 -O
one start                         one start
two start                         one middle
three start                       one end
four start                        two start      # target 단위로 출력이 된다.
one middle                        two middle
two middle                        two end
one end                           three start
two end                           three middle
three middle                      three end
four middle                       four start
three end                         four middle
four end                          four end

make 4.2.1 버전에서는 '-O' 옵션에 버그가 있는것 같네요. 테스트에 4.3 버전을 사용했습니다.

병렬 실행시 입력

기본적으로 두 개의 프로세스가 동일한 device ( 터미널 ) 로부터 입력을 받을 수는 없습니다. 따라서 make 은 하나의 recipe 만 남기고 나머지들은 stdin 을 disable 합니다. 이때 만약에 다른 recipe 에서 stdin 읽기를 시도한다면 그것은 오류가 됩니다 ( broken pipe ). 그러므로 여러 개의 recipe 를 병렬로 실행할 때는 stdin 을 사용하지 않는 것이 좋습니다. 물론 single thread 로 실행할 경우는 이와 같은 문제가 발생하지 않습니다.

시스템 로드에 따라 새로 생성되는 job 제한하기

-l ( --load-average ) 옵션을 이용하면 시스템 로드에 따라 새로 생성되는 job 을 제한할 수 있습니다. 기본적으로 1 개의 job 은 항상 실행되지만 새로 job 을 생성할 때는 먼저 -l 옵션에서 설정한 값을 비교하여 현재 시스템 로드가 해당 값보다 작을 경우에만 새로 job 을 생성합니다.

# 시스템 로드가 2.5 이상이면 새로 job 을 생성하지 않는다.

sh$ make -j8 -l 2.5

병렬 실행시 특정 순서대로 실행하기

다음은 foo 의 prerequisites 에서 aaa, bbb 보다 xxx 타겟을 먼저 실행하기 위해 의존 타겟 라인을 추가한 것입니다. (1) 번의 경우는 정상적으로 동작을 하지만 (2) 번의 경우는 aaa, bbb 룰에도 prerequisites 이 추가되어 정상적으로 동작하지 않습니다. 이것은 aaa 타겟의 경우 prerequisites 이 yyy, xxx 가 되고 bbb 타겟의 경우는 zzz, xxx 가 되는 것과 같습니다. 따라서 이 부분에서도 병렬로 실행이 됩니다. 그러므로 이때는 (3) 번과 같이 yyy, zzz 타겟에 대해서도 의존 타겟 라인을 추가해 주어야 xxx 타겟이 제일 먼저 실행됩니다.

                  (1)                           (2)                            (3)
foo : aaa bbb | xxx           foo : aaa bbb | xxx            foo : aaa bbb | xxx

aaa bbb : xxx                 aaa bbb : xxx                  aaa bbb : xxx
                                                             yyy zzz : xxx
aaa :                         aaa : yyy                      
    @echo $@                      @echo $@                   aaa : yyy
    @sleep 1                      @sleep 1                       @echo $@  
                                                                 @sleep 1  
bbb :                         bbb : zzz                                    
    @echo $@                      @echo $@                   bbb : zzz
    @sleep 1                      @sleep 1                       @echo $@  
                                                                 @sleep 1  
xxx :                         xxx :                                        
    @echo $@                      @echo $@                   xxx :
    @sleep 1                      @sleep 1                       @echo $@
                                                                 @sleep 1
                              yyy :                          
                                  @echo $@                   yyy :
                                  @sleep 1                       @echo $@
                                                                 @sleep 1
                              zzz :                          
                                  @echo $@                   zzz :
                                  @sleep 1                       @echo $@
                                                                 @sleep 1

###  실행 결과 ###              ###  실행 결과  ###             ###  실행 결과  ###
sh$ make -j8                  sh$ make -j8                   sh$ make -j8
xxx                           yyy                            xxx
aaa                           xxx                            yyy
bbb                           zzz                            zzz
                              aaa                            aaa
                              bbb                            bbb