Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 11 – Iterators, algorithms and ranges

C++ parallel algorithms and execution policies

Run standard C++ algorithms in parallel with execution policies, build them with GCC and TBB or Apple clang, and write element functions without data races.

  • Advanced
  • 20 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: A policy permits threads, interleaving, both or neither · Without TBB, GCC runs par on one thread and says nothing · A parallel sort pays only where the library runs it in parallel · Practise: policies, backends and timings. With a pass: 3 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Run standard algorithms in parallel with the four execution policies
  • Build parallel algorithm code with GCC and TBB, and with Apple clang
  • Prevent data races in the functions passed to parallel algorithms
  • Compare the timings of execution policies within one toolchain, with the load they ran under

Before you start

Adding std::execution::par to a std::sort of 10 million integers cut its time from 1,463 ms to 605 ms with Apple clang 21, and gained nothing with GCC 16, whose library ran the call on a single thread. The C++ parallel algorithms are the familiar algorithms called with such a policy. std::sort, std::for_each, std::transform_reduce and most other algorithms of <algorithm> and <numeric> accept one of the execution policy: An object of <execution>, such as std::execution::par, passed as the first argument of a standard algorithm to say which kinds of concurrency it may use: other threads, interleaved calls on one thread, both or neither. It permits parallelism; it does not force it. of <execution>: seq, par, par_unseq and, since C++20, unseq.

A policy is a permission, not a command. The library may still run everything on the calling thread, and the functions you pass must be safe to run at the same time as each other. GCC’s libstdc++ runs par on the TBB library and needs -ltbb, and without TBB it quietly runs on one thread, while Apple clang’s libc++ needs -fexperimental-library and runs the work on Apple’s libdispatch.

A policy permits threads, interleaving, both or neither

Execution policies tell an algorithm which kinds of concurrency it may use. The four differ in two permissions: whether the calls may run on other threads, and whether calls on one thread may be interleaved with each other, which is what vector instructions need:

Policy Several threads Interleaved calls
seq, C++17 no no
par, C++17 yes no
par_unseq, C++17 yes yes
unseq, C++20 no yes
  • seq runs the calls one after another on the calling thread, like the algorithm without a policy.
  • par may spread the calls over several threads, so they must not race with each other, but they may take a lock.
  • par_unseq may also interleave calls on one thread, so the function must not lock a mutex or wait for another call.
  • unseq interleaves calls on the calling thread only: vector instructions without extra threads.

Locks are therefore allowed with seq and par only.

Two rules hold whatever the policy. With par or par_unseq, two calls that access the same memory, at least one of them writing, without synchronisation, are a Two accesses to the same memory from different threads, at least one of them a write, with no synchronisation between them. In C++ a data race is undefined behaviour: not merely a wrong result, but no guarantee about the program at all., which is undefined behaviour: What a program does when it breaks a rule for which the C++ standard sets no requirements, such as reading past the end of an array. The compiler may assume it never happens, so the result can change with the compiler and the optimisation level. More in “Object lifetime and undefined behaviour in C++”. And with any policy, an exception that leaves one of the calls ends the program through std::terminate: The function C++ calls when exception handling cannot go on, for example when no handler matches a throw or an exception tries to leave a noexcept function. By default it calls std::abort, which ends the program with SIGABRT. More in “C++ exceptions: throw, catch and stack unwinding”, instead of reaching your catch. Answer three questions about your function, and the table rules out the policies it may not use:

Interactive · answer and compare Which policy may your element function use? Answer three questions about the function you pass to the algorithm. A policy it cannot use is ruled out with the reason.
Does the function lock a mutex or wait for another call?
Do the calls write the same memory without synchronisation, such as one shared counter?
Can the function throw an exception?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

