Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Hands-On High Performance with Go

You're reading from   Hands-On High Performance with Go Boost and optimize the performance of your Golang applications at scale with resilience

Arrow left icon
Product type Paperback
Published in Mar 2020
Publisher Packt
ISBN-13 9781789805789
Length 406 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Bob Strecansky Bob Strecansky
Author Profile Icon Bob Strecansky
Bob Strecansky
Arrow right icon
View More author details
Toc

Table of Contents (20) Chapters Close

Preface 1. Section 1: Learning about Performance in Go
2. Introduction to Performance in Go FREE CHAPTER 3. Data Structures and Algorithms 4. Understanding Concurrency 5. STL Algorithm Equivalents in Go 6. Matrix and Vector Computation in Go 7. Section 2: Applying Performance Concepts in Go
8. Composing Readable Go Code 9. Template Programming in Go 10. Memory Management in Go 11. GPU Parallelization in Go 12. Compile Time Evaluations in Go 13. Section 3: Deploying, Monitoring, and Iterating on Go Programs with Performance in Mind
14. Building and Deploying Go Code 15. Profiling Go Code 16. Tracing Go Code 17. Clusters and Job Queues 18. Comparing Code Quality Across Versions 19. Other Books You May Enjoy

A brief history of Go

Robert Griesemer, Rob Pike, and Ken Thompson created the Go programming language in 2007. It was originally designed as a general-purpose language with a keen focus on systems programming. The creators designed the Go language with a couple of core tenets in mind:

  • Static typing
  • Runtime efficiency
  • Readable
  • Usable
  • Easy to learn
  • High-performance networking and multiprocessing

Go was publicly announced in 2009 and v1.0.3 was released on March 3, 2012. At the time of the writing of this book, Go version 1.14 has been released, and Go version 2 is on the horizon. As mentioned, one of Go's initial core architecture considerations was to have high-performance networking and multiprocessing. This book will cover a lot of the design considerations that Griesemer, Pike, and Thompson have implemented and evangelized on behalf of their language. The designers created Go because they were unhappy with some of the choices and directions that were made in the C++ language. Long-running complications on large distributed compile clusters were a main source of pain for the creators. During this time, the authors started learning about the next C++ programming language release, dubbed C++x11. This C++ release had very many new features being planned, and the Go team decided they wanted to adopt an idiom of less is more in the computing language that they were using to do their work.

The authors of the language had their first meeting where they discussed starting with the C programming language, building features and removing extraneous functionality they didn't feel was important to the language. The team ended up starting from scratch, only borrowing some of the most atomic pieces of C and other languages they were comfortable with writing. After their work started to take form, they realized that they were taking away some of the core traits of other languages, notably the absence of headers, circular dependencies, and classes. The authors believe that even with the removal of many of these fragments, Go still can be more expressive than its predecessors.

The Go standard library

The standard library in Go follows this same pattern. It has been designed with both simplicity and functionality in mind. Adding slices, maps, and composite literals to the standard library helped the language to become opinionated early. Go's standard library lives within $GOROOT and is directly importable. Having these default data structures built into the language enables developers to use these data structures effectively. The standard library packages are bundled in with the language distribution and are available immediately after you install Go. It is often mentioned that the standard library is a solid reference on how to write idiomatic Go. The reasoning on standard library idiomatic Go is these core library pieces are written clearly, concisely, and with quite a bit of context. They also add small but important implementation details well, such as being able to set timeouts for connections and being explicitly able to gather data from underlying functions. These language details have helped the language to flourish.

Some of the notable Go runtime features include the following:

  • Garbage collection for safe memory management (a concurrent, tri-color, mark-sweep collector)
  • Concurrency to support more than one task simultaneously (more about this in Chapter 3, Understanding Concurrency)
  • Stack management for memory optimization (segmented stacks were used in the original implementation; stack copying is the current incantation of Go stack management)

Go toolset

Go's binary release also includes a vast toolset for creating optimized code. Within the Go binary, the go command has a lot of functions that help to build, deploy, and validate code. Let's discuss a couple of the core pieces of functionality as they relate to performance.

