Why does this snippet of code eat my CPU?

Why does this snippet of code eat my CPU?

Discuss Lua based Spring scripts (LuaUI widgets, mission scripts, gaia scripts, mod-rules scripts, scripted keybindings, etc...)

Moderator: Moderators

User avatar
Caydr
Omnidouche
Posts: 7179
Joined: 16 Oct 2004, 19:40

Why does this snippet of code eat my CPU?

Post by Caydr »

The following bit of code causes my game to freeze for, strangely enough, exactly 0.25 of a second. (sarcasm)

Code: Select all

local clock = os.clock
~~~~
function sleep(n)
  local t0 = clock()
  while clock() - t0 <= n do end
end
~~~~
function widget:MousePress(mx,my,mb)
	if (mb == 2) then
		middledown = clock()
		repeat
			Spring.Echo('blabla')
			sleep(0.05)
		until ((clock() - middledown) > 0.25)
	end
end
I would find this at least sort of understandable if I used "while" instead of "if-with-a-repeat-and-pause", since it might do something silly like check to see if the condition was satisfied as fast as possible. Then again, CustomFormations uses "while" without any problems... maybe things concerning input are handled differently...?

I've got more than a few wild guesses why this might be happening, but I'm just frustrating myself and wasting time by endlessly rewriting the above code in different ways. For all I know it could simply be a mouse input bug, as was the culprit with some other things I've become familiar with recently.

Can someone help me understand what I'm doing wrong here?

Really enjoying this, by the way. I feel like I'm back in school, running into things that I can't do due to lack of knowledge rather than lack of time. I guess I could say it's refreshing... I'm not saying this to say "look at what a super-genius I am", but I really appreciate things that genuinely challenge my creativity rather than my attention span.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Why does this snippet of code eat my CPU?

Post by zwzsg »

Well, because you coded it to wait exactly 0.25s?

I don't know why you'd want that, but I can tell you freezing Spring everytime someone press the mouse middle button won't make your widget popular.

Maybe you are confusing Lua and bos...
User avatar
aegis
Posts: 2456
Joined: 11 Jul 2007, 17:47

Re: Why does this snippet of code eat my CPU?

Post by aegis »

sleep in normal code blocks the thread, which will make everything freeze.
to wait for a period of time, check against a timer or clock in one of the Update() callins
User avatar
Caydr
Omnidouche
Posts: 7179
Joined: 16 Oct 2004, 19:40

Re: Why does this snippet of code eat my CPU?

Post by Caydr »

So why does using:
"while (bla - bla > 0.25)
do bla
if (bla - bla < 0.25)
break
end"

identically freeze my computer? Is it just checking every billionth of a millisecond to see if the conditions have been satisfied? This why I used pausing, I figured, "wait every 0.05" seconds and then re-check", would probably reduce the amount of unnecessary checking.
zwzsg wrote:Well, because you coded it to wait exactly 0.25s?
OH COME ON I EVEN SAID "SARCASM", I CAN SEE THE 0.25 IN THE CODE I WROTE. COME ON. REALLY, ZWZSG. REALLY.

BTW for a bit of fun, remove the pause and the until and watch your SSD's lifespan get cut in half while you try to find a way to close Spring. Maybe the disk writes should be cached in RAM and only written out every half second or so?
Tobi
Spring Developer
Posts: 4598
Joined: 01 Jun 2005, 11:36

Re: Why does this snippet of code eat my CPU?

Post by Tobi »

That'd make debugging impossible. (crash -> no chance anymore to commit the last (most relevant) lines to the disk)
User avatar
aegis
Posts: 2456
Joined: 11 Jul 2007, 17:47

Re: Why does this snippet of code eat my CPU?

Post by aegis »

aegis wrote:to wait for a period of time, check against a timer or clock in one of the Update() callins
SeanHeron
Engines Of War Developer
Posts: 614
Joined: 09 Jun 2005, 23:39

Re: Why does this snippet of code eat my CPU?

Post by SeanHeron »