The four execution policies against threads, vector instructions, locks and writes to shared memory
Policy Other threads Interleaved calls Function may lock Unsynchronised shared writes
std::execution::seq Poor no: every call on the calling thread Poor no: one call after another Good yes Good allowed: calls never overlap
std::execution::par Good yes: calls may spread over threads Poor no: calls on one thread stay in order Good yes, if they cannot deadlock Poor a data race: undefined behaviour
std::execution::par_unseq Good yes: calls may spread over threads Good yes: calls on one thread may interleave Poor no: interleaved calls can deadlock Poor a data race: undefined behaviour
std::execution::unseq Poor no: the calling thread only (C++20) Good yes: calls may interleave Poor no: interleaved calls can deadlock Poor unsequenced writes: undefined behaviour
How the answers decide
  • “Does the function lock a mutex or wait for another call?” Yes, it locks or waits: rules out std::execution::par_unseq and std::execution::unseq. These policies may interleave calls on one thread, so one call could wait for a lock that another call on the same thread holds.
  • “Do the calls write the same memory without synchronisation, such as one shared counter?” Yes, they share it: rules out std::execution::par, std::execution::par_unseq and std::execution::unseq. Calls that may overlap or interleave turn those writes into a data race or unsequenced writes, undefined behaviour either way; only seq runs one call at a time.
  • “Can the function throw an exception?” Yes, it may throw: adds a caveat to std::execution::seq, std::execution::par, std::execution::par_unseq and std::execution::unseq. An exception that leaves a call ends the program through std::terminate under every policy, instead of reaching your catch.
  • “Does the function lock a mutex or wait for another call?” No, it never waits and “Do the calls write the same memory without synchronisation, such as one shared counter?” No, each call writes its own element: Independent calls may use any policy; par_unseq permits the most, threads and interleaving together, and the library still decides what it uses.

Checkpoint

Check: What each execution policy permits

  1. Question 1 of 2 The function you pass to std::for_each locks a std::mutex. Which policies may run it?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • std::execution::par
    • std::execution::seq

    Locks are allowed with seq and par only: the two policies that permit interleaving calls on one thread, par_unseq and unseq, also forbid waiting for another call.

  2. Question 2 of 2 An exception leaves the element function of a std::for_each called with std::execution::seq. What happens?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The program ends through std::terminate, and the caller's catch never runs

    The rule holds for every policy: an exception that leaves one of the calls ends the program through std::terminate instead of reaching your catch.

Without TBB, GCC runs par on one thread and says nothing

Where a call with std::execution::par ends up depends on the toolchain:

What std::sort with std::execution::par becomes: TBB threads or one thread with GCC 16, an error or libdispatch threads with Apple clang 21.std::sort(std::execution::par, ...)GCC 16 with libstdc++TBB found, linked with -ltbb→ runs on TBB's worker threadsTBB not found→ serial backend: one threadApple clang 21 with libc++without -fexperimental-library→ does not compilewith -fexperimental-library→ runs on libdispatch threads

Where a call with std::execution::par ends up on each toolchain

Text description of the diagram

The figure starts from the call std::sort(std::execution::par, ...) and shows what each toolchain does with it.

GCC 16 with libstdc++: 1. If the headers of TBB are found and the program is linked with -ltbb, the calls run on TBB's worker threads. 2. If TBB is not found, libstdc++ uses its serial backend, and every call runs on one thread, without any warning.

Apple clang 21 with libc++: 1. Without -fexperimental-library, the program does not compile, because std::execution::par does not exist. 2. With -fexperimental-library, the calls run on the worker threads of libdispatch, Apple's system thread pool.

This program asks each library which backend runs its parallel algorithms, through macros that are the libraries’ own internal names rather than standard ones, and counts the threads that ran the calls of one for_each over a million elements:

backend.cpp C++ · backend.cpp
// What runs the parallel algorithms of this standard library, and how many threads one parallel for_each used.
// The backend macros are each library's own internal names, not standard ones; the thread count is measured.
#include <version>
#include <algorithm>
#include <execution>
#include <print>
#include <thread>
#include <vector>

