Senders - A Standard Model for Asynchronous Execution in C++
stdexec
is an experimental reference implementation of the Senders model of asynchronous programming proposed by P2300 - std::execution
for adoption into the C++ Standard.
Purpose of this Repository:
- Provide a proof-of-concept implementation of the design proposed in P2300.
- Provide early access to developers looking to experiment with the Sender model.
- Collaborate with those interested in participating or contributing to the design of P2300 (contributions welcome!).
Disclaimer
stdexec
is experimental in nature and subject to change without warning.
The authors and NVIDIA do not guarantee that this code is fit for any purpose whatsoever.
Example
Below is a simple program that executes three senders concurrently on a thread pool. Try it live on godbolt!.
#include <stdexec/execution.hpp>
#include <exec/static_thread_pool.hpp>
int main()
{
// Declare a pool of 3 worker threads:
exec::static_thread_pool pool(3);
// Get a handle to the thread pool:
auto sched = pool.get_scheduler();
// Describe some work:
// Creates 3 sender pipelines that are executed concurrently by passing to `when_all`
// Each sender is scheduled on `sched` using `on` and starts with `just(n)` that creates a
// Sender that just forwards `n` to the next sender.
// After `just(n)`, we chain `then(fun)` which invokes `fun` using the value provided from `just()`
// Note: No work actually happens here. Everything is lazy and `work` is just an object that statically
// represents the work to later be executed
auto fun = [](int i) { return i*i; };
auto work = stdexec::when_all(
stdexec::on(sched, stdexec::just(0) | stdexec::then(fun)),
stdexec::on(sched, stdexec::just(1) | stdexec::then(fun)),
stdexec::on(sched, stdexec::just(2) | stdexec::then(fun))
);
// Launch the work and wait for the result
auto [i, j, k] = stdexec::sync_wait(std::move(work)).value();
// Print the results:
std::printf("%d %d %d\n", i, j, k);
}
Resources
- Working with Asynchrony Generically: A Tour of Executors: Part 1 (Part 2) (Video): A comprehensive introduction to Senders and structured concurrency
- From Zero to Sender/Receiver in ~60 Minutes (Video): Live-coding a toy sender/receiver implementation from scratch
- A Unifying Abstraction for Async in C++ (Video): A simple introduction to the concepts behind P2300
- A Universal Async Abstraction for C++ (Blog): An introduction to Senders
- A Universal I/O Abstraction for C++ (Blog): A look at how the Senders concepts interact with
io_uring
on Linux - Structured Concurrency (Video): An explanation of structured concurrency in C++ and its benefits
- Executors: a Change of Perspective (Article): An article about the computational completeness of Senders
- Structured Concurrency in C++ (Article): An article about how Senders manifest the principles of structured concurrency
- Structured Networking in C++ (Video): A look at what a P2300-style networking library could look like
- HPCWire Article: Provides a high-level overview of the Sender model and its benefits
- NVIDIA HPC SDK Documentation: Documentation for the NVIDIA HPC SDK
- P2300 -
std::execution
: Senders proposal to C++ Standard
Structure
This library is header-only, so all the source code can be found in the include/
directory. The physical and logical structure of the code can be summarized by the following table:
Kind | Path | Namespace |
---|---|---|
Things approved for the C++ standard | <stdexec/...> |
::stdexec |
Generic additions and extensions | <exec/...> |
::exec |
NVIDIA-specific extensions and customizations | <nvexec/...> |
::nvexec |
stdexec
How to get There are a few ways to get stdexec
:
- Clone from GitHub
git clone https://github.com/NVIDIA/stdexec.git
- Download the NVIDIA HPC SDK starting with 22.11
- (Recommended) Use CMake Package Manager (CPM) to automatically pull
stdexec
as part of your CMake project. See below for more information.
You can also try it directly on godbolt.org where it is available as a C++ library or via the nvc++ compiler starting with version 22.11 (see below for more details).
stdexec
Using Requirements
stdexec
requires compiling with C++20 (-std=c++20
) but otherwise does not have any dependencies and only requires a sufficiently new compiler:
- clang 12+
- gcc 11+
- nvc++ 22.11+ (required for GPU support)
How you configure your environment to use stdexec
depends on how you got stdexec
.
NVHPC SDK
Starting with the 22.11 release of the NVHPC SDK, stdexec
is available as an experimental, opt-in feature. Specifying the --experimental-stdpar
flag to nvc++
makes the stdexec
headers available on the include path. You can then include any stdexec
header as normal: #include <stdexec/...>
, #include <nvexec/...>
. See godbolt example.
GPU features additionally require specifying -stdpar=gpu
. For more details, see GPU Support.
GitHub
As a header-only C++ library, technically all one needs to do is add the stdexec
include/
directory to your include path as -I<stdexec root>/include
in addition to specifying any necessary compile options.
For simplicity, we recommend using the CMake targets that stdexec
provides as they encapsulate the necessary configuration.
cmake
If your project uses CMake, then after cloning stdexec
simply add the following to your CMakeLists.txt
:
add_subdirectory(<stdexec root>)
This will make the stdexec::stdexec
target available to link with your project:
target_link_libraries(my_project PRIVATE stdexec::stdexec)
This target encapsulates all of the necessary configuration and compiler flags for using stdexec
.
CMake Package Manager (CPM)
To further simplify obtaining and including stdexec
in your CMake project, we recommend using CMake Package Manager (CPM) to fetch and configure stdexec
.
Complete example:
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(stdexecExample)
# Get CPM
# For more information on how to add CPM to your project, see: https://github.com/cpm-cmake/CPM.cmake#adding-cpm
include(CPM.cmake)
CPMAddPackage(
NAME stdexec
GITHUB_REPOSITORY NVIDIA/stdexec
GIT_TAG main # This will always pull the latest code from the `main` branch. You may also use a specific release version or tag
)
add_executable(main example.cpp)
target_link_libraries(main stdexec::stdexec)
GPU Support
stdexec
provides schedulers that enable execution on NVIDIA GPUs:
nvexec::stream_scheduler
- Single GPU scheduler that executes on the first available GPU (device 0)
- Defined in
<nvexec/stream_context.cuh>
nvexec::multi_gpu_stream_scheduler
- Executes on all visible GPUs
- Defined in
<nvexec/multi_gpu_context.cuh>
These schedulers are only supported when using the nvc++
compiler with -stdpar=gpu
.
Example: https://godbolt.org/z/4cEMqY8r9
Building
stdexec
is a header-only library and does not require building anything.
This section is only relevant if you wish to build the stdexec
tests or examples.
The following tools are needed:
CMake
- One of the following supported C++ compilers:
- GCC 11+
- clang 12+
- nvc++ 22.11
Perform the following actions:
# Configure the project
cmake -S . -B build -G<gen>
# Build the project
cmake --build build
Here, <gen>
can be Ninja
, "Unix Makefiles"
, XCode
, "Visual Studio 15 Win64"
, etc.
Specifying the compiler
You can set the C++ compiler via -D CMAKE_CXX_COMPILER
:
# Use GCC:
cmake -S . -B build/g++ -DCMAKE_CXX_COMPILER=$(which g++)
cmake --build build/g++
# Or clang:
cmake -S . -B build/clang++ -DCMAKE_CXX_COMPILER=$(which clang++)
cmake --build build/clang++
Specifying the stdlib
If you want to use libc++
with clang instead of libstdc++
, you can specify the standard library as follows:
# Do the actual build
cmake -S . -B build/clang++ -G<gen> \
-DCMAKE_CXX_FLAGS=-stdlib=libc++ \
-DCMAKE_CXX_COMPILER=$(which clang++)
cmake --build build/clang++
Tooling
For users of VSCode, stdexec provides a VSCode extension that colorizes compiler output. The highlighter recognizes the diagnostics generated by the stdexec library, styling them to make them easier to pick out. Details about how to configure the extension can be found here.