Journal Articles

Overload Journal #147 - October 2018 + Programming Topics
Browse in : All > Journals > Overload > o147 (5)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: P1063 vs Coroutines TS: Consensus on High-Level Semantics

Author: Bob Schmidt

Date: 03 October 2018 18:16:41 +01:00 or Wed, 03 October 2018 18:16:41 +01:00

Summary: Dmytro Ivanchykhin, Sergey Ignatchenko and Maxim Blashchuk argue that we need coroutines TS now to improve-based-on-experience later.

Body: 

Disclaimer: This article takes for granted that readers understand what coroutines are about. If this concept is unfamiliar to you (hey, we’re speaking about standard proposals here!) make sure to take a look at [Nishanov15] and [McNellis16].

Disclaimer #2: Just to avoid any doubt, this article is not written with the help of some magical oracle or other source of infinite wisdom; rather, this article (just as any other article for that matter) merely represents an opinion of its authors (which may or may not coincide with the opinion of the Overload editor). In addition, this article is neither sanctioned nor sponsored by any government, WG21, or other official body.

Quite recently, we have learned that newly appeared [P1063R0]1 and its ‘Core Coroutines’ proposal has led to controversy, which got in the way of voting Coroutines TS [N4760] (a.k.a. Gor-routines ☺) into C++20. As big fans of coroutines in general and asynchronous processing in particular, we became worried about this development, so we took a look at this situation from the point of view of an app-level developer (and occasional architect). In other words, we do not really care about implementation details and compiler complexities – instead, we care about stuff such as readability, performance, backward compatibility and code maintenance costs; and of course, another extremely important consideration is when we’ll be able to start using those exciting new C++ features (without standardization we’re not really able to use any feature on a massive scale as the associated risks are just too high).

App-level point of view

From our app-level point of view we can say that all code-using coroutines we can think of, in most of real-world projects will fall into two separate categories:

Coroutines TS vs P1063: end-programmer example

Let’s take the very same piece of code and see how it can be written under both proposals.

Coroutines TS a.k.a. Gor-routines

future<int> count_bytes(Connection& connection) {
  int bytes_read = 0;
  vector<char> buffer(1024);
  while(!connection.done()) {
    bytes_read +=
      co_await connection.Read(buffer.data(),
      buffer.size());
  }
  co_return bytes_read;
}

P1063R0 a.k.a. "Core Coroutines"

 auto count_bytes(Connection& connection) =>
      make_future<int>([&connection] do {
  int bytes_read = 0;
  vector<char> buffer(1024);
  while(!connection.done()) {
    bytes_read +=
      [<-]connection.Read(buffer.data(),
      buffer.size());
  }
  return bytes_read;
}); 

End-programmer semantics: exactly the same for Coroutines TS and Core Coroutines

Following from the ‘App-level point of view’ section above, the most important (and utterly unchangeable later) portion of any coroutines proposal is the semantics of co_await (or whatever other syntax it may have). Historically, there have been several significantly different semantics of await (for example, in a relatively recent [P0114R0], it was argued not to require a marker for a suspend point – which, BTW, was argued later to be a Bad Thing™ for app-level [NoBugs17]).

However, if we take a look at currently competing proposals (Coroutines TS and P1063), we’ll see (to the best of our understanding) that

the semantics of co_await and the proposed operator [<-], at least at the point where co_await/[<-] is used by end-programmer code, is exactly the same.

Not only is the flow interrupted (with the possibility of being resumed) in the very same manner for both proposals, but also all properties that are observable from the business-logic level (such as enforcing calls around async call to be asynchronous) are the same too.

As noted above, such consensus on high-level semantics (compared to co_await) wasn’t the case for earlier proposals such as [P0114R0], but is the case for [P1063R0].

On end-programmer syntax

While the semantics of the proposals are exactly the same, there are a few high-level syntactic differences between P1063 and ‘Coroutines TS’:

However, the most important property of all the syntactic differences is

As the differences are purely syntactical, nothing prevents us from either (a) choosing whatever syntax is preferred right now, without delaying the whole thing for N years, and/or (b) adding syntactic alternatives later

Customization points: mostly an implementation detail that can be changed later

In fact, what we have already discussed above is only a minor part of the differences between Coroutines TS and P1063; however, all the remaining differences we’re aware of are either (a) about optimizations (which we’ll discuss a bit later), or (b) about so-called ‘customization points’ in P1063-speak, or, from our current perspective, are about what we decided to call ‘app-level infrastructure code’. Let’s take a closer look at those customization points and app-level infrastructure code.

As for app-level infrastructure code, the most important properties are:

  1. it is hidden from the view of the end-user programmer
  2. it is rarely changed
  3. and it is small.

(BTW, as it was already noted above, P1063 itself has indications which agree with this point of view.)

As a direct result of item #1 above, from the end-user programmer point of view,

customization points/app-level infrastructure code are nothing but implementation details

Moreover, from #2 and #3 it follows that costs of rewriting such code – if such a need will ever arise – will be small; this opens us a door to change them later if/when it is demonstrated that such a change is necessary.

Performance and allocations

Another set of objections to Coroutines TS laid out in P1063 is about performance and lack of normative control over allocations. This one is simple – P1063 itself acknowledges that all their performance/allocation concerns can be addressed by extending Coroutines TS later: “These all appear to be pure extensions, so they could be done post-C++20 if need be.” As a result, we don’t really care about performance issues now, as optimizations (most of them already existing) can be made normative later.

