Are Go Generics Ready for Prime Time? – The New Stack

As we’ve discussed numerous times over the years, developers have been anticipating the addition of generics to Go for a long time now. The feature has been the most highly requested for more than a decade now and it finally arrived just last month to much celebration with the release of Go 1.18.

Since the release of the long-awaited feature, there have been tutorials, demos, and discussions aplenty, but this week the discussion has taken a bit of a turn, questioning whether the feature is really ready for prime time.

In a blog post on his company’s blog, Vice Martíwho works on performance for MySQL-compatible serverless database platform PlanetScale, writes that generics can make your Go code slower.

In his blog post, Martí says that, while there has been some opposition to generics for fear that the language will become “a verbose and Enterprisey Java-lite with Generic Factors or, most terrifyingly, a degenerate HaskellScript that replaces ifs with Monads,” others simply hope that generics will provide “a critical feature to implement clean and reusable code at scale.” Outside of this dynamic, however, exists a third party, to which he belongs: “systems engineers who are not excited about generics per se, but about monomorphization and its performance implications.”

There are generally two ways to implement generics, writes Martí. First, you can use “boxing”, wherein pointers help to abstract and make all “things” (his preferred non-technical term for this discussion) that a function will operate on “look and act the same way”. This is the approach used for Go interfaces, dyn Traits in Rust, and virtual classes in C++, but he says that they “are limited by their expressiveness and by their runtime overhead.” Second, you can use monomorphism, which essentially creates “a different copy of the function for every unique thing it must operate on” and has historically been the choice for implementing generics in various languages, as it “boils down to trading compiles longer times for significant performance gains in the resulting code.”

The downside of monomorphization, however, is that it creates a lot more code, and so Go took a bit of an in-between path, with an approach it calls “GCShape stenciling with Dictionaries.” Rick Branson, director of software engineering at PlanetScale, offered a bit of a summary of this point in an email.

“There are some very real cases where generating specific code for every type permutation of generic code will increase compile times and the resulting compiled executable size to the point where it is problematic. It can even result in slower code execution in some cases! So the Go compiler doesn’t actually generate new code for every single type permutation. It uses a number of heuristics, including ‘GC Shape Stenciling’ which makes trade-offs to keep the number of reasonable permutations. In some cases, these trade-offs reduce performance in an unfavorable way because the generated machine code can’t be that lean, mean, special purpose code. It must support a broader range of types when executed. This is a lot of what the blog post discusses,” Branson wrote.

Martí spends much of the blog post taking a very technical, and beautifully formatted, deep dive into the details of what makes this so, which we will let this summary stand in for, but if you’re dying for more specifics, they are there in spades. Moving past that, however, what we arrive at is a more succinct list of dos and don’ts for how to get the best of generics performance as it is currently designed, and Martí’s suggestions on how Go might amend its design to further increase runtime performance.

Martí summarized his opinion on the matter in an email, writing that he believes that “the right trade-off for a systems language (and any compiled language, really) is paying the upfront cost when compiling to generate a zero-cost generics abstraction at runtime. That’s the baseline, and the only sensible approach in my opinion: first, you make the compiler generate the best possible code in every circumstance. Then, you benchmark the compiler, and if it’s too slow, you work on optimizing the compiler. Incidentally, if the compiler for your language is written in your language (like it’s the case for Go), making it generate better code _also_ speeds up the compiler itself!”

This Week in Programming

  • Visual Studio 2022 Gets GitHub Copilot: Wherever you land on the ethical considerations around GitHub Copilot, the company’s so-called “AI pair programmer,” you might have to admit that it’s a fun tool to play with, and even possibly helpful. Until now, it has been available only online in GitHub Codespaces and in Visual Studio Code, but now the company has said that GitHub Copilot is now available for Visual Studio 2022. GitHub launched Copilot into a technical preview last year, and since then Visual Studio 2022, which launched late last year, has been the top requested IDE. While you still need to sign up for the waitlist, GitHub is adding more capacity as we speak, so make sure to sign up ASAP. Once you get an email saying you’re in, just follow the instructions and you’ll be up and running.
  • What’s So Harmful about Goto? Surely, by now, you’ve heard about various things being considered harmful, as initially sparked by Edsger Dijkstra’s 1968 paper “GOTO Considered Harmful.” Well, a little blog post crossed our feed this week that offered a bit of an explanation on the harmfulness of goto, an instruction later popularized in the BASIC programming language that could be used to skip around to different lines of code, and we thought it worthwhile to share. If you have ever wondered why goto is considered harmful, or perhaps argue “that goto being harmful is considered an overreaction,” as the author says some do, he argues that it is “because as complexity of a program increases, unstructured jumps can lead to an inability to understand what is happening in a program, even in small programs.” The argument is made by showing two nearly identical flow charts, with the one on the right differing only in that it has a single goto argument. “The flowchart on the left has 72 unique paths. The flowchart on the right with the dotted line signifying an unstructured jump has 93,748,416,840 unique paths,” the author explains, then adding “now imagine a program where there are 10 or 100 such jumps?” Seems harmful, indeed.
  • RedMonk’s Language Rankings Finds Stability: It may already be April on the calendar you’re reading, but the RedMonk Programming Language Rankings for January 2022 have just arrived and the analyst firm writes that “the story of this quarter’s run — as it has been for a few runs now — is stability,” a trend they have seen over recent years. In fact, 17 of the 20 languages ​​have remained stable for the past three quarters, and Redmonk ponders whether this is “representative of industry usage, or is it more akin to a process artifact?” Perhaps, they ponder, we are moving into a time “where languages ​​have found their respective niches and a level with their particular competition.” For all the details on what did (or mostly did not) move, head on over and read RedMonk’s analysis of the continually unchanging rankings, alongside the many caveats they offer. The biggest mover this quarter? C++ dropped down two spots, and your guess is as good as theirs, at this point. (Perhaps it’s the move to Rust, for memory safety?)

The New Stack is a wholly owned subsidiary of Insight Partners, an investor in the following companies mentioned in this article: PlanetScale.

Leave a Comment