In optimizing software, especially when working with large data structures or frequently used ones, managing memory efficiently can lead to better performance and reduced resource usage. Here, we present an optimization we performed on the Junos XML struct for the Route-Table. When parsing data into go from a JunOs device we need a struct. How we define that struct has an impact on the overall memory usage. This of course may not be an issue but we want to ensure that the memory used is optimal and is as small a footprint as possible.
The main.go
will load 2x structs the orginal one RpcReply
and RpcReplyOptimized
and you will be presented with total allocations.
Initially, the RpcReply
struct for the Junos XML was defined as:
type RpcReply struct {
XMLName xml.Name `xml:"rpc-reply"`
// ... other fields ...
Cli struct {
Text string `xml:",chardata"`
Banner string `xml:"banner"`
} `xml:"cli"`
}
The primary concern here was the memory usage of individual fields, especially when the overall structure would be instantiated multiple times.
Instead of having the Cli
field embedded directly within our struct, we converted it into a pointer. This drastically reduced the memory footprint because we only needed to allocate memory for the pointer, rather than the entire struct every time.
The optimized RpcReplyOptimized
struct looked like this:
type RpcReplyOptimized struct {
XMLName xml.Name `xml:"rpc-reply"`
// ... other fields ...
Cli *Cli `xml:"cli"`
}
This ensures that the Cli
struct is only allocated once and then shared among all instances of RpcReplyOptimized
, saving significant memory.
In Go, due to memory alignment considerations, the order in which you declare fields in a struct can influence its memory usage. By grouping fields of similar types together, we can ensure Go doesn't overallocate memory due to padding.
For instance, rather than having:
struct {
a string
b int
c string
}
We reordered the fields like this:
struct {
a string
c string
b int
}
This takes advantage of the memory alignment of similar types and ensures no extra memory is wasted. Notice how we go from 152 bytes down to 128 bytes by doing this optimization. In addition we can make the Route-Table fields into a pointer. We do not see an improvement in the initial size of the struct since both will be 128 bytes but the memory footprint should differ when we load the struct with data. The RpcReplyWithTablePointer should be the one which is optimized the most to hold the minimum footprint.
=====================================================================================
Total Memory Usage StructType: RpcReply Originalmain.RpcReply => [152]
=====================================================================================
Total Memory Usage StructType: RpcReplyWithCliPointer main.RpcReplyWithCliPointer => [128]
=====================================================================================
Total Memory Usage StructType: RpcReplyWithTablePointer main.RpcReplyWithTablePointer => [128]
=====================================================================================
By adopting a strategic approach to struct layout and being conscious of Go's memory alignment behavior, we achieved a more memory-efficient representation of our Junos XML struct. As applications scale and data grows, such optimizations can lead to significant savings in resources and improved performance.