int main() {
#if defined(_PSTL_PAR_BACKEND_TBB)
    std::println("libstdc++, backend: Intel TBB");
#elif defined(_PSTL_PAR_BACKEND_SERIAL)
    std::println("libstdc++, backend: serial (TBB was not found)");
#elif defined(_LIBCPP_PSTL_BACKEND_LIBDISPATCH)
    std::println("libc++, backend: libdispatch");
#elif defined(_LIBCPP_PSTL_BACKEND_STD_THREAD)
    std::println("libc++, backend: std::thread");
#elif defined(_LIBCPP_PSTL_BACKEND_SERIAL)
    std::println("libc++, backend: serial");
#endif
#ifdef __cpp_lib_parallel_algorithm
    std::println("__cpp_lib_parallel_algorithm is {}", __cpp_lib_parallel_algorithm);
#else
    std::println("__cpp_lib_parallel_algorithm is not defined");
#endif
    // Each call records the thread that ran it in its own element, so no two calls write the same memory.
    std::vector<std::thread::id> ran_on(1'000'000);
    std::for_each(std::execution::par, ran_on.begin(), ran_on.end(),
                  [](std::thread::id& slot) { slot = std::this_thread::get_id(); });
    std::ranges::sort(ran_on);
    const auto threads = std::ranges::distance(ran_on.begin(), std::ranges::unique(ran_on).begin());
    std::println("threads that ran the calls: {}", threads);
}
GCC 16 without TBB, Apple clang 21 without and with the flag Python · backend_session.py
from session import run

# GCC 16 without TBB, then Apple clang 21 without and with -fexperimental-library.
run("""
g++-16 -std=c++23 -O2 backend.cpp -o backend && ./backend
clang++ -std=c++23 -O2 backend.cpp -o backend 2>&1 | grep 'error:'
clang++ -std=c++23 -O2 -fexperimental-library backend.cpp -o backend && ./backend
""")

Output

$ g++-16 -std=c++23 -O2 backend.cpp -o backend && ./backend
libstdc++, backend: serial (TBB was not found)
__cpp_lib_parallel_algorithm is 201603
threads that ran the calls: 1
$ clang++ -std=c++23 -O2 backend.cpp -o backend 2>&1 | grep 'error:'
backend.cpp:29:35: error: no member named 'par' in namespace 'std::execution'
$ clang++ -std=c++23 -O2 -fexperimental-library backend.cpp -o backend && ./backend
libc++, backend: libdispatch
__cpp_lib_parallel_algorithm is not defined
threads that ran the calls: 6

This output changes from run to run: libdispatch decides at run time how many of its threads take part, so the last count changes from run to run.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 backend_session.py

GCC 16 compiled the file, defined __cpp_lib_parallel_algorithm, and ran every call on one thread. Without the headers of TBB, libstdc++ falls back to its serial backend, and neither the compiler nor the linker says so. The libstdc++ manual states the dependency: the parallel algorithms need TBB 2018 or newer, linked with -ltbb. Today that is oneTBB, the libtbb-dev package on Debian and Ubuntu and the tbb formula of Homebrew on macOS.

Apple clang 21 rejected std::execution::par until -fexperimental-library was given, because libc++ still treats its parallel algorithms as experimental, and with the flag it ran the calls on 6 threads of libdispatch, Apple’s system thread pool. It defines no feature-test macro for them, so #ifdef __cpp_lib_parallel_algorithm says no on the toolchain that runs in parallel and yes on the one that does not. Count threads, as this program does, when you need to know: the C++ parallel algorithms give no sign of their own when they run on one thread. Apple’s linker may also warn that libc++experimental.a “was built for newer macOS version” than the program; the warning is harmless, and the sessions on this page leave it out.

Key idea

A policy only permits parallelism: count the threads that ran the calls, because a toolchain without its backend runs on one thread without a word.

Checkpoint

