Florian Wesch

Understanding garbage collection

Posted Jan 29 2015 by Florian Wesch

Having an eye on performance is critical when developing visualizations using info-beamer for the Raspberry PI. One of the things you have to consider when writing your code is garbage collection.

Normally you don't have to worry about cleaning up resources. Lua is a garbage collected language. So when you allocate memory for example by creating a new string, you don't have to worry about deallocating that string again. Lua will keep track of its usage and clean it up once it's no longer used. This makes it easier to develop and avoids a lot of problems that lead to crashes and unexpected behaviour in other languages that don't offer garbage collection.

Types of garbage collection

There are different ways a garbage collector might work. One of them is reference counting. Each object counts how often it is referenced. A reference can be a local variable pointing to that object or it being included in a collection. If the reference counter is equal or greater than 1 it means that the object is still in use. On the other hand once the counter drops to 0 the object can immediately be removed and all resources used can be freed.

Another way of doing garbage collection is tracing garbage collection. Objects do not store their reference count. Instead from time to time the collector determines which objects are still reachable (and therefore cannot be collected yet) by starting from root objects (like function arguments, local or global variables) and jumps from object to object until it has traversed the complete reachability graph. This is called the mark phase, as all reachable objects are marked. All objects that the collector didn't visit are considered garbage and can be removed. This is done using the sweep phase.

Since both mark and sweep phases are expensive they are usually done incrementally. So each phase is spread out over multiple collector runs. This prevents big latency spikes that would otherwise occur if the garbage collection would do everything in one big step. But it also means that garbage collection using mark and sweep isn't instantaneous. There is always a delay between removing the last reference to a object and its collection.

Garbage collection used by info-beamer

Lua uses mark and sweep garbage collection. This means that objects allocated might take some time till they are being collected. Normally that's not a problem since objects are usually small enough so that keeping them around a bit longer doesn't matter too much.

Things are a bit different when using info-beamer. info-beamer allows you to render child nodes into a texture object. While the texture object itself is small and only takes a couple of bytes, the resources allocated by the OpenGL system are huge: A texture with a size of 1024×768 uses 3MB of video memory.

Lets have a look at this code:

function node.render()
    local child = resource.render_child("child")
    child:draw(0, 0, WIDTH, HEIGHT)
end

node.render is called each frame. Each time the call to resource.render_child will allocate a new texture object. If the child node is 1024×768 then that is 3MB allocated each frame. Assuming 60 frame per second that's 180MB alone allocated here each second.

Each time the control exists from the node.render function the local variable child is no longer reachable. Therefore the texture object can be considered garbage. But since mark and sweep type garbage collectors take a moment to actually remove such garbage lots of unreachable texture objects can accumulate before they are finally collected. By default the garbage collection in Lua is too slow to clean up fast enough. So code like this would exhaust all available video memory in a couple of seconds. There are two ways to solve this:

Collecting harder

Before info-beamer pi 0.8.2, the garbage collection was triggered in a way that it most likely removed garbage objects in the same frame they were generated. This comes at a cost: The garbage collection has to do a complete mark and sweep phase each frame. Depending on the number of total objects used this can be expensive and hurt performance.

If you know that you don't allocate expensive objects (by for example using resource.render_child) you can call node.set_flag "slow_gc" (notice that you need info-beamer pi 0.8.2 for that). This will put the garbage collector into a slower and more relaxed mode which increases performance since it doesn't have to do the whole mark and sweep each frame. But it also means that it can take a moment to collect unreachable objects. So how would you safely use resource.render_child then? Simple:

function node.render()
    local child = resource.render_child("child")
    child:draw(0, 0, WIDTH, HEIGHT)
    child:dispose() -- new!
end

Notice the call to dispose()? That's a way to tell info-beamer to clean up resources used by the texture object. The video memory used by that texture is released. Now the child variable only takes up a few bytes and it doesn't hurt if it is kept around for a couple of seconds. Of course you can no longer draw that texture object again. Doing so will result in an error since the texture data is no longer available.

The new way

Since 0.8.2 info-beamer will by default start with the slower collection mode. So you get minimum impact from garbage collection. Only when you start using resource.render_child or resource.create_snapshot info-beamer will switch back to the frame based garbage collection that was used by default in earlier versions.

Once you added the correct dispose calls to your expensive texture objects you can manually switch back to the slow mode:

node.set_flag "slow_gc"

function node.render()
    local child = resource.render_child("child")
    child:draw(0, 0, WIDTH, HEIGHT)
    child:dispose()
end

The call to node.set_flag will tell info-beamer that you know what you are doing and it will switch to the slow garbage collection mode that is going to make your code run faster.

You can see the state of the garbage collection in the information output that is printed in the console every 10 seconds:

   mem   fps   rps allocs width height update  event flag  name (alias)
-----------------------------------------------------------------------
 250kb  60.0   0.0    0.0   100    100   0.0%   0.0%  R-S  '- root (-)
 187kb  60.0   0.0    0.0  1024    768   0.0%   0.0%  R-s     '- child (-)
-----------------------------------------------------------------------

The third character in the flag column displays the current garbage collection mode:

f automatically set to frame based GC
s automatically set to slow mode (this is the default since 0.8.2)
F manually set to frame based GC using node.set_flag("slow_gc", false). Manually setting this mode usually makes no sense.
S manually set to slow gc mode using node.set_flag "slow_gc". You have to manually dispose expensive resources using dispose().

Summary

info-beamer since 0.8.2 is a bit smarter about how garbage collection is done. Usually you don't have to worry about details since info-beamer will try to do the right thing. In code that doesn't use resource.render_child or resource.create_snapshot this will result in a slight performance boost.

If you understand garbage collection, info-beamer will provide you the means to archive better performance even when using lots of expensive texture objects.


Read more...


info-beamer.com offers the most advanced digital signage platform for the Raspberry Pi. Fully hosted, programmable and easy to use. Learn more...


Get started for free!

Trying out the best digital signage solution for the Raspberry Pi is totally free: Use one device and 1GB of storage completely free of charge. No credit card required.


Follow @infobeamer on twitter to get notified of new blog posts and other related info-beamer news. It's very low traffic so just give it a try.

You can also subscribe to the RSS Feed RSS feed.


Share this post:

Share using Twitter


Questions or comments?
Get in contact!