-- 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