C and Go, Models of Computation. by David Chisnall. 2012

By Xah Lee. Date: .

C Lang and Go Lang Models of Computation

(following are written by Steve aka Stefano)

Kinda relevant to this discussion, I suggest the first chapter of Go Programming Language Phrasebook by David Chisnall. Buy at amazon

In few pages, it presents the abstract computer model of Go, and it compares it with the abstract model of C.

The point of a low-level language is to provide an abstract machine model to the programmer that closely reflects the architecture of the concrete machines that it will target. There is no such thing as a universal low-level language: a language that closely represents the architecture of a PDP-11 will not accurately reflect something like a modern GPU or even an old B5000 mainframe. The attraction of C has been that, in providing an abstract model similar to a PDP-11, it is similar to most cheap consumer CPUs.

Over the last decade, this abstraction has become less like the real hardware. The C abstract model represents a single processor and a single block of memory. These days, even mobile phones have multicore processors, and a programming language designed for single-processor systems requires significant effort to use effectively. It is increasingly hard for a compiler to generate machine code from C sources that efficiently uses the resources of the target system.

The aim of Go was to fill the same niche today that C fit into in the ’80s. It is a low-level language for multiprocessor development. C does not provide any primitives for communicating between threads, because C does not recognize threads; they are implemented in libraries. Go, in contrast, is designed for concurrency. It uses a form of C.A.R. Hoare's Communicating Sequential Processes (CSP) formalism to facilitate communication between goroutines.

Creating a goroutine is intended to be much cheaper than creating a thread using a typical C threading library. The main reason for this is the use of segmented stacks in Go implementations. The memory model used by early C implementations was very simple. Code was mapped (or copied) into the bottom of the address space. Heap (dynamic memory) space was put in just above the top of the program, and the stack grew down from the top of the address space. Threading complicated this. The traditional C stack was expected to be a contiguous block of memory. When you create a new thread, you need to allocate a chunk of memory big enough for the maximum stack size. Typically, that's about 1MB of RAM. This means that creating a thread requires allocating 1MB of RAM, even if the thread is only ever going to use a few KB of stack space.

Go functions are more clever. They treat the stack as a linked list of memory allocations. If there is enough space in the current stack page for their use, then they work like C functions; otherwise they will request that the stack grows. A short-lived goroutine will not use more than the 4KB initial stack allocation, so you can create a lot of them without exhausting your address space.

When you're writing single-threaded code,garbage collection is a luxury. It's nice to have, but it's not a vital feature. This changes when you start writing multithreaded code. If you are sharing pointers to an object between multiple threads, then working out exactly when you can destroy the object is incredibly hard. Even implementing something like reference counting is hard. Acquiring a reference in a thread requires an atomic increment operation, and you have to be very careful that objects aren't prematurely deallocated by race conditions. Like Java, and unlike C or C++, Go does not explicitly differentiate between stack and heap allocations. Memory is just memory. (sorry for the wall of text)

go lang David Chisnall PMP5d
go lang David Chisnall PMP5d