Ok - my understanding is still a bit shallow, but basicly its so:
Unlike bos scripts (of which I know little - but that at least in effect run "parallel" to everything else), gadgets synced code (and from what you're saying I assume also their unsynced code, and widgets) get processed one after the other.
Ie for a certain callin (GameFrame or MousePress) the engine takes all the gagdets (and widgets) there are, and walks through them, one after the other (according to their layer order). It won't go on to the next until it's finished with the one it's processing - and the whole rest of the simulation get's to wait as well .
Cause otherwise, at least for synced code, you'd have real trouble with calculations in gadgets being done faster relative to the rest of the simulation on some players computers then on the next (oh - player 1's com got a HP boost from gadget before he get's struck by missile - player 2's doesn't and dies... :P).

Going with that knowledge, it should now be obvious that all the ways you were trying to solve it (ie do your stuff for .25 seconds - directly after/within the callin!) can only lead to the fail behaviour you're getting :P.

As aegis correctly pointed out, what you want to do is set a variable (and a timer value), and then check for the variable from widget:Update() [well - as well as the timer, and reset that after you're required period].
If you required greater precision you could also use widget:GameFrame(f), but that shouldn't usually be necessary.

Excuse my arguing from a Gadget perspective, it's where I know my way round best (but like I say, it seems the same applies to unsynced code / widgets)
User avatar
Caydr
Omnidouche
Posts: 7179
Joined: 16 Oct 2004, 19:40

Re: Why does this snippet of code eat my CPU?

Post by Caydr »

Tobi wrote:That'd make debugging impossible. (crash -> no chance anymore to commit the last (most relevant) lines to the disk)
I can appreciate where this is true - but we're rapidly moving towards widespread SSD adoption. Spring is the only program I know of that writes in such a potentially dangerous way with no safeguards of any kind. IIRC, Spring wrote 1024 times to my HDD in the space of a quarter second when I was first working on this script, but I hadn't yet added the "end when..." part to it. That's 250,000 writes a minute, and low-cost SSDs (which most people are buying) have a projected lifespan per sector of only tens of thousands. This means that a single lua script could really harm someone's computer if things went awry.

Just saying "well, don't use lua you don't trust" or "that's a stupid mistake" isn't good enough. Regular people will (reasonably) think that a game engine isn't going to damage their computer's hardware. Yes it's a stupid mistake, but if there were't stupid mistakes made all the time, we wouldn't have a help and support forum.

In a world of rickrolls and mudkips, we could really do without a wide-open invitation like this.

Perhaps run a separate process which handles writing to infolog and which won't be affected by a crash. Then just limit writes to every second or so.
User avatar
aegis
Posts: 2456
Joined: 11 Jul 2007, 17:47

Re: Why does this snippet of code eat my CPU?

Post by aegis »

if your ssd has wear leveling (which it probably will), you need to write the maximum number of cycles to most of the disk for it to be a problem. due to the drive's internal buffer, writing to the ssd 100k times might not use anywhere near 100k write cycles.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Why does this snippet of code eat my CPU?

Post by zwzsg »

Caydr wrote:Perhaps run a separate process which handles writing to infolog and which won't be affected by a crash. Then just limit writes to every second or so.
Hey, no, I don't wan't to risk losing debug info when Spring crash unexpectedly!

Why isn't the OS caching file writes anyway?
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Why does this snippet of code eat my CPU?

Post by Argh »

Just use GameFrame(), Caydr. I.E.:

Code: Select all

function widget:MousePress(mx,my,mb)
   if (mb == 2) then
         blahblah =somenumber, a table entry, whatever
   end
end

function widget:GameFrame(f)
if f % 8 < 0.1 then -- approximately every quarter-second, if people's CPUs aren't chugging
  if blahblah == somenumber, is a table with an entry at [1], etc.
then
-- do whatever you want here
-- reset blahblah back to nil, zero, whatever
end
end
end
For most things, this is really safe, and won't cause evil CPU-eating errors. As for the rest of this stuff... meh, this is sorta-real programming, Caydr. You can do various things that can work your hardware really hard, if you screw up badly enough. Just how it is- if you're worried about your SSD, use a SATA drive for paging RAM while working on Spring code, or just turn off paging entirely.
Ralith
Posts: 22
Joined: 06 Aug 2007, 04:43

Re: Why does this snippet of code eat my CPU?

Post by Ralith »

On linux/ext3+, at least, filesystem writes *are* cached--by the OS. I imagine Windows does something similar. It's not Spring's job to worry about that sort of thing.
User avatar
SpliFF
Posts: 1224
Joined: 28 Jul 2008, 06:51

Re: Why does this snippet of code eat my CPU?

Post by SpliFF »

exactly, it's called write-caching and it isn't the programs' job. Look it up, enable it if you want it (you generally need a UPS to make write-caching a sensible option).
User avatar
lurker
Posts: 3842
Joined: 08 Jan 2007, 06:13

Re: Why does this snippet of code eat my CPU?

Post by lurker »

Anyway, Caydr, the problem is that you're not reading the code literally enough. When it says while(x) wait() end, it means WHILE. It stays in that loop and does nothing else whatsoever until it escapes. If it was going to change the game behind your back there would have to be a lot of complicated interlock code to do anything in lua.
User avatar
Niobium
Posts: 456
Joined: 07 Dec 2008, 02:35

Re: Why does this snippet of code eat my CPU?

Post by Niobium »

Argh wrote:Just use GameFrame(), Caydr. I.E.:
Except that GameFrame is affected by the game speed, pausing, etc. Update(dt) is much more appropriate (where dt is roughly the time since the last Update call)
User avatar
Caydr
Omnidouche
Posts: 7179
Joined: 16 Oct 2004, 19:40

Re: Why does this snippet of code eat my CPU?

Post by Caydr »

Yeah that was my concern, I'd read elsewhere that conventional methods of "waiting" were affected by the game's speed. For this function, as it is part of mouse input, I need 0.25 seconds to really be 0.25 seconds.

I imagine I just don't know the proper way of doing what I'm trying to do. Actually I didn't even know there was a "while" command until a few days ago, so...

Can someone suggest a better way of handling this?

Code: Select all

function widget:MousePress(mx,my,mb)
   if (mb == 2) then
      middledown = clock()
      repeat
         Spring.Echo('blabla')
         sleep(0.05)
      until ((clock() - middledown) > 0.25)
   end
end
Basically what I need to do is just be sure that the mouse button has been held down for 0.25 consecutive real seconds before the script activates, else it resets and waits for the next time the mouse button goes down.
User avatar
Niobium
Posts: 456
Joined: 07 Dec 2008, 02:35

Re: Why does this snippet of code eat my CPU?

Post by Niobium »

MousePress -> remember current time
MouseRelease -> check new current time vs remembered time, if >0.25 sec -> fire

Something like that would be the best, no need for loops/sleeps that way. You can use os.clock() which gives you the seconds since lua started (so is nice for comparing differences in time), or use the slightly more complicated spring timer functions, which I think are more accurate.
SeanHeron
Engines Of War Developer
Posts: 614
Joined: 09 Jun 2005, 23:39

Re: Why does this snippet of code eat my CPU?

Post by SeanHeron »

You're welcome Caydr, I appreciate it.
Regret
Posts: 2086
Joined: 18 Aug 2007, 19:04

Re: Why does this snippet of code eat my CPU?

Post by Regret »

Code: Select all

local midclicktimer --placeholder
local pm1,pm2,pm3 --placeholder
function widget:Update()
	local _,_,m1,m2,m3 = Spring.GetMouseState() --get mouse button status
	if (m2 and (not pm2)) then --is pressed and was not pressed before == was pressed right now
		midclicktimer = os.clock() + 0.25 --initialize timer
	end
	if (midclicktimer ~= nil) then --timer initialized?
		if (m2) then --is button still pressed?
			if (os.clock() >= midclicktimer) then --enough time passed?
				midclicktimer = nil --reset timer
				yourfunction() --do shit
			end
		else --nope
			midclicktimer = nil --reset timer
		end
	end
	pm1,pm2,pm3 = m1,m2,m3 --remember last mouse state
end
User avatar
aegis
Posts: 2456
Joined: 11 Jul 2007, 17:47

Re: Why does this snippet of code eat my CPU?

Post by aegis »

polling is ugly <_<
Post Reply

Return to “Lua Scripts”