TECH

February 23, 2014 11:09

Go

If you’re new to Go, or if you don’t understand the phrase “concurrency is not parallelism,” check out Rob Pike’s excellent talk on the subject. It’s about 30 minutes. I guarantee it’s worth 30 minutes.

To summarize — “When people hear the word concurrency, they often think of parallelism, a related but very different concept. In programming, concurrency consists of processes that execute independently, while parallelism is the simultaneous execution of (possibly related) computations. Concurrency means doing many things at the same time. Parallelism means doing many things at once.” [1]

Go allows us to write concurrent programs. It provides goroutines and, more importantly, communication between goroutines. So let me talk about goroutines.

Goroutines and threads

Go uses Goroutines, while languages like Java use threads. What’s the difference? We need to look at three factors – memory consumption, creation and destruction costs and switching costs.

Memory consumption

Creating a Goroutine doesn’t require much memory, just 2kB of stack space. They increase stack space through on-demand allocation and release of the heap. [2][3] Threads, on the other hand, start at 1Mb of memory (more than 500 times the size of a Goroutine) and have an area of memory called the Guard page, which protects the memory of one thread from that of another.

Thus, a server handling incoming requests could create a Goroutine for each request without any problem, but creating a thread for each request would eventually result in a dreaded OutOfMemoryError. This isn’t limited to Java — any language that uses operating system threads as its primary means of concurrency will face this problem.

Creation and destruction costs

The creation and destruction costs of a thread are very high because it must request resources from the operating system and release them when it runs out. The solution to this problem is to maintain a thread pool. Instead, goroutines are created and destroyed by the runtime environment, and these operations cost very little. The Go language does not support manual management of Goroutine.

Switching costs

When one thread blocks, another needs to be scheduled to run on the current processor. Threads are preemptively, when a thread switches to another thread, the scheduler needs to save/restore all the registers, i.e., 16 general purpose registers, program counter, stack pointer, Segment Registers and 16 XMM registers, floating point coprocessor state, 16 AVX registers, all special module registers (MSR), etc. This can be costly when switching quickly between threads.

Goroutines are cooperatively and only 3 registers need to be saved/restored when a switch occurs – program pointer, stack pointer and DX. The cost is much lower.

As discussed earlier, the number of Goroutines is usually much higher than threads, but for two reasons. One is to consider only runnable Goroutines and not blocking goroutines. The second is that modern schedulers have a complexity of O(1), which means that switching time is not affected by the number of choices (threads or Goroutines). So the number of Goroutines has no effect on the switching time. [5]

How do I implement Goroutine

As mentioned earlier, the runtime environment manages the Goroutine from creation to scheduling to destruction. The runtime environment allocates several threads on which all goroutines are multiplexed. At any time, each thread will execute a Goroutine. If the goroutine is blocked, another goroutine is executed on that thread. [6]

Since goroutines are collaborative, a goroutine that loops endlessly may exhaust other Goroutines on the same thread. In Go 1.2, this problem was solved by occasionally starting the Go scheduler when a function was called, so that a loop could be preempted when there were no inline functions.

Goroutines blocking

Goroutine costs very little and does not cause the running thread to block in the following blocking case.

  • Network input
  • Go to bed
  • Channels operating
  • Basic operations that block in the sync package

Even if tens of thousands of goroutines were created and most of them were blocked on one of them, there would not be much waste of system resources because the runtime would schedule another goroutine.

In short, Goroutines are lightweight abstractions on threads. Go programmers do not handle threads, and similarly, the operating system is unaware of the existence of goroutine. From an operating system perspective, the Go program behaves like an event-driven C program. [5]

Threads and processors Although you cannot directly control the number of threads that the runtime environment will create, you can set the number of processor cores used by your program. Set the variable GOMAXPROCS and call Runtime.gomaxprocs (n). Increasing the number of processor cores does not necessarily improve a program’s performance, depending on the design of the program itself. Profiling tools can be used to find the ideal number of cores for your program.

conclusion

As with other languages, it is important to prevent multiple Goroutines from accessing shared resources at the same time. It is better to use channels to communicate by sharing memory between goroutines. Instead, share memory by communicating.

Finally, I strongly recommend that you check out C.A.R.Hoare’s Communicating Processes. This man is a genius. In the 1978 paper, he predicted that the single-core performance of processors would eventually hit a bottleneck, and chipmakers would instead increase the number of cores. His proposal to take advantage of this had a profound impact on the design of Go.

footnotes

1 – Concurrency is not parallelism by Rob Pike

2 – Effective Go: Goroutines

3 – Goroutine stack size was decreased from 8kB to 2kB in Go 1.4.

4 – Goroutine stacks became contiguous in Go 1.3.

5 – Scheduling of goroutines on golang-nuts by Dmitry Vyukov

6 – Analysis of the Go runtime scheduler by Deshpande et al.

7 – 5 things that make Go fast by Dave Cheney

Further reading

If you’re interested in learning more about Go, here are some excellent lectures on the language

  • Go Concurrency Patterns by Rob Pike
  • Advanced Go Concurrency Patterns by Sameer Ajmani.

The original link: blog.nindalf.com/posts/how-g…