Kamaze wrote:
Dosn't the compiler optimize such things? (Yes, this is anyway bad code...)
not everyone has a good compiler. but, let's say we only have to deal with gcc, which should do that, function inlining, and a whole lot of other fun stuff: it's still no excuse for bad code, since it is only a program -- it will do things for you if you know what you're doing, but you can't rely to be able to read your mind, or be able to fix bad but nonetheless valid code (that's why humans are still in the mix).
Watermelon wrote:
yes almost all functions in the source files I touched use this kind of variable declaration style,probably it will hog some cpu time if the compiler cannot optimize them efficiently(tons of mov to register),esp the ones get executed every frame/tick.
it makes me cry too.
Watermelon wrote:
Also I think 1000 ticks per a second is probably too much,think modern games run at 100? ticks/server frame per second at max...
i agree with you there: hopping it down to 100 would free up a whole lot more cpu cycles (an order of magnitude, to be exact), though i really hate to use this to support one of my other points: we can't get away with reducing the tick count to 100 if the collision detection system uses the "have you hit me now?" tick-by-tick approach -- that's probably the only reason there are 1000 ticks per second. if you make the time interval 10 times smaller, then projectiles have to move 10 times further each tick, and at that, with projectiles at their current speed, you'd find that heat shells may well have a 50/50 chance of appearing on the other side of an enemy tank the next tick, bypassing it entirely. the
only way to reclaim these ticks is by completely removing
any and all forms of collision detection that rely on tick-by-tick checking to see if one moving object currently exists in the same location as another -- there is literally no other way to do it.
if we replace the current collision detection checks with smart trigonometric checks, such as raycasting (as devurandom mentioned in another post)
and an event-queue system for almost all in-game actions, you could actually reduce the tick frequency to 1 tick per second, and everything would occur
exactly the same, in regards to collision detection as if you were running at 10 million ticks per second (a collision detection system which never makes mistakes, such as space-warping past an object it should've hit, is pretty nice).
and with any "look ahead" collision detection system (such as ray-casting), in almost all cases, doing collision detection every tick (or every other tick, or at any repeating interval) is a complete waste of time, and really shouldn't be done, since look ahead systems can "look ahead" for an arbitrary number of ticks, and can be used to figure out the first
possible moment that any given object could collide with anything else -- only at that "first possible moment" would you check the future collision possibilities for that moving object again.
Watermelon wrote:
delaying variable declaration/reusing variable are always 'good' optimization techniques imo.
DevUrandom wrote:
Yes, and if you reuse your little "int i" often enough, you can be sure that no one understands your code anymore...
i just thought of something, but i don't think i've ever seen it used before (either because no one's done it, or it's a really really bad idea and no one wants to do it):
take advantage of the c pre-processor... that way both of you two are right and a compromise is made (only one
actual variable is used, but it is given distinct names for each use):
as an example, take the following code example (has no relation to anything):
Code: Select all
void dosomething( void ) {
int num_objects;
num_objects = count_objects();
/* code here to use num_objects */
/* as of now, we will have no further use for num_objects
we could collect it from memory (i forget how to do this
in c, but i think it's del in c++) or just let it sit until it is
dereferenced when the function is returned... for emphasis
on clean, efficient code, i'll del it now */
del num_objects;
/* deleting num_objects requires a few processor instructions,
so while removing it frees up some memory, you don't get
to magically del things for free. note if it wasn't done now,
the same thing would've been done when the function
returns */
int last_array_index = -1;
int arr[1000];
while ( last_array_index < 23 ) {
arr[ ++last_array_index ] = last_array_index * 2;
}
for ( int i = last_array_index; i >= 0; i-- )
printf ( "position: %d, value: %d", i, arr[ i ] );
}
in the above traditional approach, all variables are clearly distinct, but you have 2 int variables that are used at seperate times (no overlap of use), so the variable
num_objects could be used again instead of creating a new distinct variable, but this, as dev pointed out, would mislead the reader, and reclaiming resources from
num_objects takes processor time no matter what (either if done explicitly when it is no longer needed, which is preferred, or automatically when the function returns). from the standpoint of code readability, there must be two seperate variables, but from the standpoint of code performance, there must be only one, which brings us to the next code piece:
Code: Select all
void dosomething( void ) {
int num_objects;
num_objects = count_objects();
/* code here to use num_objects */
/* as of now, we will have no further use for "num_objects" */
#define last_array_index num_objects
/* we're making all instances of the "last_array_index" actually
point to the "num_objects" variable, so that the now unused
variable can be usefully reused under a different name */
last_array_index = -1;
int arr[1000];
while ( last_array_index < 23 ) {
arr[ ++last_array_index ] = last_array_index * 2;
}
#undef last_array_index
/* virtual garbage collection: we're no longer using this alias to the
num_objects variable, and we don't want it to conflict with other
uses of it throughout the source code: this is absolutely mandatory
and at least at the end of every function, there should be one of
these undef statements for each one created. */
for ( int i = last_array_index; i >= 0; i-- )
printf ( "position: %d, value: %d", i, arr[ i ] );
}
as you can see above, as far as the code is concerned after the preprocessor runs through, all instances of last_array_index will be turned into num_object, and will use the exact same variable in memory, but is distinct and meaningful in the code, and except for the
#define statement (the virtual variable declaration), it looks exactly like normal c code. the only downside, which is a big downside, is that you need to #undef each "variable alias" at the bottom of the code block in which it is used (usually a function), or you'll get a lot of "undeclared variable num_objects" style errors, but, if you can remember your undefs (just create an #undef at the bottom of the function
as soon as you finish typing the corresponding
#define, then this shouldn't be a problem, and you'll have very lean code that is no less readable than it was before.
so given the above, you can just reuse no-longer-used variables until you run out of old ones of that type, in which case you create a new one, and then link aliases to that when it is no longer used.
as an example of the differences between this approach and a modern approach:
if your code is organized so that any given variable is not used anywhere it's not needed, and is fairly isolated, you might be able to have a function using the same 3 ints throughout, when before it would've needed 12 or 14 to keep it readable, with all the extra memory use and dereferencing that goes with managing those.
also, on a side note, you really should only be using single-letter variable names, such as i or j for loop iterations, so reusing "i" in anything but the context of a loop iteration is about as confusing as you can get, but when you reuse it an unrelated loop, it's pretty well understood that there's no connection between this loop and the last one (assuming you reset i to 0, or whatever you need)