hi there serdar yagidlab for infoworld in this episode of smartgo we're going to examine how to
keep track of the way go programs allocate memory and how to stay on top of how that can impact
performance and go programs memory allocations in go are a metric of how efficient the go program
is not just in terms of how much memory is used but how much time the go run time spends managing
memory and how often the garbage collector needs to clean things up fewer allocations means both
a more memory efficient program and a faster one now to talk about this i need to explain
the two different ways go programs handle memory the stack or maybe stacks plural and the heap
stack memory in go consists of a block of memory usually about two kilobytes that's
allocated to each function call or go routine and is local to it heap memory is allocated
outside of any one function call or go routine it's for the sake of the whole application
variables allocated memory can live in either one of these two places the stack or the heap
depending on where they're created and how they behave and the rule of thumb is stack
memory is faster but it's limited heat memory is slower because it involves the garbage collector
but it's more flexible most simple programs aren't demanding enough for us to worry about whether a
variable will live on the stack of the heap but if you're writing a server or some program that runs
operations in a tight loop those things may need special attention for how they work with memory
if we have thousands of allocations taking place in a loop that's going to slow things down now
when you compile a go program the go compiler does some work to figure out where best to allocate
variables if we can prove a variable never escapes from its function that it's always local to that
function then that variable gets placed on the stack if not it gets placed on the heap what's
more the go compiler can tell us explicitly when variables have to be moved to the heap as a way to
be conscious of how to optimize their use so here i have a very simplified example of how this works
this program has two functions that sort of do the same thing one takes in an integer and returns the
same value incremented by one the other performs the same operation but it does it a different
way it returns a pointer to a newly allocated variable with that value not the value itself
so if i'm going to crunch this program with the compiler and find out where any heap allocations
might be taking place i can just compile it with a set of command line flags like this
the gc flags flag indicates that i want to pass some additional flags specifically to address
how garbage collection and memory management work and when we do that in conjunction with this flag
here the m flag that gives us explicit details about optimizations so if i run this we're going
to get the following output we learned that b in the second function is allocated on the heap
because it escapes the function that it's in see it's a pointer that's passed between functions
so it's not going to be easy to predict what's going to happen to it this variable a a on the
other hand in the first function does not behave like this it's a copied value so it's path is
predictable and the variable itself does not leave the function at any time by the way side note
you'll notice that i have these two comments go colon no inline in front of each function now this
really deserves its own video but what these are are hints to the compiler and they indicate that
the functions in question should not be inlined in other words they should not have their code copied
to wherever they are called as a performance measure to avoid a function call if we remove
these annotations and we recompile the program you'll notice that we're going to get a different
output because one of the things the compiler will try to do is make inlining optimizations this in
turn can reduce the number of allocations that are performed in a program but in this case i'm
deliberately forcing inlining off to ensure that the stack and the heap behaviors that i've been
describing here will stay consistent through this example so back to our main example and this will
lead me in fact to the other tool that you can use to inspect a program's allocation behavior
the benchmem flag in a previous video linked below i talked about how to create tests for
a given program and tests can also be used as benchmarks and that's precisely what i
have here i've created a simple test that also operates as a benchmark for my simple application
now when i run this test like so with these flags bench and benchmem i receive a report that tells
me how many allocations per invocation of the tested functions took place the good news is
that i only have one allocation per op which is in line with there being only one variable escaping
to the heap in our operation watch this if i go back to my program and i turn off the calls that
generate that escaped variable and then rerun my test code that number will drop back to zero
now it's not realistic at all to expect a given function to have zero allocations for instance
if you have a format.printf command that will automatically create allocations within itself so
if you have that somewhere in a function you're going to have at least one allocation and you
can't do anything about it save getting rid of the printf the goal though is not to produce
allocations to zero what matters is keeping the number of allocations down especially in
functions that are called often again that's why inlining becomes useful or when you have a tight
loop and again you may never get this down to zero and that shouldn't really be your goal but
you should look for hot spots where allocations do happen and you should try to minimize them
so to sum up goes runtime controls memory allocations which can be placed in a heap
for the whole process or into a stack for individual functions you can use tracing tools
to determine which variables end up where stack or the heap and also determine how many memory
allocations are performed per operation the fewer allocations you have especially in tight loops
the more likely your program's not going to suffer from memory related performance issues
that's it for this video if you liked it leave a comment below
don't forget to subscribe to the tech talk channel on youtube and for more smart go
and more dev with serdar be sure to follow me on facebook youtube and infoworld.com