Post

std::execution

std::execution

std::execution


Prerequisites


1. What is std::execution? (C++17)

C++17 introduced execution policies through the <execution> header.

They allow standard algorithms (such as std::for_each, std::transform, std::sort) to control how the algorithm is executed:

  • sequentially
  • in parallel
  • or in a more aggressively optimized manner (parallel + vectorized)

2. Why std::execution exists?

Traditional STL algorithms define what to do, but not how to execute it.

1
std::for_each(v.begin(), v.end(), f);
  • Always sequential
  • Single thread
  • No control over execution strategy

With execution policies:

1
std::for_each(std::execution::par, v.begin(), v.end(), f);

You explicitly tell the algorithm:

“You are allowed to run this in parallel.”

3. Available Execution Policies

1
#include <execution>
PolicyDescription
std::execution::seqSequential execution
std::execution::parParallel (multi-threaded) execution
std::execution::par_unseqParallel + unsequenced (SIMD + reordering allowed)
Featureseqparpar_unseq
Threads1MultipleMultiple
SIMDNoOptionalYes (allowed)
Order GuaranteeYesNoNo
ReorderingNoLimitedFully allowed
DeterminismYesNoNo

3-1. std::execution::seq

1
std::for_each(std::execution::seq, v.begin(), v.end(), f);
  • Executes in order
  • Single thread
  • Same as default behavior
Characteristics
  • deterministic
  • easiest to debug
  • no concurrency

3-2. std::execution::par

1
std::for_each(std::execution::par, v.begin(), v.end(), f);
  • Executes using multiple threads
  • Work is split into chunks
  • Order is not guaranteed
Characteristics
  • multi-threaded
  • faster for large workloads
  • requires thread-safe operations

3-3. std::execution::par_unseq

1
std::for_each(std::execution::par_unseq, v.begin(), v.end(), f);
  • Parallel + vectorized execution
  • Allows compiler to reorder operations
  • Enables SIMD optimizations (automatically create SIMD operation on compiler)
Characteristics
  • maximum performance potential
  • no ordering guarantees at all
  • strongest constraints on safety

3-4. std::execution::par vs std::execution::par_unseq

1
2
3
std::for_each(std::execution::par, v.begin(), v.end(), [](int x) {
    work(x);
});
std::execution::par

thread A: work(v[0]) thread B: work(v[5]) thread C: work(v[2])

std::execution::par_unseq

thread A: create SIMD - < v[0], v[1], v[2], v[3] >

4. Critical Rules

No Data Races
1
2
3
4
5
6
int sum = 0;

std::for_each(std::execution::par, v.begin(), v.end(), [&](int x)
{
    sum += x;   // ❌ race condition
});
No Dependency Between Elements
1
2
// ❌ depends on previous element
v[i] = v[i - 1] + 1;
Avoid Shared Mutable State
1
2
3
// ❌ unsafe shared state
static int counter = 0;
counter++;
This post is licensed under CC BY 4.0 by the author.