Thanks, I got a little worried for a minute there.
So If I can attach references wherever I want, if I wanted a piercing projectile to only damage each specific entity it hits only one time, could I add an array of references to entities it's already hit onto the projectile, and then compare to see if one of those is the entity it's currently being compared against? So not an equality check, rather a check to see of two references point to the same table. I think I saw something once about being able to do this with metatables but I can't remember or find it. Is there a way to do this efficiently?
Edit: could I use __eq for this by attaching it to my class module and put the metatable comparison functionality (if that's how it's done) in there?
Optimization Stuff
Re: Optimization Stuff
If I understand you correctly, you just need to use == without any __eq redefinition. Using == on tables only gives true if they are the same object. It gives false if they are different objects, even if they have the same content. Example:
Code: Select all
a = {1}
b = {1}
c = a
print(a == b) -- prints false
print(a == c) -- prints true
- Felix_Maxwell
- Prole
- Posts: 24
- Joined: Wed Dec 04, 2019 3:15 pm
Re: Optimization Stuff
Wow, that's so... easy! Thanks!
Re: Optimization Stuff
You shouldn't leave the garbage collector enabled in a game, at all. You should collectgarbage("stop") and manually collectgarbage() only when appropriate (like level transitions). You should create all the tables you're going to need during initialization, and not create any during gameplay.
Remember, the garbage collector scans through all tables every time it runs, and it completely stops your game while doing so. That's bad.
Remember, the garbage collector scans through all tables every time it runs, and it completely stops your game while doing so. That's bad.
Re: Optimization Stuff
That's terrible advice. Disabling memory management and implementing your own strategy/heuristics is a recipe for disaster. It's nearly impossible to produce zero garbage in non-trivial interactive games. You'll end up sprinkling your game code with expensive collectgarbage calls as it grows, calling it more often than required - only to have it still occasionally crash in tight spots on weaker machines.Xii wrote: ↑Sat Dec 26, 2020 12:45 am You shouldn't leave the garbage collector enabled in a game, at all. You should collectgarbage("stop") and manually collectgarbage() only when appropriate (like level transitions). You should create all the tables you're going to need during initialization, and not create any during gameplay.
Avoid garbage where possible. Use object pooling and memoization. Tweak GC parameters if you must. Don't break your game by disabling garbage collection.
- Felix_Maxwell
- Prole
- Posts: 24
- Joined: Wed Dec 04, 2019 3:15 pm
Re: Optimization Stuff
Does changing the amount of garbage significantly change the CPU use of the GC? I thought it used most of it's time 'mark and sweep'-ing through all of the tables in the game. I assume you cut down garbage by nil-ing stuff out when you're done with it?
What do you mean by intermediate? do you mean something like:
Code: Select all
local arg = {} -- is this an 'intermediate' table?
arg.hp = 10
arg.speed = 5
function foo:new(arg)
self.hp = arg.hp
self.speed = arg.speed
end
I know the GC uses some clock cycles, but it dramatically optimizes my life. The project I'm working on can handle about 2200 entities, each with a drop shadow, plus 3600 tiles before it starts to dip even close to 60 fps, so it doesn't feel like the collector is getting in my way. I test for it sometimes by creating and destroying stuff for a while, and then waiting around to see if it 'hangs' but it doesn't seem to. Does someone know how often it runs by default?
Re: Optimization Stuff
Intermediate tables are basically short-lived and not stored (for very long):Felix_Maxwell wrote: ↑Sat Dec 26, 2020 2:59 pm [What do you mean by intermediate? do you mean something like:
Code: Select all
function getPosition()
return {x,y}
end
Re: Optimization Stuff
"Garbage" is all memory allocations to which no strong reference exist anymore. Niling an object removes one reference, so it will still become garbage. I don't think there is any difference GC-wise between explicitely niling a local object vs. just letting scope take care of it. Although tighter scopes are mentioned in the LuaJIT docs as a way to help the compiler generate better code.Felix_Maxwell wrote: ↑Sat Dec 26, 2020 2:59 pm I assume you cut down garbage by nil-ing stuff out when you're done with it?
Unless your code is real bad, GC cycles will mostly show up as short spikes, not as a constant performance degradation. Like, one frame every x seconds takes 20 ms instead of 10. You don't always notice those in the fps counter, but a drawing frame time history diagram can be helpful to see them.before it starts to dip even close to 60 fps, so it doesn't feel like the collector is getting in my way.
Re: Optimization Stuff
I had written this before grump's reply, so I'll mark the sections that are already replied in grey.
Something that stresses the GC is if something like this is called many times every frame:
If you can run it every frame without a performance drop, that's probably best.
That produces garbage. When you set a variable to nil, the object becomes unreferenced, and the garbage collector needs to identify it as unreferenced in order to clean it up. A priori, Lua does not know whether there's some other reference to the object anywhere, that's why the garbage collector exists in the first place.Felix_Maxwell wrote: ↑Sat Dec 26, 2020 2:59 pmI assume you cut down garbage by nil-ing stuff out when you're done with it?
In your example, for as long as arg is not set to a different value, the table will be kept in memory, and the GC will not have any need to clean it up.Felix_Maxwell wrote: ↑Sat Dec 26, 2020 2:59 pm What do you mean by intermediate? do you mean something like:
[...]
Something that stresses the GC is if something like this is called many times every frame:
Code: Select all
myfunc({field1 = 3, field2 = 5})
Sure thing, but note that "some clock cycles" can add up to more than one frame time, causing micro-stutters. If you don't run it manually every frame, it will typically run every few frames. I don't know the rules that decide when it should run; I know the rules are not fully deterministic (it may depend on things like the amount of objects that could be created in a certain time, which isn't always the same due to caches and other stuff).Felix_Maxwell wrote: ↑Sat Dec 26, 2020 2:59 pmI know the GC uses some clock cycles, but it dramatically optimizes my life.
If you can run it every frame without a performance drop, that's probably best.
Re: Optimization Stuff
If you can't do it, that's fine; but just because you lack the understanding of programming to achieve something doesn't make it "impossible".
In fact, this is how games have been programmed since basically forever. It's only in our post-modern era when computers have become so fast that developers now have the luxury of being, ahem, lazy.
But it's a trap. If you don't learn to deal with it, you'll hit a performance ceiling sooner or later. The complexity of your games can't grow beyond that barrier, unless you actually start thinking about what the computer is doing with all those anonymous tables you're creating every frame.
I mean, if you're just making Tetris clones or whatever then by all means, ignore everything I've said.
Who is online
Users browsing this forum: No registered users and 4 guests