C++ Papers for Issaquah - Concurrency

published at 28.01.2014 16:30 by Jens Weller

In february the C++ committee is going to meet for a week in Issaquah, it could be the final Meeting for C++14, the papers in this series will reflect both, C++14 and the standard that will come after it. Currently mostly known as C++1y. A lot of papers that are not part of C++14 will be formed into technical specifications, which some will then become C++1y later. Like the last series, I will again focus on the working groups, and post the papers sorted by the name of the working group, starting with concurrency. Also there is the previous review on concurrency.

Concurrency

N3831 - Language Extensions for Vector level parallelism

This paper deals with how to integrate SIMD Instructions into the C++ Standard. It combines techniques from Cilk Plus and OpenMP 4.0. The paper starts with describing the C++ constructs used, and which constraints apply to them (f.e. counted loops are for or ranged for loops). The paper proposes 3 main changes to the language:

array notations (in Part II, not yet included afaik)

SIMD Loops

SIMD Functions

A SIMD Loop is a simple for loop, which has the keyword simd added: for simd(...), the paper has no example code to clarify this. The authors plan to add simd as a new, context dependent keyword (like f.e. override), a SIMD enabled function could look like this:

void vec_add (float *r, float *op1, float *op2, int k) simd(uniform(r,op1,op2) linear(k:1)) simd{ r[k] = op1[k] + op2[k];}

The functions body is marked as simd and there is a block describing which variables have which role. This paper I think is a good step forward into getting SIMD into the standard, but its still at a very early stage.

N3832 - Task Region

This paper wants to introduce 3 new functions into the standard for better parallelism:

task_region

task_run

task_wait

These functions are inspired by similar concepts for task groups in PPL and TBB.

The paper builds up on paper N3711, which introduced the idea of task groups. The function task_region takes a callable, which can have multiple calls to task_run which will start a new task for its callable argument. In the examples the callables are mostly lambdas. A function calling task_run should only be called/executed from a call to task_region, otherwise it is supposed to be undefined behavior. This paper follows the ideas of fork-join parallelism such as Cilk or OpenMP, I don't like the idea of introducing undefined behavior if a certain function isn't executed by another one.

N3851 - Multidimensional bounds, index and array_view

This paper wants to integrate the ability to perform calculations on multidimensional data such as matrices or image processing into the standard. This paper builds up on ideas coming from Microsofts C++ AMP, where index and array_view are used to abstract access into various sets of data for parallel access. The paper models the types bounds, index and array_view for the C++ standard.

N3857 - Improvements to future<T> and related APIs

This paper is the follow up on N3784, dealing again with extending std::future and std::shared_future freestanding and memberfunctions.

Which are:

then

unwrap

is_ready

when_any / when_any_swapped / when_all

make_ready_future

The .then member function shall take a callable, which will be called with the resulting value of the future once its ready (calculated). In some cases, its useful to wrap a future into a future, unwrap lets you access the future inside the outer future. The authors claim that it is not easy to get this right for such nested futures (exception handling f.e.), such the standard should provide such functionality. The method bool is_ready lets the user query in a non blocking way if the future is ready. when_any, when_any_swapped and when_all represent freestanding functions, which have a range of futures as an argument, and return either when any future or all futures have been calculated. The function make_ready_future returns a future that is ready, and has the value given as an argument to it. Sometimes it is necessary to wrap a result into a future, so make_ready_future provides the corresponding make function.

N3858 - resumable functions

This paper is a follow up on N3722, new in this version are minor changes to the examples, explanatory text and a clearer section on generators. Resumable functions are functions, which are out of a multithreaded context resumable, and can wait for certain parallel function calls to be executed before execution continues. I have already written a whole blog post about resumable functions, and there was also a very good talk about resumable functions at GoingNative 2013.

N3865 - more improvements to std::future<T>

This paper aims at adding more member function to std::future (and std::shared_future). It build up on N3784, the proposed member functions are:

