A Pleading for Reasonable Parallel Processing Support in C++

Detlef Vollmann
vollmann engineering gmbh
dv@vollmann.ch
Document Number: JTC 1/SC22/WG21/N1834 J16/05-0094
Date: 2005-06-24
Project: JTC1.22.32
Programming Language C++
Evolution Working Group
Reference: ISO/IEC IS 14882:2003(E)

Preliminary: This paper presents the personal opinion of the author. The statements in this paper might seem provocative to some, but are based on quite a lot of personal experience.

This paper does not present a final statement, but there must be strong reasons for the author not to follow the conclusions given at the end of this paper.


Introduction

Parallel processing is in modern computers a given. More and more you have hardware support for parallel processing, like Hyperthreading, multiple cores or multiple CPUs. But even without real parallel hardware support, modern operating systems give you a quasi-parallel execution environment even in pretty small embedded systems.

Given that reality, there is probably a general agreement in the C++ standardization committee that C++0x needs to address parallel processing. Where there is no general agreement yet is how such support should look like.

This paper argues that fixing the execution model for C++ is a must, while having a low-level "threading" or "locking" library might be a bad idea.


Threads vs. Parallel Processing

A general note on terminology and concepts: some papers and authors use the term multi-threading when addressing parallel processing, and some use parallel processing when addressing multi-threading.

This paper uses the term thread for a separate thread of control in completely the same address space (without the possible exception of specifically declared thread-local storage). This was common practice in operating systems like PC-DOS where all tasks shared the same address space.

This paper uses the term parallel processing generally for separate thread of controls. In modern operating systems this often provides different address spaces, but allow sharing some common resources (memory and others).

A lot of current literature proposes threads as a solution for parallel processing needs, but this is almost always a bad idea. Any standard C++ feature that promotes threads instead of general parallel processing means would be a bad service to the C++ community.


Core Level Support

Paper N1777 and Andrei Alexandrecu's presentation in Redmond and Hans Boehm's presentation in Lillehammer showed clearly that the current execution model of C++ is underspecified for parallel executing tasks accessing the same memory.

Some (including the author) believe that in that context the keyword volatile should play a role to mark memory that is accessed from different tasks in parallel. But if volatile gets some useful meaning (whatever), there are several parts of the C++ standard that need some revisiting (e.g. does an implementation provide a volatile op=, do standard library classes (like complex<>) support volatile operations, etc.).


Library Support

Fixing the execution model of C++ helps for those using implementation specific functionality. But without some standard library support there is no way for a C++ user to use parallel processing in a standard way. While paper N1815 simply states "The standard must define some mechanism for locking.", this paper proposes a different approach.

For standard library extensions for parallel processing support exist different ideas. These can be categorized into different domains:


High-Level Library

A high-level library to support parallel processing would provide classes for shared resources (mainly memory, but possibly also I/O streams) and means for asynchronously executed functions.

Shared resources would be provided by pure container classes (strings, fixed sized sequences, dynamic sequences, associative containers) and monitors that combine shared data storage with synchronized methods.

Asynchronously executed procedures would allow starting functions asynchronously, and controlling them: requesting their status, wait for them, and collect any results (e.g. Futures).

The definition of such a library must not be thread specific. On the contrary for shared resources there should be a requirement that they also work when accessed from different processes, and for asynchronous functions there should be at least strong encouragement for implementations favouring different processes instead of different threads. Whether such different implementations should be transparent to the user is an open question.

Such a high-level library could be used by application programmers without much risk of introducing the usual synchronization problems (deadlock and races) and would therefore be useful to a major portion of the C++ user community.


Low-Level Library

A low-level library for parallel processing support would provide classes for synchronization mechanisms like locks, semaphores and conditional events. Such mechanisms provide detailed control mechanisms for fine-grained synchronization. But such mechanisms also introduce a high risk for problems like deadlock and race conditions.

Again, if such a library is specified, it must not be specific to threads but must also provide synchronization across process/address space boundaries.

A really useful low-level synchronization library must provide mechanisms to wait for all kind of events (mutex availability, process completion, I/O operations, signals, etc.) As those events depend highly on the underlying operating system, a really useful low-level synchronization library is out of the scope of C++0x.


Thread Library

A synchronization library is generally only useful if there are means to start other threads of control that execute asynchronously. A thread library would allow starting and controlling threads. But such a library would need to support threads as well as processes. To define such a library with natural implementations on different operating systems is quite a challenge (location of program executable, program arguments).


Conclusion

All support for parallel processing in C++0x must work for processes. Support for threads might also be useful, but has lower priority.

C++0x must provide an execution model that generally supports asynchronous access to objects.

If library support for parallel processing is provided, a high-level library has absolute priority.

A low-level library that is really useful requires a major effort and is therefore probably out of scope for C++0x. A half-baked low-level library would be a bad service to the C++ community. A half-baked low-level library without a high-level library would be disastrous, as this would give the message "Use this dangerous library, because it is standard, instead of a safer non-standard library."

Generally, a C++ library approach along the lines of POSIX threads is very problematic, as that specification is focused on low-level support, while an approach along the lines of OpenMP looks much more promising, as that specification focuses on high-level constructs while still providing some low-level functionality.

Just as a side note: we currently have a similar situation with the C++ I/O streams library. This supports generally safe and easy to use high-level abstractions instead of the low-level OS-specific functionality like memory-mapped files and asynchronous I/O that is often required for system programming.

References

[N1777] Andrei Alexandrescu, Hans Boehm, Kevlin Henney, Ben Hutchings, Doug Lea, and Bill Pugh, C++ Standard Committee, WG21/N1777=J16/05-0037, Memory model for multithreaded C++: Issues, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1777.pdf .

[N1815] Lawrence Crowl, C++ Standard Committee, WG21/N1815=J16/05-0075, ISO C++ Strategic Plan for Multithreading, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1815.html .

[Monitor] 2nd ACM Conference on the History of Programming Languages, "Monitors and Concurrent Pascal: A personal history", Per Brinch Hansen, 1993, pp. 1-25.

[Future] ACM Transactions on Programming Languages and Systems, "Multilisp - A Language for Concurrent Symbolic Computation", Robert H. Halstead, 1985, pp. 501-538.

[POSIX] ISO, ISO/IEC 9945-1:2003, Information technology -- Portable Operating System Interface (POSIX), http://www.unix.org/version3/iso_std.html .

[OpenMP]  http://www.openmp.org/ .