Files
FlexLove/profiling/__profiles__/memory_profile.lua
2025-11-20 11:36:41 -05:00

243 lines
6.6 KiB
Lua

-- Memory Profile
-- Tests memory usage and GC patterns
local FlexLove = require("FlexLove")
local profile = {
elementCount = 100,
maxElements = 500,
minElements = 50,
root = nil,
memoryStats = {
startMemory = 0,
currentMemory = 0,
peakMemory = 0,
gcCount = 0,
lastGCTime = 0,
},
updateTimer = 0,
createDestroyTimer = 0,
createDestroyInterval = 2, -- seconds between create/destroy cycles
}
function profile.init()
FlexLove.init({
width = love.graphics.getWidth(),
height = love.graphics.getHeight(),
gcStrategy = "manual", -- Manual GC for testing
})
-- Record starting memory
collectgarbage("collect")
collectgarbage("collect")
profile.memoryStats.startMemory = collectgarbage("count") / 1024 -- MB
profile.memoryStats.peakMemory = profile.memoryStats.startMemory
profile.buildLayout()
end
function profile.buildLayout()
-- Clear existing root
if profile.root then
profile.root = nil
end
profile.root = FlexLove.new({
width = "100%",
height = "100%",
backgroundColor = FlexLove.Color.new(0.05, 0.05, 0.1, 1),
positioning = "flex",
flexDirection = "vertical",
overflowY = "scroll",
padding = { horizontal = 20, vertical = 20 },
gap = 10,
})
-- Create elements container
local elementsContainer = FlexLove.new({
width = "100%",
positioning = "flex",
flexDirection = "horizontal",
flexWrap = "wrap",
gap = 5,
marginBottom = 20,
})
for i = 1, profile.elementCount do
local hue = (i / profile.elementCount) * 360
local color = FlexLove.Color.new(
0.3 + 0.5 * math.sin(hue * math.pi / 180),
0.3 + 0.5 * math.sin((hue + 120) * math.pi / 180),
0.3 + 0.5 * math.sin((hue + 240) * math.pi / 180),
1
)
local box = FlexLove.new({
width = 50,
height = 50,
backgroundColor = color,
borderRadius = 8,
margin = { horizontal = 2, vertical = 2 },
})
elementsContainer:addChild(box)
end
profile.root:addChild(elementsContainer)
-- Memory stats panel
local statsPanel = FlexLove.new({
width = "100%",
padding = { horizontal = 15, vertical = 15 },
backgroundColor = FlexLove.Color.new(0.1, 0.1, 0.2, 0.9),
borderRadius = 8,
positioning = "flex",
flexDirection = "vertical",
gap = 5,
})
local currentMem = collectgarbage("count") / 1024
local memGrowth = currentMem - profile.memoryStats.startMemory
statsPanel:addChild(FlexLove.new({
text = string.format("Memory Profile | Elements: %d", profile.elementCount),
fontSize = 18,
textColor = FlexLove.Color.new(1, 1, 1, 1),
}))
statsPanel:addChild(FlexLove.new({
text = string.format("Current: %.2f MB | Peak: %.2f MB", currentMem, profile.memoryStats.peakMemory),
fontSize = 14,
textColor = FlexLove.Color.new(0.8, 0.8, 0.8, 1),
}))
statsPanel:addChild(FlexLove.new({
text = string.format("Growth: %.2f MB | GC Count: %d", memGrowth, profile.memoryStats.gcCount),
fontSize = 14,
textColor = FlexLove.Color.new(0.8, 0.8, 0.8, 1),
}))
statsPanel:addChild(FlexLove.new({
text = "Press G to force GC | Press +/- to adjust elements",
fontSize = 12,
textColor = FlexLove.Color.new(0.7, 0.7, 0.7, 1),
}))
profile.root:addChild(statsPanel)
end
function profile.update(dt)
profile.updateTimer = profile.updateTimer + dt
profile.createDestroyTimer = profile.createDestroyTimer + dt
-- Update memory stats every 0.5 seconds
if profile.updateTimer >= 0.5 then
profile.updateTimer = 0
profile.memoryStats.currentMemory = collectgarbage("count") / 1024
if profile.memoryStats.currentMemory > profile.memoryStats.peakMemory then
profile.memoryStats.peakMemory = profile.memoryStats.currentMemory
end
-- Rebuild to update stats display
profile.buildLayout()
end
-- Automatically create and destroy elements to stress GC
if profile.createDestroyTimer >= profile.createDestroyInterval then
profile.createDestroyTimer = 0
-- Destroy old elements
profile.root = nil
-- Create new elements
profile.buildLayout()
end
end
function profile.draw()
if profile.root then
profile.root:draw()
end
-- Overlay info
love.graphics.setColor(1, 1, 1, 1)
love.graphics.print("Memory Stress Test", 10, love.graphics.getHeight() - 140)
love.graphics.print(
string.format("Elements: %d | Range: %d-%d",
profile.elementCount,
profile.minElements,
profile.maxElements
),
10,
love.graphics.getHeight() - 120
)
love.graphics.print(
string.format("Memory: %.2f MB | Peak: %.2f MB",
collectgarbage("count") / 1024,
profile.memoryStats.peakMemory
),
10,
love.graphics.getHeight() - 100
)
love.graphics.print(
string.format("GC Count: %d | Strategy: manual",
profile.memoryStats.gcCount
),
10,
love.graphics.getHeight() - 80
)
love.graphics.print("Press G to force garbage collection", 10, love.graphics.getHeight() - 60)
love.graphics.print("Press + to add 25 elements", 10, love.graphics.getHeight() - 45)
love.graphics.print("Press - to remove 25 elements", 10, love.graphics.getHeight() - 30)
end
function profile.keypressed(key)
if key == "=" or key == "+" then
profile.elementCount = math.min(profile.maxElements, profile.elementCount + 25)
profile.buildLayout()
elseif key == "-" or key == "_" then
profile.elementCount = math.max(profile.minElements, profile.elementCount - 25)
profile.buildLayout()
elseif key == "g" then
-- Force garbage collection
local beforeGC = collectgarbage("count") / 1024
collectgarbage("collect")
collectgarbage("collect") -- Run twice for thorough cleanup
local afterGC = collectgarbage("count") / 1024
profile.memoryStats.gcCount = profile.memoryStats.gcCount + 1
profile.memoryStats.lastGCTime = love.timer.getTime()
print(string.format("Manual GC: %.2f MB -> %.2f MB (freed %.2f MB)",
beforeGC, afterGC, beforeGC - afterGC))
profile.buildLayout() -- Update stats display
end
end
function profile.resize(w, h)
FlexLove.resize(w, h)
profile.buildLayout()
end
function profile.reset()
profile.elementCount = 100
collectgarbage("collect")
collectgarbage("collect")
profile.memoryStats.startMemory = collectgarbage("count") / 1024
profile.memoryStats.peakMemory = profile.memoryStats.startMemory
profile.memoryStats.gcCount = 0
profile.memoryStats.lastGCTime = 0
profile.updateTimer = 0
profile.createDestroyTimer = 0
profile.buildLayout()
end
function profile.cleanup()
profile.root = nil
collectgarbage("collect")
collectgarbage("collect")
end
return profile