by Dave Astels
When we started implementing SteelSeries Engine3, we made the unusual choice of writing a central part of the system in Google’s Go language. In this post, I’ll explore what Go is, why we used it, and what we’re doing with it.
Background: What is Go?
Go is a relatively new programming language from Google, started in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson and open-sourced in Nov. 2009. Pike and Thompson were two of the original Unix team at Bell Labs, and were the main two people on the Plan 9 project.
To quote a talk given by Rob Pike at the SPLASH 2012 conference:
“Go is a compiled, concurrent, garbage-collected, statically typed language developed at Google. Go is efficient, scalable, and productive. Go was designed to address the problems faced in software development at Google, which led to a language that is not a breakthrough research language but is nonetheless an excellent tool for engineering large software projects. Go is a programming language designed by Google to help solve Google’s problems, and Google has big problems.”
We’re using Go
Go is a bit of an unusual choice for a consumer application. It’s use is increasing for backend services, but you generally don’t see much of it beyond that.
We’re a polyglot shop and use 7 different programming languages (I’m not counting HTML and CSS as programming languages) in the Engine 3 technology stack: from C in the driver layer, to Erlang in the cloud. We believe in using the most suitable tool for the job at hand. We use Go to write the background service that is always running: managing your configurations, devices, application bindings, and providing access to the user interface.
Why are we using Go?
We had several requirements for the resident service when we started planning Engine3:
- performance: it had to be fast, consuming minimal CPU resources, since it was always running in the background and we wanted to have minimal impact on the system while the user was gaming;
- size: it had to consume a minimal amount of memory since it was always present, for the same reasons performance was important;
- portable: it had to be cross platform since we needed to run on Windows and OSX, and Linux in time;
- manageable: it’s cross platform capabilities could not come at the cost of manageability; we had to have a single codebase with only minor differences for each supported platform. Those differences had to be easy to manage in development, build, and execution contexts.
With these in mind, we looked at several languages:
- C#: VM based, to not as fast and small as we’d like. Also not the best cross platform story for a fairly low level application. We had initially hoped to salvage what we could from the previous version of Engine (which was written in C#), but that proved to not be a reasonable expectation.
- Java: Similar performance story to C#, but has a great cross platform story.
- C++: We considered C++ very seriously as is it fast and relatively compact, and available on all platforms. Its problems are more related to developer friendliness and efficiency. Memory management and language/runtime complexity are serious detriments.
- C: It’s hard to imagine anything that would let you get smaller or faster than C; but it shares many of C++’s problems, although being far more elegant.
- Objective-C: While we didn’t explore this option very much, it was a candidate. It’s major shortcoming was the relative lack of support on non-Apple platforms.
We were discussing this on the walk into Greektown for lunch one day and finding fault with each alternative when the idea of using Go occurred to me. I was working for Google when Go was starting to be publicized internally, and I had experimented with it and written about it when it was released to the public, so I was fairly familiar with it and it’s features.
Some of the team had heard of Go, but none had any experience with it. As we discussed it, we found that Go seemed to satisfy our requirements without incurring any of the costs of the other options we had considered. After doing some more research, we decided that it was the way to Go.
The major features we liked:
- Cross platform
- Memory management builtin
- Concurrency builtin
- Clean and modern
- Not bogged down with all objects all the time (i.e. it espouses composition over inheritance)
What are we using Go for?
The user interface interacts with the resident app through a RESTful, JSON based, HTTP API. This API is part of the Go app. The app also pushes information to the user interface via websockets. All database management is done by Go code. It is also responsible for managing, and interacting with, the lower layers of our stack which in turn interact with our hardware. Those layers are implemented in C/C++ and we use cgo to bridge between C and Go worlds. The Go app is also responsible for interacting with our data cloud.
One shortcoming of Go is that it can’t dynamically load code at runtime, although apparently that’s planned. We needed that capability, so we went the route of an embedded scripting language. I wrote a simple Lisp system in Go to let us dynamically script what we needed to. We did it as a separate project with the goal of open-sourcing it. That will happen soon.
We’ve been happy with Go. Even though nobody but myself had done anything in Go previously (and I’d only done some dabbling) we were ramped up and productive in a very short period of time. We’ve done some cool thing in Go, and we’ll be sharing as much as we can with you here, and even opensourcing some things.