Makefile - 4. 고급 Makefile 작성 (4-2. 병렬 빌드 (Parallel Build))

2025. 3. 11. 19:10개발/개발도구와 환경

📌 4-2. 병렬 빌드 (Parallel Build)

Makefile에서 병렬 빌드(Parallel Build)는 여러 개의 컴파일 작업을 동시에 실행하여 빌드 속도를 향상시키는 기법입니다.
이를 위해 make -j 옵션과 $(MAKE) 변수를 활용할 수 있습니다.


1. 병렬 빌드(Parallel Build)란?

📌 1-1. 병렬 빌드의 개념

  • 기본적으로 make는 직렬(Sequential)로 실행됩니다.
  • 하지만, 여러 개의 소스 파일을 각각 독립적으로 컴파일할 수 있는 경우,
    여러 개의 CPU 코어를 활용하여 동시에 컴파일을 수행하면 빌드 속도가 크게 향상됩니다.

📌 1-2. 병렬 빌드가 필요한 이유

빌드 속도 향상

  • 멀티코어 CPU를 활용하여 여러 개의 파일을 동시에 컴파일
  • 프로젝트 규모가 커질수록 빌드 시간이 단축됨

하드웨어 리소스 최적 활용

  • 일반적으로 컴파일은 CPU 연산 중심이므로, 멀티코어 환경에서 더 빠르게 실행 가능

대규모 프로젝트에서 필수

  • 리눅스 커널, 크로미움 브라우저, LLVM 같은 대형 프로젝트는 항상 병렬 빌드를 활용

2. make -j 옵션을 사용한 병렬 빌드

📌 2-1. make -j 기본 사용법

make -jN 옵션을 사용하면 N개의 작업을 병렬로 실행합니다.

make -j4

➡ 4개의 프로세스를 동시에 실행하여 컴파일 속도를 높임.

make -j8

➡ 8개의 프로세스를 실행 (멀티코어 CPU에서 추천).

make -j$(nproc)

➡ 시스템의 CPU 개수만큼 프로세스를 자동으로 사용 (nproc 명령어 활용).


📌 2-2. make -j 실행 예제

🔹 프로젝트 구조

project/
├── src/
│   ├── main.c
│   ├── utils.c
│   ├── io.c
├── Makefile

🔹 Makefile (병렬 빌드 적용)

CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c io.c
OBJS = $(SRC:.c=.o)
TARGET = myprogram

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(OBJS) $(TARGET)

직렬 빌드 실행 (기본 실행)

make

➡ main.c, utils.c, io.c가 순차적으로 컴파일됨.

병렬 빌드 실행 (4개 프로세스 사용)

make -j4

➡ main.c, utils.c, io.c가 동시에 컴파일됨 → 빌드 속도 향상.


3. $(MAKE) 변수를 활용한 병렬 빌드

📌 3-1. $(MAKE)란?

  • $(MAKE) 변수는 현재 실행 중인 make 명령어를 저장하는 특별한 변수입니다.
  • 병렬 빌드(-j 옵션 포함)를 하위 Makefile에도 자동으로 전달할 수 있습니다.

📌 3-2. $(MAKE) 활용 예제

🔹 프로젝트 구조

project/
├── src/
│   ├── main.c
│   ├── utils.c
│   ├── Makefile
├── lib/
│   ├── math.c
│   ├── Makefile
├── bin/
│   ├── Makefile
├── Makefile (최상위 Makefile)

🔹 project/src/Makefile

CC = gcc
CFLAGS = -Wall -g
SRC = main.c utils.c
OBJS = $(SRC:.c=.o)

all: $(OBJS)
	$(CC) $(CFLAGS) -c $^

clean:
	rm -f $(OBJS)

🔹 project/lib/Makefile

CC = gcc
CFLAGS = -Wall -g
SRC = math.c
OBJS = $(SRC:.c=.o)

all: $(OBJS)
	$(CC) $(CFLAGS) -c $^

clean:
	rm -f $(OBJS)

🔹 project/Makefile (최상위 Makefile)

SUBDIRS = lib src bin

all:
	for dir in $(SUBDIRS); do $(MAKE) -C $$dir; done

clean:
	for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean; done

병렬 빌드 실행

make -j4

➡ $(MAKE) -C $$dir에 의해 각 서브 디렉토리에서도 병렬 빌드가 적용됨.

하위 디렉토리에서도 -j 옵션 전달됨

make -j8

➡ lib/, src/, bin/ 내부에서도 make -j8이 적용되어 속도가 더욱 빨라짐.


4. 병렬 빌드의 주의점과 최적화 방법

📌 4-1. 병렬 빌드 시 발생할 수 있는 문제

의존성 문제

  • 여러 개의 프로세스가 동시에 실행되므로, 의존성이 올바르게 설정되지 않으면 빌드가 실패할 수 있음.

링킹 문제

  • .o 파일이 아직 생성되지 않았는데 gcc -o가 먼저 실행될 경우 에러 발생 가능.

📌 4-2. 병렬 빌드 최적화 방법

올바른 의존성 설정

  • all 빌드 타겟이 실행되기 전에 모든 .o 파일이 먼저 생성되도록 의존성을 명확하게 설정해야 함.
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

디렉토리별 병렬 빌드 적용 ($(MAKE) -C)

  • 하위 디렉토리에서 개별적으로 병렬 빌드 수행 가능
all:
	$(MAKE) -j$(nproc) -C src
	$(MAKE) -j$(nproc) -C lib

최적의 -j 값 선택

  • 시스템의 CPU 개수(nproc 또는 sysctl -n hw.ncpu)를 기반으로 병렬 빌드 수행.
make -j$(nproc)

➡ 시스템의 CPU 개수에 맞춰 최적의 성능을 자동으로 조정.


📌 4-2. 병렬 빌드 요약 정리

개념  설명
make -jN N개의 프로세스를 사용하여 병렬 빌드 수행
$(MAKE) 하위 디렉토리에서도 make 실행 시 -j 옵션을 유지
의존성 문제 해결 타겟 의존성을 올바르게 설정해야 병렬 빌드 시 에러 방지
최적의 -j 값 설정 make -j$(nproc)을 사용하여 CPU 개수에 맞춰 빌드