- The Language Barrier
- Compiler Cleverness
- Evolving the Language
- Future Challenges
Evolving the Language
Another option is to modify the language. GCC, for example, includes a number of extensions to the C family of languages. One relatively simple extension allows you to define vector types. The primitive C operations are extended to work on vector types.
Shader languages modify the language even more. OpenCL C, for example, is quite similar to C, but has a lot of small differences, including primitive vector types, large collections of built-in functions, and support for multiple address spaces. OpenCL C isn't used in isolation; it's used for writing short program fragments, which are launched from other code.
It's possible to imagine a combination of these two techniques. OpenCL C both adds and removes features from C. You could use the extensions in normal C programs, and then rely on the compiler to split up the program so that the parts that only use the GPU-compatible subset could run on the GPU.
The latest drafts of the C standard include some support for concurrency, but nothing particularly useful. For inspiration for future languages, Erlang and Haskell provide more interesting models.
Erlang was originally created for telecom systems, but has seen some use in other concurrent systems. The most popular open source XMPP server, ejabberd, is written in Erlang and scales very well. Erlang uses the actor model for concurrency, where code is partitioned into separate processes that can communicate only by copying data between them, meaning that nothing other than process references is ever aliased between processes.
Erlang processes are purely a source-language construct, as is the copying operation. In practice, they're implemented as lightweight threads on top of operating system threads, and the copy operation just passes a pointer between threads. Although Erlang doesn't allow objects to be modified at the language level, the compiler will modify some objects at the implementation level if they're not aliased, and it's relatively easy to check whether they are aliased, due to the constraints of the language.
A language with no side effects isn't particularly useful. Things like I/O are impossible. In Haskell, these things are explicitly defined using monads, which are effectively a mechanism for collecting side effects for later application. The side-effect-free language builds monads (an ordered collection of computational steps), and then can apply them externally to the side-effect-free code.
These work very well in conjunction with software transactional memory. You can run a set of function calls in parallel, applying their results to memory only if nothing occurred along the way that would cause them to fail. This design is analogous to that of database transactions, where either a complete set of changes is applied, or no changes take place. (I look at how STM makes concurrent programming easier in my article "Writing Concurrent Systems, Part 3.") In effect, this is a high-level version of the speculative execution that superscalar CPUs do.