Check: What runs par on each toolchain

  1. Question 1 of 2 How many threads ran the calls of the parallel for_each in the GCC 16 build without TBB?

    Type a number.

    Show the answer: Question 1 of 2

    Answer: 1 thread

    Without the headers of TBB, libstdc++ falls back to its serial backend and runs every call on the calling thread, and neither the compiler nor the linker says so: the program printed threads that ran the calls: 1.

  2. Question 2 of 2 Why is #ifdef __cpp_lib_parallel_algorithm a poor test of whether par really runs in parallel?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It said yes on GCC 16, which ran on one thread, and no on Apple clang 21, which ran on several

    The macro and the parallelism disagreed on both toolchains. Count the threads that ran the calls, as backend.cpp does, when you need to know what you got.

A parallel sort pays only where the library runs it in parallel

sort_time.cpp C++ · sort_time.cpp
// Sorting 10 million random integers with each execution policy: the median of 3 runs of each, in milliseconds.
#include <algorithm>
#include <chrono>
#include <cstdlib>  // getloadavg: provided by macOS and glibc, not by standard C++ or POSIX
#include <execution>
#include <print>
#include <random>
#include <thread>
#include <vector>

// Times one sort of a copy of data with the given policy, in milliseconds.
template <class Policy>
double sort_ms(const Policy& policy, const std::vector<int>& data) {
    std::vector<int> v = data;  // the same unsorted input every time
    const auto start = std::chrono::steady_clock::now();
    std::sort(policy, v.begin(), v.end());
    const auto stop = std::chrono::steady_clock::now();
    if (!std::ranges::is_sorted(v)) std::abort();
    return std::chrono::duration<double, std::milli>(stop - start).count();
}

double median(std::vector<double> times) {
    std::ranges::sort(times);
    return times[times.size() / 2];
}

int main() {
    std::mt19937 gen(42);
    std::vector<int> data(10'000'000);
    for (int& x : data) x = static_cast<int>(gen());
    double load = 0;
    getloadavg(&load, 1);
    std::println("{} hardware threads, load average {:.0f}", std::thread::hardware_concurrency(), load);
    // Three rounds, each timing the three policies in turn, so that a slower or faster moment of the computer
    // affects all three alike.
    std::vector<double> seq_ms, par_ms, par_unseq_ms;
    for (int round = 0; round < 3; ++round) {
        seq_ms.push_back(sort_ms(std::execution::seq, data));
        par_ms.push_back(sort_ms(std::execution::par, data));
        par_unseq_ms.push_back(sort_ms(std::execution::par_unseq, data));
    }
    const double seq = median(seq_ms), par = median(par_ms), par_unseq = median(par_unseq_ms);
    std::println("seq        {:5.0f} ms", seq);
    std::println("par        {:5.0f} ms   {:.1f}x", par, seq / par);
    std::println("par_unseq  {:5.0f} ms   {:.1f}x", par_unseq, seq / par_unseq);
}
10 million integers, three policies, two toolchains Python · sort_time_session.py
from session import run

run("""
g++-16 -std=c++23 -O2 sort_time.cpp -o sort_time && ./sort_time
clang++ -std=c++23 -O2 -fexperimental-library sort_time.cpp -o sort_time && ./sort_time
""")

Output

$ g++-16 -std=c++23 -O2 sort_time.cpp -o sort_time && ./sort_time
18 hardware threads, load average 48
seq         3035 ms
par         3637 ms   0.8x
par_unseq   4178 ms   0.7x
$ clang++ -std=c++23 -O2 -fexperimental-library sort_time.cpp -o sort_time && ./sort_time
18 hardware threads, load average 52
seq         1463 ms
par          605 ms   2.4x
par_unseq    672 ms   2.2x

This output changes from run to run: Times depend on what else the computer is doing, so only the shape of the output is fixed.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 sort_time_session.py

GCC 16’s three policies took 3,035, 3,637 and 4,178 ms. Without TBB, the parallel sort of libstdc++’s serial backend calls the same sequential std::sort, so the three lines measure one algorithm three times, and their differences are not parallel work. Apple clang’s par sorted the same 10 million integers 2.4 times faster than its seq, and par_unseq 2.2 times faster, at a load average of 52: other programs wanted more threads than the machine’s 18 hardware threads, so this speed-up is what a busy machine gives, not the best case. The libraries also differ before any policy is involved: libc++’s sequential sort took less than half the time of libstdc++’s, so compare policies within one toolchain, never across two.

