Page 1 of 5

Love CPU usage

Posted: Sun Jul 11, 2010 2:39 am
by kaikimi
is it only me, or do love games seem a little cpu intensive? I've tried out multiple games on the love arcade and they seem to really eat up cpu. I've also tried out the game the people made in this topic and its not even playable on my box.

viewtopic.php?f=3&t=1609&p=15730&hilit=gamecamp#p15730

I've tried it out on the dual core computer i have at work and it seems to work, but it still has its little skips here and there.

The pc i have at home isn't all that old, it's got plenty of ram and a pentium4 processor, so what gives? this just bad programming on the developers part, or is it something specific to love2d?

Re: Love CPU usage

Posted: Sun Jul 11, 2010 2:49 am
by bmelts
LÖVE tries to achieve maximum FPS at all times, which means it's going to use as much CPU as it can. The default run loop sleeps for about a millisecond every frame, but that still means it'll be eating a lot of your CPU.

The solution is to use love.timer.sleep in your love.update loop - by telling LÖVE to sleep, you remove some of the burden on the CPU. Here's a quick bit of code you can drop in your programs to help LÖVE and your CPU chill out:

Code: Select all

FPSCAP = 60 -- change if you want higher/lower max fps

function sleep(dt)
    local s = 1/FPSCAP - dt
    if s > 0 then love.timer.sleep(s*1000) end
end
Then just make the last line of love.update "sleep(dt)" and your program will relinquish any CPU time it doesn't need, hopefully giving your machine a bit of a breather. Note that if your program drops below its FPS cap, it's still going to be running full throttle.

Re: Love CPU usage

Posted: Sun Jul 11, 2010 6:06 pm
by kikito
:huh: I kind of assumed that this was done automatically by LÖVE!

I'll apply this little piece of code to PÄSSION inmediately.

I do believe that LÖVE should come with a default "relinquish the cicles I don't need" mechanism installed by default.

Re: Love CPU usage

Posted: Sun Jul 11, 2010 6:14 pm
by thelinx
Doesn't LÖVE have a vsync toggle? That should do the trick, right?

Re: Love CPU usage

Posted: Sun Jul 11, 2010 8:49 pm
by bartbes
It's on by default, I guess his video card/driver doesn't support vsync.

Re: Love CPU usage

Posted: Mon Jul 12, 2010 10:39 am
by kikito
Well I'm pretty sure my card admits vsync and I still notice lots of cpu usage (haven't applied anjo's code yet)

Re: Love CPU usage

Posted: Mon Jul 12, 2010 12:08 pm
by pekka
I would like to point out that Anjo's code does not work reliably. Here is a corner case that shows how badly it can go wrong; it is demonstrated by the first code block below. Granted, the FPSCAP of five is unrealistic, but it gives you enough time to see the lengths of frames on screen in real time. It also shows that despite the cap, the program can run at up to 10 FPS (which is exactly what happens on this computer I am on). This is not what I ask the program to do when I put the 5 in there.

You can also try running this with a higher FPSCAP. Turn off vsync and log the times it takes to run each frame, and you'll probably see a pattern where occasional very short frames appear between the frames of expected length. This is because going over the limit for one frame lets the program logic run the next frame as fast as possible. I get a result of about 102 FPS when I ask for 60 and set vsync off in conf.lua!

It would be better to have a solution that resulted in approximately equal length frames in any case, both under high load and low load from other processing in the game. And it should not depend on vsync, which can't be guaranteed to be usable on every computer your game might run on. I'm afraid this is not it:

Code: Select all

FPSCAP = 5 -- change if you want higher/lower max fps

local lastframe = nil
local diffs = {}
local LINES = 25

function love.update(dt)
    local s = 1/FPSCAP - dt
    if s > 0 then love.timer.sleep(s*1000) end
	if lastframe then
		local now = love.timer.getMicroTime()
		table.insert(diffs, now - lastframe)
	end
	lastframe = love.timer.getMicroTime()
	if #diffs > LINES then table.remove(diffs, 1) end
end

function love.draw()
	love.graphics.print('FPS: ' .. love.timer.getFPS(), 20, 20)
	for i = 1, #diffs do
		love.graphics.print(tostring(diffs[i]), 20, 20+i*20)
	end
end
This one has better behavior in this corner case. However, does it work so well with higher FPS and no vsync, and higher loads from other game logic? The timer granularity on some systems can also be an issue.

You be the judge! But remember that a good solution should be demonstrably a good solution--and in all cases you might encounter.

Code: Select all

FPSCAP = 5 -- change if you want higher/lower max fps

local lastframe = nil
local diffs = {}
local LINES = 25

function love.load()
	lastframe = love.timer.getMicroTime()
end

function love.update(dt)
	local slack = 1/FPSCAP - (love.timer.getMicroTime() - lastframe)
    if slack > 0 then love.timer.sleep(1000*slack) end
	local now = love.timer.getMicroTime()
	local diff = now - lastframe
	lastframe = now
	table.insert(diffs, diff)
	if #diffs > LINES then table.remove(diffs, 1) end
end

function love.draw()
	love.graphics.print('FPS: ' .. love.timer.getFPS(), 20, 20)
	for i = 1, #diffs do
		love.graphics.print(tostring(diffs[i]), 20, 20+i*20)
	end
end
Again, note that FPSCAP = 5 comes from wanting to read the output in real time!

In a real program, you should start keeping track of lastframe when the game actually begins, of course. It does you no good to know the first one lasted 30 seconds or whatever it takes for the game to load completely :)

Re: Love CPU usage

Posted: Mon Jul 12, 2010 1:23 pm
by Robin
pekka wrote:In a real program, you should start keeping track of lastframe when the game actually begins, of course. It does you no good to know the first one lasted 30 seconds or whatever it takes for the game to load completely :)
It does. It tells you you need to change your game loading code. ;)

Re: Love CPU usage

Posted: Tue Jul 13, 2010 5:34 am
by pekka
Fair enough, Robin, though you might have your game start slower because you start it from a network drive ;)

I'd like to add that there is a rather unlikely possibility of failure in my code too.

Code: Select all

local slack = 1/FPSCAP - (love.timer.getMicroTime() - lastframe)
if slack > 0 then love.timer.sleep(1000*slack) end
I don't know how exactly the results of getMicroTime wrap around, or if they do, but suppose they do so in a way that results in the parenthesized expression becoming a large negative number. The subtraction will flip its sign, and this will result in your game sleeping for a long time. That will be one frame to remember!

So, for extra paranoia, check the slack value for some reasonable upper bound too:

Code: Select all

-- not tested, just typed in the forum
if slack > 0 and slack < LIMIT then love.timer.sleep(1000*slack) end
I suppose you are not ever going to want to sleep longer than 1/FPSCAP, for example, so it might be a good value for the LIMIT. Now, the docs say the result of getMicroTime is limited to 24 hours, so this little bit of code might be needed once every 24 hours your game runs in one go. Ha ha. :)

This topic would deserve a bit more thorough analysis and explanation, and cleaner sample code, and when I have the time for it, I'll see if I can put up a reasonable Wiki page. (Or improve an existing page, if the topic already exists.)

Re: Love CPU usage

Posted: Tue Jul 13, 2010 6:15 am
by kikito
I'm definitively interested on that. The single thing that worries me about LÖVE is the processor - it's starting up a game and it goes up very quickly.

I'm watching this thread - please comment here it if/when you do your research.