Godoc is Go's documentation tool that keeps the cruxes of documentation at the forefront of program development. A clean implementation, in-depth documentation, and modularity are all core pieces of building a scalable, performant system. Godoc helps with accomplishing these goals by auto-generating documentation. Godoc extracts and generates documentation from packages it finds within $GOROOT and $GOPATH. After generating this documentation, Godoc runs a web server and displays the generated documentation as a web page. Documentation for the standard library can be seen on the Go website. As an example, the documentation for the standard library pprof package can be found at https://golang.org/pkg/net/http/pprof/.

The addition of gofmt (Go's code formatting tool) to the language brought a different kind of performance to Go. The inception of gofmt allowed Go to be very opinionated when it comes to code formatting. Having precise enforced formatting rules makes it possible to write Go in a way that is sensible for the developer whilst letting the tool format the code to follow a consistent pattern across Go projects. Many developers have their IDE or text editor perform a gofmt command when they save the file that they are composing. Consistent code formatting reduces the cognitive load and allows the developer to focus on other aspects of their code, rather than determining whether to use tabs or spaces to indent their code. Reducing the cognitive load helps with developer momentum and project velocity.

Go's build system also helps with performance. The go build command is a powerful tool that compiles packages and their dependencies. Go's build system is also helpful in dependency management. The resulting output from the build system is a compiled, statically linked binary that contains all of the necessary elements to run on the platform that you've compiled for. go module (a new feature with preliminary support introduced in Go 1.11 and finalized in Go 1.13) is a dependency management system for Go. Having explicit dependency management for a language helps to deliver a consistent experience with groupings of versioned packages as a cohesive unit, allowing for more reproducible builds. Having reproducible builds helps developers to create binaries via a verifiable path from the source code. The optional step to create a vendored directory within your project also helps with locally storing and satisfying dependencies for your project.

Compiled binaries are also an important piece of the Go ecosystem. Go also lets you build your binaries for other target environments, which can be useful if you need to cross-compile a binary for another computer architecture. Having the ability to build a binary that can run on any platform helps you to rapidly iterate and test your code to find bottlenecks on alternate architectures before they become more difficult to fix. Another key feature of the language is that you can compile a binary on one machine with the OS and architecture flags, and that binary is executable on another system. This is crucial when the build system has high amounts of system resources and the build target has limited computing resources. Building a binary for two architectures is as simple as setting build flags:

To build a binary for macOS X on an x86_64 architecture, the following execution pattern is used:

GOOS=darwin GOARCH=amd64 go build -o myapp.osx

To build a binary for Linux on an ARM architecture, the following execution pattern is used:

GOOS=linux GOARCH=arm go build -o myapp.linuxarm

You can find a list of all the valid combinations of GOOS and GOARCH using the following command:

go tool dist list -json

This can be helpful in allowing you to see all of the CPU architectures and OSes that the Go language can compile binaries for.

Benchmarking overview

The concept of benchmarking will also be a core tenant in this book. Go's testing functionality has performance built in as a first-class citizen. Being able to trigger a test benchmark during your development and release processes makes it possible to continue to deliver performant code. As new side effects are introduced, features are added, and code complexity increases, it's important to have a method for validating performance regression across a code base. Many developers add benchmarking results to their continuous integration practices to ensure that their code continues to be performant with all of the new pull requests added to a repository. You can also use the benchstat utility provided in the golang.org/x/perf/cmd/benchstat package to compare statistics about benchmarks. The following sample repository has an example of benchmarking the standard library's sort functions, at https://github.com/bobstrecansky/HighPerformanceWithGo/tree/master/1-introduction.

Having testing and benchmarking married closely in the standard library encourages performance testing as part of your code release process. It's always important to remember that benchmarks are not always indicative of real-world performance scenarios, so take the results you receive from them with a grain of salt. Logging, monitoring, profiling, and tracing a running system (as will be discussed in Chapter 12, Profiling Go Code; Chapter 13, Tracing Go Code; and Chapter 15, Comparing Code Quality Across Versions) can help to validate the assumptions that you have made with your benchmarking after you've committed the code you are working on.

You have been reading a chapter from
Hands-On High Performance with Go
Published in: Mar 2020
Publisher: Packt
ISBN-13: 9781789805789
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image