Line Graph Maker Plot your own timings against the input size to see where a parallel version starts to pay.

Checkpoint

Check: Timing a parallel sort

  1. Question 1 of 2 Apple clang's seq sort took 1,463 ms and its par sort 605 ms. What speed-up is that, to one decimal place?

    Type a number.

    A hint

    Divide the sequential time by the parallel one.

    Show the answer: Question 1 of 2

    Answer: 2.4 times (anything from 2.35 to 2.45 counts)

    1,463 ÷ 605 = 2.42, which the program printed as 2.4x, measured at a load average of 52 on 18 hardware threads.

  2. Question 2 of 2 libc++'s sequential sort took 1,463 ms and libstdc++'s 3,035 ms for the same 10 million integers. What may you conclude about the policies?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Nothing across the two; compare policies within one toolchain, because the libraries differ before any policy is involved

    Each library has its own sequential sort, and libc++'s took less than half the time of libstdc++'s, so the effect of a policy is the ratio within one toolchain.

Practise: policies, backends and timings

Try the policies, the two backends and the sort timings, one question at a time; every choice tells you why.

Practice that adapts to you

Practise this lesson

10 questions about 9 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 What does std::execution::unseq, added in C++20, permit?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: Interleaving the calls on the calling thread only, as vector instructions need, without extra threads

    The four policies differ in two permissions, other threads and interleaved calls: seq neither, par threads, unseq interleaving, par_unseq both.

  2. Question 2 of 10 Why may a function run under par_unseq not lock a mutex?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: Calls on one thread may be interleaved, so one call could wait for a lock that another call on the same thread holds

    par_unseq and unseq permit interleaving several calls on one thread, so a function must not lock a mutex or wait for another call; use par for work that needs synchronisation.

  3. Question 3 of 10 Must the library run a std::sort called with std::execution::par on several threads?

    Choose one answer.

    Show the answer: Question 3 of 10

    Answer: No; a policy is a permission, and the library may still run everything on the calling thread

    An execution policy says which kinds of concurrency an algorithm may use. What it does use depends on the library and its backend.

  4. Question 4 of 10 A parallel for_each with par increments a plain int that all its calls share. Which statement is true?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: It is a data race, so the behaviour is undefined

    With par or par_unseq, two calls that access the same memory, at least one of them writing, without synchronisation, are a data race, which is undefined behaviour.

  5. Question 5 of 10 What does GCC 16's libstdc++ need to run its parallel algorithms on several threads?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: The TBB library, 2018 or newer, linked with -ltbb

    libstdc++ runs par on TBB when its headers are found and the program is linked with -ltbb; otherwise it uses its serial backend.

  6. Question 6 of 10 What did Apple clang 21 do with std::execution::par when -fexperimental-library was not given?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: It stopped with an error, no member named par in namespace std::execution

    With the flag, the same file ran the calls on libdispatch, Apple's system thread pool. Apple's linker may also warn that libc++experimental.a was built for a newer macOS version; that warning is harmless.

  7. Question 7 of 10 Your Linux build uses GCC. How do you check whether its par calls really run in parallel?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: Count the distinct threads that run the calls of a parallel algorithm, as backend.cpp does

    The C++ parallel algorithms give no sign of their own when they run on one thread, so measure what you got: the number of threads that ran the calls.

  8. Question 8 of 10 GCC 16's seq, par and par_unseq sorts took 3,035, 3,637 and 4,178 ms. What do the differences between them measure?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: Not parallel work; without TBB the serial backend runs the same sequential sort three times

    Without TBB, libstdc++'s parallel sort calls the sequential std::sort, so the three timings measure one algorithm three times on a busy machine.

  9. Question 9 of 10 Apple clang's 2.4x was measured at a load average of 52 on a machine with 18 hardware threads. What does that mean for the number?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: Other programs wanted more threads than the machine had, so it is what a busy machine gives, not the best case

    A speed-up depends on the cores that are idle while it runs, which is why the program prints the load average next to its times.

  10. Question 10 of 10 Why does sort_time.cpp time the three policies in turn, in three rounds, and report the median of each?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: So that a slower or faster moment of the computer affects all three alike

    Timing each policy once, all in a row, would let one busy moment hit one policy only; rounds and a median make the comparison fairer.

