As a Roblox developer, it is currently too hard to work with nested memory categories.
Problem case:
-- Script
local Module = require(script.Module)
Module.step()
-- Module
local function doExpensiveOperation()
debug.setmemorycategory("Module.ExpensiveOperation")
-- do something...
debug.resetmemorycategory() -- Oops! We reset the category to "Script" instead of "Module"
end
function module.step()
debug.setmemorycategory("Module")
doExpensiveOperation()
-- Oops! Our memory category was reset to "Script" instead of "Module",
-- so doSomethingElse() will be categorized wrong!
doSomethingElse()
debug.resetmemorycategory()
end
Potential solution: debug.withmemorycategory
-- Script
local Module = require(script.Module)
Module.step()
-- Module
local function doExpensiveOperation()
debug.withmemorycategory("Module.ExpensiveOperation", function()
-- do something...
end)
end
function module.step()
debug.withmemorycategory("Module", function()
doExpensiveOperation()
doSomethingElse()
end)
end
Potential solution: debug.getmemorycategory
-- Script
local Module = require(script.Module)
Module.step()
-- Module
local function doExpensiveOperation()
local preMemoryCategory = debug.getmemorycategory()
debug.setmemorycategory("Module.ExpensiveOperation")
-- do something...
debug.setmemorycategory(preMemoryCategory)
end
function module.step()
debug.setmemorycategory("Module")
doExpensiveOperation()
doSomethingElse()
debug.resetmemorycategory()
end
Comments
Most big games use a lot of modules. Assigning memory to the script that started a coroutine is an okay and very flexible default, but with a lot of modules may be misleading. We wouldn’t mind working around this, but we can’t do so comprehensively! In the case that there are nested calls that we want to track the memory of, it’s impossible to return back to the memory category one level up the stack. We either reset
back to the default category or we guess what the memory category was (by e.g. looking at the stack with debug.info
). This is inaccurate and might lead some developers to spend time investigate the incorrect modules for memory use.
A comprehensive solution to this is a built-in function that switches to a category on the start of a function call and switches back to the pre-call custom category on end or on error*. A simpler solution is a getmemorycategory
function which would let developers implement withmemorycategory
themselves.
The only possible work-around for this problem case is to wrap every setmemorycategory
call to implement getmemorycategory
ourselves. This is not compatible between modules by different authors, so using any third party code risks your memory categories being incorrect.
* switching back on error is important because a coroutine can continue after error with pcall
and errors are a likely place that custom memory category stacks would get tripped up on.
If this issue is addressed, it would improve my development experience because I will have more accurate memory categories to work with, making debugging memory leaks significantly easier.