Go: String & Conversion Optimization

ℹ️ This article is based on Go 1.14.
In Go, converting an array of bytes to a string could involve a memory allocation along with a copy of the converted string. However, converting bytes to a string just to satisfy a code constraint, such as a comparison in a switch statement or as a key in a map, is definitely a waste of CPU time. Let’s review some cases and the optimizations done.
Conversion
The conversion from an array of bytes to a string involves:
- An allocation for the new string on the heap if the variable outlives the current stack frame.
- A copy of the bytes to the string.
For more details about the escape analysis, I suggest reading my article “Go: Introduction to the Escape Analysis.”
Here is a simple program that goes through those two steps:

Here is a diagram of that conversion:

If you want to know more about the copy function, I suggest you read my article “Go: Slice and Memory Management.”
At the runtime, Go provides only one optimization during the conversion. If the converted array of bytes contains actually one byte, the returned string will point to a static array of byte embedded in the runtime:

However, if this string is modified later, it will allocate memory from the heap before assigning the new value.
The Go compiler also provides some optimizations that can skip the two phases of the conversion we have seen.
Switch
Let’s start with an example of conversion to string for a comparison purpose:

The examples used to illustrate the string optimizations will force the allocation on the heap by using the getBytes
function. It avoids some other compiler optimizations that could hide the string optimizations introduced here.
In this example, the conversion is used for the switch
instruction only, and Go is able to avoid the conversion since it just needs to compare the actual content. Go actually optimizes the code by removing the conversion and pointing directly to the backed array of bytes:

We can also see the exact optimization with the generated assembly:

Go uses the returned bytes directly in the comparison. It checks the size of the first case
statement with the array of bytes and then the string itself. Assigning the string outside of the switch
would lead to an allocation since the compiler would not know where this is string used later.
Optimizations
The instruction switch
is not the only case optimized with the string conversions. Go compiler applies this behavior to other cases such as:
- Accessing to an element of a map. Here is an example:

While accessing the map, there is actually no conversion needed making the access faster.
- String concatenation. Here is an example:

The concatenation with an array of bytes and some string does not lead to any allocation or conversion of the bytes. The concatenation will refer directly to the backed array, as seen previously.
- String comparisons. Here are some examples:

This case is similar to the switch
. It compares first the size of the string and the size of the array of bytes, and then compare the strings.