C++ Execution Policy | "실행 정책" 가이드

C++ Execution Policy | "실행 정책" 가이드

이 글의 핵심

C++ Execution Policy에 대해 정리한 개발 블로그 글입니다. #include <algorithm> #include <execution> #include <vector>

Execution Policy란?

알고리즘 실행 방식 (C++17)

#include <algorithm>
#include <execution>
#include <vector>

std::vector<int> v = {3, 1, 4, 1, 5};

// 순차
std::sort(std::execution::seq, v.begin(), v.end());

// 병렬
std::sort(std::execution::par, v.begin(), v.end());

// 병렬 + 벡터화
std::sort(std::execution::par_unseq, v.begin(), v.end());

실행 정책 종류

#include <execution>

// sequenced_policy: 순차
std::execution::seq

// parallel_policy: 병렬
std::execution::par

// parallel_unsequenced_policy: 병렬 + 벡터화
std::execution::par_unseq

// unsequenced_policy: 벡터화 (C++20)
std::execution::unseq

실전 예시

예시 1: 병렬 정렬

#include <algorithm>
#include <execution>
#include <vector>
#include <chrono>

void benchmark() {
    std::vector<int> data(10000000);
    std::generate(data.begin(), data.end(), std::rand);
    
    // 순차
    auto v1 = data;
    auto start1 = std::chrono::steady_clock::now();
    std::sort(std::execution::seq, v1.begin(), v1.end());
    auto end1 = std::chrono::steady_clock::now();
    
    // 병렬
    auto v2 = data;
    auto start2 = std::chrono::steady_clock::now();
    std::sort(std::execution::par, v2.begin(), v2.end());
    auto end2 = std::chrono::steady_clock::now();
    
    auto time1 = std::chrono::duration_cast<std::chrono::milliseconds>(end1 - start1);
    auto time2 = std::chrono::duration_cast<std::chrono::milliseconds>(end2 - start2);
    
    std::cout << "순차: " << time1.count() << "ms" << std::endl;
    std::cout << "병렬: " << time2.count() << "ms" << std::endl;
}

예시 2: 병렬 변환

#include <algorithm>
#include <execution>

int main() {
    std::vector<int> v(1000000);
    std::iota(v.begin(), v.end(), 1);
    
    // 병렬 변환
    std::transform(std::execution::par, v.begin(), v.end(), v.begin(),
         { return x * x; });
}

예시 3: 병렬 집계

#include <numeric>
#include <execution>

int main() {
    std::vector<int> v(10000000, 1);
    
    // 병렬 reduce
    int sum = std::reduce(std::execution::par, v.begin(), v.end(), 0);
    
    std::cout << "합: " << sum << std::endl;
}

예시 4: 조건부 병렬

#include <algorithm>
#include <execution>

template<typename T>
void conditionalSort(std::vector<T>& v, bool parallel = true) {
    if (parallel && v.size() > 10000) {
        std::sort(std::execution::par, v.begin(), v.end());
    } else {
        std::sort(v.begin(), v.end());
    }
}

정책 선택

// seq: 순차 (기본)
// - 단일 스레드
// - 예측 가능

// par: 병렬
// - 멀티 스레드
// - 데이터 레이스 주의

// par_unseq: 병렬 + 벡터화
// - SIMD + 멀티 스레드
// - 동기화 불가

자주 발생하는 문제

문제 1: 데이터 레이스

int counter = 0;

std::vector<int> v(1000);

// ❌ 데이터 레이스
std::for_each(std::execution::par, v.begin(), v.end(), [&](int x) {
    ++counter;  // 레이스
});

// ✅ atomic
std::atomic<int> counter{0};
std::for_each(std::execution::par, v.begin(), v.end(), [&](int x) {
    ++counter;
});

문제 2: 동기화

std::mutex mtx;

// ❌ par_unseq에서 뮤텍스
std::for_each(std::execution::par_unseq, v.begin(), v.end(), [&](int x) {
    std::lock_guard lock{mtx};  // 정의되지 않은 동작
    // ...
});

// ✅ par에서 뮤텍스
std::for_each(std::execution::par, v.begin(), v.end(), [&](int x) {
    std::lock_guard lock{mtx};
    // ...
});

문제 3: 오버헤드

std::vector<int> small(100);

// ❌ 작은 데이터에 병렬
std::sort(std::execution::par, small.begin(), small.end());
// 오버헤드 > 이득

// ✅ 큰 데이터에 병렬
std::vector<int> large(10000000);
std::sort(std::execution::par, large.begin(), large.end());

문제 4: 예외

// 병렬 실행 중 예외
try {
    std::for_each(std::execution::par, v.begin(), v.end(),  {
        if (x < 0) {
            throw std::runtime_error("음수");
        }
    });
} catch (...) {
    // 여러 예외 가능
    // std::terminate() 호출 가능
}

지원 알고리즘

// 대부분의 STL 알고리즘 지원
std::sort(policy, begin, end)
std::transform(policy, begin, end, out, func)
std::for_each(policy, begin, end, func)
std::reduce(policy, begin, end, init)
std::find(policy, begin, end, value)
// ...

FAQ

Q1: Execution Policy?

A: 알고리즘 실행 방식 (C++17).

Q2: 종류?

A: seq, par, par_unseq.

Q3: 병렬 조건?

A:

  • 큰 데이터
  • 독립적 연산
  • 데이터 레이스 없음

Q4: 동기화?

A: par_unseq 불가. par 가능.

Q5: 성능?

A: 큰 데이터에서 효과.

Q6: 학습 리소스는?

A:

  • “C++17 The Complete Guide”
  • “C++ Concurrency in Action”
  • cppreference.com

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ Parallel Algorithms | “병렬 알고리즘” 가이드
  • C++ path | “경로 처리” 가이드
  • C++ Policy-Based Design | “정책 기반 설계” 가이드

관련 글

  • C++ Parallel Algorithms |
  • C++ any |
  • 모던 C++ (C++11~C++20) 핵심 문법 치트시트 | 현업에서 자주 쓰는 한눈에 보기
  • C++ CTAD |
  • C++ string vs string_view |