This is without mentioning that the whole argument along the lines of “we don’t want allocations” becomes more and more moot as soon as we take into account that modern single-threaded allocators can perform malloc()+ free() pairs in as little as 15 CPU cycles [Ignatchenko18]; with this cost being comparable to the cost of a single branch mis-prediction(!), efforts related to eliminating allocations become more and more of a ‘yet another optimization’ rather than ‘a thing we should care about a lot’.

Analysis: coroutines TS CAN be voted in, even if P1063 is right on every point

Now, we’re done with the preliminaries and can proceed to the point of this article. Let’s assume for the moment that ISO committee and the industry follow this path:

Now, let’s consider all the possible scenarios with regards to the merits of P1063 in this context (keeping in mind its claims about being more generic than Gor-routines):

In other words:

In each and every conceivable scenario, including the one where P1063 is right with each and every significant claim they’re making, voting in Coroutines TS is The Right Thing To Do™.

Voting Coroutines TS into C++20 will provide two all-important benefits:

In a sense, what we have is a situation similar to prima facie hearing in the criminal law of some countries; in such hearings, even if all the evidence presented by the prosecution, is taken at face value, but the defendant is still not guilty, there is no need to argue about the merits of the evidence, and the decision can be made in favour of the defendant without conducting a full hearing. Such cases are admittedly rare, but in our case of P1063-vs-Coroutines-TS, it is possible because of two major observations:

Or, trying to approach the same thing from a different perspective: we clearly feel that current Coroutines TS does represent ‘gradual expansion’ without degenerating into ‘opportunistic hacking’ as defined in [P0976] by Bjarne Stroustrup.

Gradual expansion, relying on feedback, is my ideal. Better an incomplete design than a poor/clumsy/bloated ‘complete solution’.

And FWIW, ‘relying on feedback’ is not really possible until co_await makes it into the standard one way or another; it means that the merits of voting in Coroutine TS right now go far beyond our simple desire to start using it ASAP: it is also important to ensure that the end-product (the C++ standard) is the best one possible. Indeed, if some over-specified stuff makes it into the standard, it will be next to impossible to replace it later – and right now we just don’t have sufficient information to say which way is the best one; in this sense, the approach taken by Coroutines TS (to hide as much as possible beyond the implementation boundary, or – in other words – ‘to under-specify rather than over-specify’) is a Good Thing™; combined with an as-early-as-possible acceptance of Coroutines TS into the standard, this allow to get that all-important feedback Bjarne refers to in [P0976].

Conclusion

We hope that we have made a case for ‘voting for Coroutines TS right now regardless of the merits of the finer points of P1063’ (that is, points going beyond two major observations listed above):

In other words, we hope we have demonstrated that voting in Coroutines TS is The Right Thing To Doâ„¢ without criticizing P1063 itself.

Phew. We rest our case.

References

[Ignatchenko18] (Re)Actor Allocation At 15 CPU Cycles, Sergey Ignatchenko, Dmytro Ivanchykhin, Marcos Bracco, Overload #142, https://accu.org/index.php/journals/2533

[Knuth] The Art of Computer Programming, Donald Knuth, Vol. I

[McNellis16] Introduction to C++ Coroutines, James McNellis, CppCon2016, https://www.youtube.com/watch?v=ZTqHjjm86Bw

[Nishanov15] C++ Coroutines – a negative overhead abstraction, Gor Nishanov, CppCon2015, https://www.youtube.com/watch?v=_fu0gx-xseY

[N4760] Working Draft, C++ Extensions for Coroutines, Gor Nishanov, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4760.pdf

[NoBugs17] Eight Ways to Handle Non-Blocking Returns in Message-Passing Programs, ‘No Bugs’ Hare, http://ithare.com/eight-ways-to-handle-non-blocking-returns-in-message-passing-programs-with-script/3/, CppCon17

[P0114R0] Resumable Expressions, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf

[P0973R0] Coroutines TS Use Cases and Design Issues, Geoff Romer, James Dennett, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0973r0.pdf

[P0976] The Evils of Paradigms Or Beware of one-solution-fits-all thinking, Bjarne Stroustrup, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0976r0.pdf

[P1063R0] Core Coroutines, Geoff Romer, James Dennett, Chandler Carruth, http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1063r0.pdf

[Stroustrup04] Speaking C++ as Native (Multi-paradigm Programming in Standard C++), Bjarne Stroustrup, http://ewh.ieee.org/r5/central_texas/austin_cs/presentations/2004.02.25.pdf

Dmytro Ivanchykhin Dmytro Ivanchykhin has 10+ years of development experience, and has a strong mathematical background (in the past, he taught maths at NDSU in the United States).

Sergey Ignatchenko Sergey Ignatchenko has 15+ years of industry experience, including being a co-architect of a stock exchange, and the sole architect of a game with 400K simultaneous players. He currently holds the position of Security Researcher.

Maxim Blashchuk Maxim Blashchuk has substantial development experience, most of it with embedded programming. Recently he joined a team performing research on low-level C++ libraries providing properties such as determinism and memory safety.

Notes: 

More fields may be available via dynamicdata ..