has_value

next

recover

fallback_to

The paper defines the member functions next and recover as future factories, next takes a callable which should be called with the result of the previous future, while recover takes a callable with the argument of an exception_ptr. The paper further defines that these functions behave like .then:

These functions behave like .then() but will call the continuation only when the future is ready with a value or an exception respectively. The continuations takes the future value type or an exception_ptr as parameter respectively. This has the advantage to make easier the continuation definition as is a lot of cases there is no need to protect the future<T>::get() operation against an exception thrown.

This implementation is without has_value not possible, also the user could test in this way if a future is ready with a value. In the case that there is a default value used in case of an exception, fallback_to produces a future which is set to this user defined value in case of an exception. The calls to next and recover can be chained (f.next(...).next(...).recover(...)). IMHO this is an interesting paper on further ideas which functionality futures should expose as a library solution.

N3872 - A primer on scheduling fork-join parallelism with work-stealing

This paper tries to define a basic foundation for work-stealing, so that later a proposal can refer to this paper. The author states, that because of this, he sees this paper as a primer, and not a proposal. Hence the title. This paper is a good introduction into the basics of work-stealing. It also shows how much work in the details of Concurrency and Parallelism is still ahead in the standardization of C++. Threads are just the beginning.

N3874 - Light-weight execution agents

Execution agents are a way to abstract Threads, not always is an OS thread needed, often it is enough to be able to only execute a task in parallel, without the overhead of starting a thread each time. The executor pattern is currently the preferred way to standardize something like threadpools for C++. This paper is now about light-weight execution agents, which are also known as tasks or threads in other papers.

The paper defines 4 different classes of execution agents:

concurrent execution

parallel execution

SIMD execution

parallel + SIMD execution

The paper goes on with further text about execution agents. There is no example implementation provided, this paper is trying to define what an light-weight execution agent should be.

N3885 - C++ Latches and Barriers

This paper is only listed, but not linked. The previous paper is N3817.

N3891 - A proposal to rename shared_mutex to shared_timed_mutex

Obviously this paper tries to improve the shared_mutex class, renaming things in the standard is always difficult, as it could (and mostly will) break a lot of code. The authors conclude 2 good reasons to rename shared_mutex to shared_timed_mutex, before it might become part of C++14:

for consistency with the other mutexes (fixing naming inconsistency) to leave room for a shared_mutex which can be more efficient on some platforms than shared_timed_mutex.

The paper builds up on N3797 and N3659.

N3892 - C++ OStream buffers

This paper wants to enhance the standard by providing an ostream buffer for synchrononizing streams. Such an implementation could be based on stream mutexes, stream guards or uninterleaved string output streaming.

N3896 - Library foundations for asynchronous operations

This paper tries to give the foundations for async operations through out the standard. Its main point is to work out when lightweight callbacks and when futures are the better solution, and how to integrate them into a library solution. This paper supersedes N3747, and adds a new section on executors and schedulers. The core of this document is what it describes as two different models of async operations: callbacks and futures. The callback approach is known from boost asio, where each async operation is handled by a callable handler. While a future is an object returned from the initiating function, the caller then may attach a continuation to that future via .then().

The paper states that futures can be a poor choice for implementing async operations, and that a pure callback based approach can have its advantages. The author presents an approach to a unified model, usable with both models, futures and callbacks. The callback oriented approach is known for example from boost::asio. In performance critical application such as finance, it can be that the user would like to switch the underlying model of asynchronous operations. Futures do have a little overhead, the callback based approach can be a few microseconds more performant. The author looks very detailed at both models, and then presents the universal approach introduced into boost::asio with boost 1.54, handling callbacks, futures and resumable functions or coroutines.

Read the second part, papers from Concepts, Database and Evolution.

Join the Meeting C++ patreon community!

This and other posts on Meeting C++ are enabled by my supporters on patreon!