Beyond Effective Go: Part 1 - Achieving High-Performance Code
By Corey S Scott and Siew May Tan
()
About this ebook
Are you an experienced Go developer that wants to be more productive? Do you want to write
Corey S Scott
Corey Scott is currently a Principal Software Engineer at Grab & Ovo, living in Melbourne, Australia.He has been programming professionally since 2000, with Go as his preferred language for building large-scale distributed services since 2014.
Related to Beyond Effective Go
Titles in the series (1)
Beyond Effective Go: Part 1 - Achieving High-Performance Code Rating: 0 out of 5 stars0 ratings
Related ebooks
Go Programming Cookbook: Over 75+ recipes to program microservices, networking, database and APIs using Golang Rating: 0 out of 5 stars0 ratingsDesign Patterns in Swift: A Different Approach to Coding with Swift Rating: 0 out of 5 stars0 ratingsLearning Concurrent Programming in Scala Rating: 0 out of 5 stars0 ratingsInstant OSGi Starter Rating: 0 out of 5 stars0 ratingsClojure for Java Developers Rating: 0 out of 5 stars0 ratingsOpa Application Development Rating: 0 out of 5 stars0 ratingsPython High Performance - Second Edition Rating: 0 out of 5 stars0 ratingsJava 8 to 21: Explore and work with the cutting-edge features of Java 21 (English Edition) Rating: 0 out of 5 stars0 ratingsGo Cookbook Rating: 5 out of 5 stars5/5Rust for C++ Programmers: Learn how to embed Rust in C/C++ with ease (English Edition) Rating: 0 out of 5 stars0 ratingsThe Way to Go: A Thorough Introduction to the Go Programming Language Rating: 2 out of 5 stars2/5Platform engineering The Ultimate Step-By-Step Guide Rating: 0 out of 5 stars0 ratingsSpring 2.5 Aspect Oriented Programming Rating: 0 out of 5 stars0 ratingsPractical C++ Backend Programming Rating: 0 out of 5 stars0 ratingsBeginning Rust Programming Rating: 0 out of 5 stars0 ratingsGROKKING ALGORITHMS: A Comprehensive Beginner's Guide to Learn the Realms of Grokking Algorithms from A-Z Rating: 0 out of 5 stars0 ratingsProgramming the Network with Perl Rating: 0 out of 5 stars0 ratingsMastering C# Concurrency Rating: 0 out of 5 stars0 ratingsOSGi in Action: Creating Modular Applications in Java Rating: 0 out of 5 stars0 ratingsDomain Driven Design A Complete Guide - 2020 Edition Rating: 0 out of 5 stars0 ratingsGrokking Streaming Systems: Real-time event processing Rating: 5 out of 5 stars5/5Rust In Practice, Second Edition: A Programmers Guide to Build Rust Programs, Test Applications and Create Cargo Packages Rating: 0 out of 5 stars0 ratingsCloud Architects A Complete Guide - 2019 Edition Rating: 0 out of 5 stars0 ratingsMastering Elasticsearch 5.x - Third Edition Rating: 0 out of 5 stars0 ratingsSystem Design Interview: Prepare And Pass Rating: 0 out of 5 stars0 ratingsSchematron: A language for validating XML Rating: 0 out of 5 stars0 ratingsScala for Java Developers Rating: 5 out of 5 stars5/5
Software Development & Engineering For You
Hand Lettering on the iPad with Procreate: Ideas and Lessons for Modern and Vintage Lettering Rating: 4 out of 5 stars4/5Learn to Code. Get a Job. The Ultimate Guide to Learning and Getting Hired as a Developer. Rating: 5 out of 5 stars5/5PYTHON: Practical Python Programming For Beginners & Experts With Hands-on Project Rating: 5 out of 5 stars5/5Python For Dummies Rating: 4 out of 5 stars4/5Modern C++ for Absolute Beginners: A Friendly Introduction to C++ Programming Language and C++11 to C++20 Standards Rating: 0 out of 5 stars0 ratingsThe Inmates Are Running the Asylum (Review and Analysis of Cooper's Book) Rating: 4 out of 5 stars4/5SQL For Dummies Rating: 0 out of 5 stars0 ratingsHow Do I Do That In InDesign? Rating: 5 out of 5 stars5/5How to Write Effective Emails at Work Rating: 4 out of 5 stars4/5Grokking Algorithms: An illustrated guide for programmers and other curious people Rating: 4 out of 5 stars4/5OneNote: The Ultimate Guide on How to Use Microsoft OneNote for Getting Things Done Rating: 1 out of 5 stars1/5Level Up! The Guide to Great Video Game Design Rating: 4 out of 5 stars4/5Beginning Programming For Dummies Rating: 4 out of 5 stars4/5Lua Game Development Cookbook Rating: 0 out of 5 stars0 ratingsBeginning C++ Programming Rating: 3 out of 5 stars3/5How Do I Do That in Photoshop?: The Quickest Ways to Do the Things You Want to Do, Right Now! Rating: 4 out of 5 stars4/5Learning Python Rating: 5 out of 5 stars5/5Photoshop For Beginners: Learn Adobe Photoshop cs5 Basics With Tutorials Rating: 0 out of 5 stars0 ratingsGood Code, Bad Code: Think like a software engineer Rating: 5 out of 5 stars5/510x Software Engineer Rating: 0 out of 5 stars0 ratingsTiny Python Projects: Learn coding and testing with puzzles and games Rating: 5 out of 5 stars5/5Reversing: Secrets of Reverse Engineering Rating: 4 out of 5 stars4/5Beginning C++ Game Programming - Second Edition: Learn to program with C++ by building fun games, 2nd Edition Rating: 0 out of 5 stars0 ratingsProgramming Problems: A Primer for The Technical Interview Rating: 4 out of 5 stars4/5Agile Practice Guide Rating: 4 out of 5 stars4/5Gray Hat Hacking the Ethical Hacker's Rating: 5 out of 5 stars5/5
Reviews for Beyond Effective Go
0 ratings0 reviews
Book preview
Beyond Effective Go - Corey S Scott
Contents 
Preface
Book Structure and Formatting
Chapter 1
Understanding Concurrency, Parallelism, and the Sync Packages
Introduction
Code
Understanding Concurrency & Parallelism
Concurrency
Parallelism
Concurrency is not parallelism
Parallelism considerations
GOMAXPROCS and goroutines
Unraveling Go’s Concurrency Approach
Fan-in
Fan-out
Multiplexing
Sometimes CSP is not the best choice
Deadlocks, Starvation, and Livelocks
Deadlocks
Starvation
Livelocks
Detecting, fixing, and avoiding deadlocks, livelocks, and starvation
Data Races
Detecting data races
The Sync/Atomic Package
Significant features of sync/atomic package
The Sync Package
Significant features of the sync package
Summary
Questions
Chapter 2
Applying Go Concurrency: Primitives, Patterns, and Tools
Introduction
Code
Goroutines for Experts
Anonymous closures
Created does not mean started
Clean up after yourself
Diagnosing goroutine leaks
Analyze exit conditions
Cheap does not mean free
Long-running vs. Lots running
Channels for Experts
Channel direction
Slow consumers
Producer buffering
Closing a channel
Bounded output
Data types matter
Select Statements for Experts
Select with timeout
Nil channels
Extended Semaphores
Advanced Concurrency Patterns
Copy-on-write
Batching
Reply channels
Event listeners
Fastest responder
Update oldest
Resource pool
Execution Tracing
When should we use execution tracing?
Keeping Out of Concurrent Trouble
Don’t switch between atomic and non-atomic code
Combine mutexes with the types they protect
Never cross the streams
Thread-safety
Minimize synchronization protection
Summary
Questions
Chapter 3
Achieving High-Performance Code
Introduction
Code
When to Optimize
What to Optimize
How to Optimize with pprof
CPU profiling
Memory profiling
Block profiling
Mutex profiling
Benchmark Tests
Benchmarking basics
Beyond the basics
Constructing good benchmark tests
Benchmarking traps
Benchmarking tricks
Performance Patterns
The fastest code
Only do it once
Data encoding/decoding
Reducing allocations
Trade memory for CPU
Be mindful of maps
Pre-allocation
String concatenation
Printf and friends
Defer
Cheap checks before expensive ones
Summary
Questions
Postface
More From This Author
Preface
When folks ask me, How do I learn Go?
I tell them the first stop is the Tour of Go from the go.dev website. Then I tell them that while they are ready to Go after the tour, they should also read the Effective Go article.
I also warn them that they likely won’t absorb all of the wisdom in the Effective Go article right now, but they should add a reminder in their calendar to re-read it in 6 months. However, when they come to me and say, I've been doing Go for a while and want to get better, where do I go now?
I could never come up with a concise answer to this.
So I decided to take a shot at collecting the community's best practices, the Go-isms, and all my hard-won experiences into a book. This is my humble attempt to help you become more efficient and more effective while using Go as the language to provide tangible customer value.
You may have noticed from the book's subtitle that it is a part of a more extensive collection of work. I have decided to release the content in parts to ensure that it is available to you without being delayed by the lengthy process of writing, editing, and publishing (and everything else).
If you are interested in the other parts and their progress or want to provide feedback, then please:
● Drop by my website https://coreyscott.dev/
● Reach out via Github https://github.com/corsc/
● Or message me on Twitter https://twitter.com/CoreySScott
Book Structure and Formatting
Each chapter is comprised of five sections:
The introduction - will outline the chapter's goal and highlight the key concepts that the chapter will cover.
The code listing - This section includes a link where you can download the code found throughout the chapter. You are encouraged to download this code and refer to it often. The downloaded code often includes more context beyond the sections of the code highlighted in the book. Also, the downloaded copy will include any fixes if there are bugs in the code.
The main content - This is where all the knowledge is.
The summary - This attempts to summarize the chapter by highlighting the key concepts.
A list of questions - These allow you to determine if you gained the expected understanding from the chapter. There is no list of answers for these questions, as the answers are in the chapter itself.
Throughout this book, we have used the following style conventions:
● Regular text - This is the default style and will be used for any content not covered by the other styles in this list.
● In-line code - This style is used for code that appears within regular content.
● Code Blocks - This style is used for blocks of code.
Chapter 1
Understanding Concurrency, Parallelism, and the Sync Packages
Introduction
Concurrency is one of Go's flagship features. In the multi-core, high-performance world we find ourselves in, mastering concurrency will set us apart from other programmers and provide immeasurable value to our users and employers.
This chapter will examine concurrency and parallelism, paying particular attention to their differences.
We will briefly discuss Go’s concurrency ideology.
We will develop a deeper understanding of the issues we face when using concurrent code, including data races, deadlocks, livelocks, and starvation.
Armed with our new appreciation of the issues facing concurrency programming, we will round out the chapter with a look at two essential packages from the standard library, the Sync and Atomic packages.
For a chapter on Go’s concurrency, this chapter may not have as much about channels, goroutines, and the select statement as you might expect, but don’t worry, we will be going deep on all of these in the next chapter.
The following topics will be covered in this chapter:
● Understanding Concurrency & Parallelism
● Unraveling Go’s Concurrency Approach
● Deadlocks, Starvation, and Livelocks
● Data Races
● The Sync/Atomic Package
● The Sync Package
Code
You will need a recent copy (1.13+) of Go installed and your favorite IDE to get the most out of this chapter.
The full versions of all code examples and other related material can be found at:
https://github.com/corsc/Beyond-Effective-Go/tree/master/Chapter01
Understanding Concurrency & Parallelism
Like many things in our industry, the terms Concurrency and Parallelism mean different things to different people and are often incorrectly used interchangeably.
Therefore, let's start by defining what these terms mean to us as Gophers, how they relate to each other, and how they relate to our goals as programmers.
Concurrency
Concurrency is, perhaps surprisingly, not about the execution of the program. It is a programming style.
Concurrency is breaking the code into chunks that can run separately and produce the correct result.
Let’s break this down. The first point is that the chunks can run separately, meaning they can run simultaneously. The second is that the result is correct no matter how the chunks of code run, either in parallel or not.
Parallelism
While parallelism is often used interchangeably or incorrectly to mean the same as concurrency, it is not the same.
Parallelism is the simultaneous execution of multiple chunks of code.
These chunks could be related, or they could be completely separate. The key is that many valuable things are happening at the same time. In our current professional environment, parallelism is essential, as it allows us to take full advantage of the many CPU cores in modern hardware.
Concurrency is not parallelism
While it is possible to have concurrency without parallelism, the reverse is not true. To achieve parallelism, we must first code using a concurrency-based style.
Let’s look at an example to explore the differences between concurrency and parallelism further. First, we define a goroutine that writes a value to a channel 15 times and then exits:
func output(wg *sync.WaitGroup, value int, result chan int) {
defer wg.Done()
for x := 0; x < 15; x++ {
result <- value
// inform the schedule we can be interrupted
runtime.Gosched()
}
}
Now we start two instances of our goroutine and wait until they both finish:
func main() {
result := make(chan int, 100)
wg := &sync.WaitGroup{}
// start our separate processes
wg.Add(1)
go output(wg, 0, result)
wg.Add(1)
go output(wg, 1, result)
// wait until all goroutines are done
wg.Wait()
close(result)
for value := range result {
print(value)
}
}
Without running the program, what do you expect the output to be? The most intuitive answer would be:
010101010101010101010101010101
Sometimes it will be, but after running the program three times, my results were:
101010101010101010101010111000
101010101010101010101010101010
111011111111111100000000000000
This is not quite the intuitive answer, is it? The reason for this is the non-deterministic nature of the scheduler.
The scheduler is the part of the computer that decides which threads and, by extension, which goroutines run and when. The fact that we cannot predict what is running and when is a significant source of complexity and mistakes when attempting to write parallel code.
While we cannot control the scheduler, we can influence it. We can run the above program and restrict the scheduler to only 1 CPU core with the command:
GOMAXPROCS=1 go run ./Chapter01/01_concurrency_parallelism/01_intro/main.go
The output is most likely to be:
101010101010101010101010101010
In this example, our code is concurrent. However, we are restricting the scheduler to only one core. As a result, only one goroutine can run at a time. In this program, our goroutines will generally switch when they encounter the runtime.Gosched() command, which informs the scheduler that they can yield the CPU.
To demonstrate the relationship between parallelism and the number of cores, let’s draw our program's execution visually:

Figure 1.1 - Single Core
Because we have only one core, we cannot have any parallelism. Conversely, when we have multiple cores, we achieve parallelism, and the result becomes much less predictable. One such execution may look like this:

Figure 1.2 - Multi-Core
In this situation, the processes are free to run either separately or simultaneously and consequently produce an unpredictable result.
As we have seen, a program