How to measure garbage?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
idbrii
Prole
Posts: 34
Joined: Sat Jun 12, 2021 5:07 am

How to measure garbage?

Post by idbrii »

Is there a good way to measure how much garbage you're generating?

It seems pretty trivial to put love in a state where the garbage alloc never stabilizes. Take this code:

Code: Select all

local time = 0
local text = {
    garbage = love.graphics.newText(love.graphics.getFont(), ''),
    time = love.graphics.newText(love.graphics.getFont(), ''),
}

function love.update(dt)
    time = time + dt

    local gc_alloc = string.format("gc_alloc: %0.2f KB", collectgarbage('count'))
    text.garbage:set(gc_alloc)
    -- Enabling this line prevents gc count from stabilizing!
    --~ text.time:set(time)

    if love.keyboard.isDown("escape") then
        love.event.quit()
    end
end

function love.draw()
    love.graphics.draw(text.garbage, 60,535)
    love.graphics.draw(text.time, 60,435)
end

function love.quit()
    print('time:', time)
end
As is, it stabilizes after 20 seconds. But uncommenting the second Text:set call prevents gc count from stabilizing even after several minutes. (And the same result with love.graphics.print.)

I want to see how big the effect of pooling some objects have on gc usage, but I can't just print gc count since it's constantly increasing from factors outside my control.
User avatar
idbrii
Prole
Posts: 34
Joined: Sat Jun 12, 2021 5:07 am

Re: How to measure garbage?

Post by idbrii »

Writing out that question helped me figure it out. You can collect garbage before measuring and use that to zero out other garbage so you only measure one specific function:

Code: Select all

local function measure_garbage(fn, ...)
    collectgarbage('collect')
    local tare = collectgarbage('count')
    collectgarbage('stop') -- prevent gc during measurement
    fn(...)
    local count = collectgarbage('count') - tare
    collectgarbage('restart')
    return count
end
An example using that function where you can use up/down to generate more/less garbage per frame:

Code: Select all

local amount = 1
local time = 0
local text = {
    garbage = love.graphics.newText(love.graphics.getFont(), ''),
    time = love.graphics.newText(love.graphics.getFont(), ''),
}

local function generate_garbage()
    local a = ''
    for i=1,amount*100 do
        a = a .. string.format("%s %s", i * 2, i)
    end
    return a
end
function love.update(dt)
    time = time + dt

    local gc_alloc = string.format("gc_alloc: %0.2f KB", measure_garbage(generate_garbage))
    text.garbage:set(gc_alloc)
    text.time:set(time)

    if love.keyboard.isDown("escape") then
        love.event.quit()
    elseif love.keyboard.isDown("up") then
        amount = amount + 1
    elseif love.keyboard.isDown("down") then
        amount = amount - 1
    end
end

function love.draw()
    love.graphics.draw(text.garbage, 60,535)
    love.graphics.draw(text.time, 60,435)
    love.graphics.print(string.format("Iterations: %s", amount * 100), 60,415)
end
User avatar
pgimeno
Party member
Posts: 3551
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to measure garbage?

Post by pgimeno »

idbrii wrote: Fri Jul 02, 2021 4:55 pmI can't just print gc count since it's constantly increasing from factors outside my control.
They are in your control. Well, sorta, kinda.

The garbage comes from Löve, and from the strings you generate. Generating the same string over and over does not generate new garbage, that's why the first version stabilizes. The second version doesn't because the string changes every time you generate it.

But of course, if you want to print something that changes, you either need strings, or reinventing the wheel. Just for fun, let's reinvent the wheel a little:

Code: Select all

local text = {}
local chars = {}
for i = 0, 255 do chars[i + 1] = string.char(i) end

local function num_to_string(n)
  for i = 1, #text do text[i] = false end
  local neg
  if n < 0 then
    neg = true
    n = -n
  end
  local frac = n % 1
  n = n - frac
  local i = 0
  while n ~= 0 do
    i = i + 1
    local digit = n % 10
    text[i] = chars[digit + 49]
    n = (n - digit) / 10
  end
  if neg then
    i = i + 1
    text[i] = "-"
  end

  -- Reverse the text so far
  for j = 1, math.floor(i / 2) do
    text[j], text[i - j + 1] = text[i - j + 1], text[j]
  end

  if frac ~= 0 then
    i = i + 1
    text[i] = '.'
    for j = 1, 6 do
      frac = frac * 10
      digit = math.floor(frac)
      frac = frac - digit
      i = i + 1
      text[i] = chars[digit + 49]
    end
  end
end

function love.draw()
  local cnt = collectgarbage("count")
  num_to_string(cnt)
  local i = #text
  while text[i] == false do i = i - 1 end
  for j = 1, i do
    love.graphics.print(text[j], j*8, 0)
  end
end
Note how Löve generates garbage too. Moving the mouse, pressing keys, and probably other things will raise the count. That's what is not under your control.
User avatar
idbrii
Prole
Posts: 34
Joined: Sat Jun 12, 2021 5:07 am

Re: How to measure garbage?

Post by idbrii »

Ah, of course. I didn't think about the tostring that was happening inside Text:set!

> Note how Löve generates garbage too. Moving the mouse, pressing keys, and probably other things will raise the count. That's what is not under your control.

I guess not generating garbage shouldn't be my goal. Instead, I should isolate where I'm measuring garbage. My `measure_garbage` seems to do that, but if anyone has other ideas I'd love to hear them!
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 68 guests