Data races and exceptions in element functions, reproduced Pro: opens with a Pro, Premium or Ultimate pass

Most bugs in parallel algorithm code come from the function passed to the algorithm, not from the algorithm itself. This part reproduces the two classic ones, a shared counter and an exception, and shows the fixes and the tool that finds the first.

In this part:

  • A shared counter, counted wrong, and two ways to count right
  • Reading the ThreadSanitizer report
  • An exception under any policy ends the program

5 runnable examples (C++, Python) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

When par pays, measured: input size, work per element and cores Pro: opens with a Pro, Premium or Ultimate pass

A parallel algorithm pays only when each thread gets enough work to outweigh the cost of starting and joining it. This part measures where that happens for light and heavy work on this 18-thread machine, and turns the results into rules of thumb with numbers.

In this part:

  • Light and heavy work at five input sizes
  • Worked numbers: what limits the speed-up

1 diagram · 2 runnable examples (C++, Python) · about 6 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

The histogram exercise, four ways, measured and checked Pro: opens with a Pro, Premium or Ultimate pass

A histogram is the smallest program that shows every trade-off of parallel algorithms at once: shared state, atomics, contention and reductions. This part solves the exercise four ways, times each version and checks the correct ones with ThreadSanitizer.

In this part:

  • Your turn: parallelise a histogram
  • Four versions
  • Measured, and checked with ThreadSanitizer

2 runnable examples (C++, Python) · about 4 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher to mid · Warm-up

    What is the difference between std::execution::par and std::execution::par_unseq, and when may you use each?

    Both let a standard algorithm run its calls on several threads. With par, the calls on any one thread still run one after another, so the function may take a lock, as long as it avoids data races and deadlocks. With par_unseq, the library may also interleave several calls on one thread, as vector instructions do, so the function must not lock a mutex or wait for another call: one interleaved call could wait for a lock that another holds on the same thread. Use par for work that needs synchronisation, and par_unseq for independent arithmetic on each element, such as a transform over numbers. Both permit parallelism without forcing it, and with either one, an exception that leaves the function ends the program with std::terminate.

  2. Mid to senior

    You changed a hot std::sort to std::execution::par. A colleague's Mac build sorts 2.4 times faster, but the Linux service built with GCC shows no gain at all. What do you check, in what order, and how do you stop it happening again?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A parallel for_each builds a histogram by incrementing counters in a shared array, and the totals come out too low. Fix it, and compare the possible fixes for correctness and speed on an 18-core machine.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A batch job validates records with std::for_each(std::execution::par, …), and the validator throws on a bad record. In testing, the job sometimes dies in std::terminate instead of reporting the record. Explain the behaviour and redesign the error handling.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • An execution policy (seq, par, par_unseq, unseq) is the first argument that lets a standard algorithm run in parallel, vectorised, or both; it permits, it does not force.
  • With par and par_unseq, the function you pass must not race on shared data; with par_unseq and unseq it must not lock or wait either.
  • An exception that leaves an element function ends the program with std::terminate, under every policy.
  • GCC’s libstdc++ needs TBB and -ltbb for real parallelism and silently runs on one thread without it, while Apple clang’s libc++ needs -fexperimental-library, so count the threads to know what you got.
  • Compare policies within one toolchain and report the machine’s load, because the two libraries’ sequential sorts already differ by a factor of two.

Terms in this lesson

execution policy
An object of <execution>, such as std::execution::par, passed as the first argument of a standard algorithm to say which kinds of concurrency it may use: other threads, interleaved calls on one thread, both or neither. It permits parallelism; it does not force it.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.