Reducing Function Times

Posted By: Dooley

Reducing Function Times - 08/12/20 01:23

I just became aware that the biggest factor slowing my game is the function speed.
I assume reducing the number of lines in any given function should help to minimize this.

Do you have any general tips on reducing function complexity?
Posted By: Dooley

Re: Reducing Function Times - 08/12/20 01:27

As an example, if I am running my game, it will get around 30 fps. If I pause the game (freeze_mode) it jumps up to 80 fps. Obviously there should be some difference, but it would be great to improve the efficiency of what I'm doing. According to the debug menu, functions are taking around 25 ms/frame. When paused it drops down to 6 or so.
Posted By: 3run

Re: Reducing Function Times - 08/12/20 06:22

Hey!

I guess there are lots of things which can affect function times slower. But for me the biggest slowdown was using while loops for each entity (npc, items, objects, etc). How many while loops do you have in your game running at once? As a workaround, I would suggest to stop using while loops for main game logic at all. Instead, I would suggest using 'ENABLE_FRAME' for decor objects and on_frame for main game loop. I got this from one of the Superku's posts around the forum, this will help you with structuring function execution, as well will help to avoid using 'wait' (it sucks and can cause lots of troubles) and also will help to avoid using lots of while loops. If you need more examples, take a look at the source code of my Retro-FPS which is published on forum and can be downloaded from my github.

Greets.
Posted By: Jerome8911

Re: Reducing Function Times - 08/12/20 10:11

Hi Dooley,

Quote
Do you have any general tips on reducing function complexity?

I'm dealing with the same issues, since a RTS gots tons of functions with loops.

For analyzing which part of the functions took most time to calculate I used dtimer() and displayed the time on screen.

Code
void* gameloop()
{
	...

	dtimer();
	check_unit_integrity();
	time_unit_loop = dtimer();
	
	dtimer();
	levelloop();
	time_level_loop = dtimer();	

	...
}

function show_info_txt()
{
	STRING* s1 = " ";

	...

	str_for_num(s1, time_unit_loop);
	draw_text("Rechenzeit Unitloop", 10, 275, WHITE);
	draw_text(s1, 140, 275, WHITE);

	str_for_num(s1, time_units);
	draw_text("Rechenzeit Einheiten", 10, 315, WHITE);
	draw_text(s1, 140, 315, WHITE);

	...
}


Searching structures took most of the execution time, so I would start from there. At start I even used ent_next to search for specific entities, obviously that takes a lot of time. Saving specific objects (like the units) in its own search lists saved a lot of time for me:
Code
//search list for units
int no_of_units;
int max_units = 256;
UNIT_STRUCT unit[256];

//search list for buildings
int no_of_buildings = 0;
int max_buildings = 64;
ENTITY* building_array[64];

...

Another thing you might consider is to "update" some functions less frequently (not every frame, but every other frame or even less frequent).

Best regards
Posted By: Dooley

Re: Reducing Function Times - 08/12/20 14:47

These are great suggestions, thank you!

Wow, I was not even aware of ENABLE_FRAME so I will have to look into that. Lots of while loops in my code!
Posted By: Dooley

Re: Reducing Function Times - 08/12/20 14:49

I also use ent_next, but I only use it prior to the entity's main loop. So will use it once at the beginning of a particular level, and it will assign the found entity to a ENTITY* pointer, for use later in the level...

dtimer() seems like it might be very useful in helping me find the bottlenecks, so I will check that out.
Posted By: 3run

Re: Reducing Function Times - 08/12/20 16:51

As for the ent_next, I would try to avoid cycling through all entities, I think this might be slow too (on big levels). Let's say you have 1000 entities on the level (sprites, models, etc) and you only need 100 of them, so instead of cycling through all 1000 and finding those that 100 you need, you can right at the start of the level, add those 100 into ENTITY array list (don't forget to have integer counter, to register total amount of entities) and then cycle within that list.
Code
ENTITY *list[999];

int counter = 0;

action add_to_list()
{
	list[counter] = my;
	counter++;
}

void cycle_in_list()
{
	int i = 0;
	for(i = 0; i < counter - 1; i++)
	{
		if(list[i] == NULL)
		{
			continue;
		}
		
		// do something with the entity
		
	}
}
Posted By: Dooley

Re: Reducing Function Times - 08/12/20 17:50

I've always wondered about using for loops vs while loops. I generally use while loops as they just make more sense to me. But maybe a for loop could be more efficient when it does not have to wait for each frame?
Posted By: 3run

Re: Reducing Function Times - 08/12/20 17:57

Well, as far as I know if you cycle fixed amount of times in while loop, it doesn't need to have wait either. I mostly use for loops since they are mainly made and used for that kind of purpose.
Posted By: Dooley

Re: Reducing Function Times - 08/12/20 20:19

I just switched one of the major loops from a while (without wait) to a for loop. It did not make any noticeable impact on frame rate.
Posted By: 3run

Re: Reducing Function Times - 08/13/20 06:23

Switching from while to for loop won't indeed have any impact on frame rate. You need to get rid of while loops with wait per each entity by switching to on_frame and EVENT_FRAME and also you need to avoid cycling through all entities on the level. Add entities into separate lists and cycle through via for loop (or while if you like it, but for is more suitable for counter/fixed amount of cycling loops).
Posted By: txesmi

Re: Reducing Function Times - 08/13/20 11:13

Originally Posted by 3run
Switching from while to for loop won't indeed have any impact on frame rate.

As far as I have read, compilers do normally compile both instructions to the very same thing.

Originally Posted by Dooley
Do you have any general tips on reducing function complexity?

One thing you can try is to replace local variables of actions by entity skills. Acknex do restore and save actions stack memory each frame. It has a significant impact on execution when there are a lot. Entity skills are slower to read and write, so it is not a fire-written law but something to take into account in reference to variables usage. I did a messurement of the impact of 'wait' and stack size in my oldy computer some time ago: https://opserver.de/ubb7/ubbthreads.php?ubb=showflat&Number=474652#Post474652

Recursively accesing array members by its index is a little waste of resurces that can be avoided by getting the proper pointer. I mean:
Code
var array[100];

// acces array members by its index
int i = 0;
for(; i<100; ++i)
   array[i] = random(1); // it performs an addition each loop in order to access the array member: *(array + i) = ... 

// faster approach
var *ptr = array;
var *ptrLast = ptr + 100;
for(; ptr<ptrLast; ++ptr)
   *ptr = random(1); // No extra additions. The more accesses to array members, the more additions it avoids.


The use of var type as indexes on arrays also have a little penalty since the program have to cast them to long type. It is just a 10 bits right shift, but you know, dunes grow grain by grain.

I will always recomend the use of function pointers on complex state machines. Function pointers let you atomize the execution and avoid the whole switch/case block commonly used to build state machines. I do normally set the function scheduler and state machines into the same system. Something like the following:
Code
#include <acknex.h>
#include <default.c>

//-------------------------------------------------------------------------------------------

#define ITEMS_MAX              1000

typedef struct SCHEDULER_ITEM
{
	void *object;
	void *state(void *item);
	void remover(void *item);
} SCHEDULER_ITEM;

SCHEDULER_ITEM items[ITEMS_MAX];
int itemCount = 0;

SCHEDULER_ITEM *itemAdd(void* object, void *remover, void *state)
{
	SCHEDULER_ITEM *item = items + itemCount;
	itemCount += 1;
	
	item->object = object;
	item->state = state;
	item->remover = remover;
	
	return item;
}

void itemRemoveAll()
{
	SCHEDULER_ITEM *item = items;
	SCHEDULER_ITEM *itemLast = item + itemCount;
	for(; item < itemLast; ++item)
		item->remover(item->object);
	itemCount = 0;
}

//-------------------------------------------------------------------------------------------

// Prototypes
void *stMoveUp(ENTITY *ent);

// States
void *stWaitDown(ENTITY *ent)
{
	ent->skill2 += time_step;
	if(ent->skill2 > ent->skill1)
	{
		ent->skill4 += 1;
		if(ent->skill4 > ent->skill3)
		{
			return NULL;
		}
		
		ent->skill1 = random(200);
		ent->skill2 = 0;
		
		return stMoveUp;
	}
	
	return stWaitDown;
}

void *stMoveDown(ENTITY *ent)
{
	ent->z -= time_step * 10;
	
	if(ent->z < ent->skill1)
	{
		ent->skill1 = 4 + random(16);
		ent->skill2 = 0;
		return stWaitDown;
	}
	
	return stMoveDown;
}

void *stWaitOnTop(ENTITY *ent)
{
	ent->skill2 += time_step;
	
	if(ent->skill2 > ent->skill1)
	{
		ent->skill1 = -random(200);
		ent->skill2 = 0;
		
		return stMoveDown;
	}
	
	return stWaitOnTop;
}

void *stMoveUp(ENTITY *ent)
{
	ent->z += time_step * 10;
	
	if(ent->z > ent->skill1)
	{
		ent->skill1 = 4 + random(16);
		ent->skill2 = 0;
		return stWaitOnTop;
	}
	
	return stMoveUp;
}

void sphereCreate()
{
	if(itemCount > ITEMS_MAX - 1)
		return;
	ENTITY *ent = ent_create(SPHERE_MDL, vector(0, random(600) - 300, -random(200)), NULL);
	ent->skill1 = random(200); // limit
	ent->skill2 = 0; // limit counter
	ent->skill3 = 1 + random(4); // Count of loops
	ent->skill4 = 0; // loop counter
	
	itemAdd(ent, ent_remove, stMoveUp);
}

//-------------------------------------------------------------------------------------------

void scheduler()
{
	int index = 0;
	SCHEDULER_ITEM *item = items;
	for(; index < itemCount; ++index, ++item)
	{
		item->state = item->state(item->object);
		if(item->state != NULL)
			continue;
		item->remover(item->object);
		itemCount -= 1;
		memcpy(item, items + itemCount, sizeof(SCHEDULER_ITEM)); // The fastest deletion but sorting gets broken.
		index -= 1;
		item -= 1;
	}
}

//-------------------------------------------------------------------------------------------

void createMultipleEntities()
{
	int i = 0;
	for(; i<ITEMS_MAX; i+=1)
		sphereCreate();
}

void main()
{
	max_entities = maxv(ITEMS_MAX, 5000);
	wait(3);
	level_load("");
	camera->x = -600;
	
	createMultipleEntities();
	
	on_esc = NULL;
	on_space = sphereCreate;
	
	while(!key_esc)
	{
		scheduler();
		wait(1);
	}
	
	itemRemoveAll();
	sys_exit(NULL);
}


Hope it helps...
Salud!
Posted By: Dooley

Re: Reducing Function Times - 08/13/20 12:50

@3run Thanks I am planning to try EVENT_FRAME for some of my game objects to see how it works.

@txesmi Your stuff is pretty advanced for me. I don't know if I have anything where those solutions will actually work, but I will look more carefully. I appreciate the input!
Posted By: Dooley

Re: Reducing Function Times - 08/13/20 16:25

@3run Wow! EVENT_FRAME made a huge impact. Almost 10 fps in some areas. I will be applying this to more game objects asap!
Posted By: txesmi

Re: Reducing Function Times - 08/13/20 17:00

@Dooley
I am sorry, I suspected such a circumstance but thrown the code same xP

Let me explain. The point is that if you want to get rid of engines entity action scheduler, as 3Run suggested, you have to build your own which involves some sort of object and function management. That is basically what my example do. It happens that once you have a function scheduler working, with little changes you get a state machines manager by the same prize. I have cleaned, commented and formatted the code above so now it works as a little transparent module to include in any project:

scheduler.h

Code
#ifndef _SCHEDULER_H_
#define _SCHEDULER_H_

//-------------------------------------------------------------------------------------------

#define SCHEDULER_MAX_ITEMS    1000 // Maximum amount of simultaneous objects into the scheduler

//-------------------------------------------------------------------------------------------

typedef struct SCHEDULER_ITEM
{
	void *object;               // A generic pointer to an object of an undetermined type
	void remover(void *item);   // A function pointer that removes the pointed object
	void *state(void *item);    // A function pointer to the current state function
} SCHEDULER_ITEM;

// Static scheduler items
SCHEDULER_ITEM schItems[SCHEDULER_MAX_ITEMS];
int schItemCount = 0;

//-------------------------------------------------------------------------------------------

/* Add an object to the scheduler
 *  object:  a pointer to an object of any kind. It can be NULL.
 *  remover: function that removes the object. It can be NULL, but the object will not be removed.
 *  state:   function to be called each frame with the object as parameter. Mandatory!
 *           void *myAction(TypeOfTheObject *object) 
 *           {
 *	             ...
 *	             return myAction; // Return NULL in order to remove the object and the scheduler item
 *           }
 */
void schItemAdd(void *object, void *remover, void *state)
{
	if(schItemCount == SCHEDULER_MAX_ITEMS) // No space enough?
		return;
	
	SCHEDULER_ITEM *item = schItems + schItemCount; // Get a pointer to the next last scheduler item
	schItemCount += 1; // Increase the item counter;
	
	item->object = object; // Init the scheduler item 
	item->state = state;
	item->remover = remover;
}

//-------------------------------------------------------------------------------------------

/* Remove all the objects and items in the scheduler
 *  
 */
void schItemRemoveAll()
{
	SCHEDULER_ITEM *item = schItems;
	SCHEDULER_ITEM *itemLast = item + schItemCount;
	for(; item < itemLast; ++item)
		if(item->remover != NULL)
			item->remover(item->object);
	schItemCount = 0;
}

//-------------------------------------------------------------------------------------------

/* Scheduler
 *  
 */
void scheduler()
{
	int index = 0;
	SCHEDULER_ITEM *item = schItems;
	for(; index < schItemCount; ++index, ++item) // Loop through the whole list of scheduled items
	{
		// Call the state function with the object as parameter and save the return on itself, so next time will call the current return
		item->state = item->state(item->object); 
		
		// Continue through the list when the return is not NULL
		if(item->state != NULL)
			continue;
		
		// Otherway, proceed to delete the object
		if(item->remover != NULL) // Call the object remover function stated at itemAdd function call
			item->remover(item->object); 
		
		// Decrease the item counter
		schItemCount -= 1; 
		
		// Copy the last scheduled item to the place where the deleted one is
		memcpy(item, schItems + schItemCount, sizeof(SCHEDULER_ITEM)); 
		// This is the fastest deletion but sorting gets messed. 
		// In case the sorting is important, it will need to copy the whole tail down instead.
		// memcpy(item, item + 1, sizeof(SCHEDULER_ITEM) * (schItemCount - index))
		
		// Decrease the for loop variables, so it computes the same item again, the item overwritten by the memcpy instruction
		index -= 1;
		item -= 1;
	}
}

#endif



You don't really need to understand what is going on, but there you go:

The basis is the SCHEDULER_ITEM data structure which saves each scheduled objects data. The scheduler works over a static list of SCHEDULER_ITEM and loops through it each frame. The list starts empty. The objects are added to the scheduler through 'schItemAdd' which adds a new SCHEDULER_ITEM to the list. The list items are deleted when their state function call returns NULL. The object can also be removed at the same time.

You would only need to include 'scheduler.h', set 'scheduler' function as 'on_frame' event and set 'schItemRemoveAll' as 'on_exit' event. You might, of course, call this functions differently. f.e: as a part of larger looping and close functions.
Code
#include <acknex.h>
#include "scheduler.h"
...
void main()
{
   on_frame = scheduler;
   on_exit = schItemRemoveAll;
}


At this point, you can start adding objects to the scheduler. Lets imagine we want to make an entity on WED be managed by the scheduler. It will need assign an action to it, as it is commonly done, so the engine would call the action on level load.

An action is usually composed by three blocks that can be built as three functions:
- initialization
- loop
- deletion

The initialization remains in the action, but the loop content and the deletion are passed to the scheduler as parameters of 'schItemAdd' in the form of functions. Something like this:
Code
void *crazyLoop(ENTITY *ent)
{
	ent->x = random(100) - 50;
	ent->y = random(100) - 50;
	ent->z = random(100) - 50;
	
	if (key_space)
		return NULL;
	
	return crazyLoop;
}

void crazyRemove(ENTITY *ent)
{
	str_remove((STRING*)ent->skill1);
	ent_remove(ent);
}

action actCrazy()
{ 
	my->skill1 = str_create("my crazy name");
	
	schItemAdd(me, crazyRemove, crazyLoop);
}


This setup will start by the action asociated to an entity in WED which creates a string, saves it into a skill, and includes the entity into the scheduler list with its removing and looping functions.

The removing function removes the string created on initialization and the entity itself.

The looping funtion needs to be of a particular signature:
Code
void *loopingFunction(TypeOfTheObject *object)
{
	...
	return loopingFunction;
}


The return of the function must be a function (void* or NULL) and the parameter should be of the type of the object, an 'ENTITY*' in the example. As far as a function returns its function name, it will act as a loop with a 'wait'. When NULL is returned, the scheduler calls the object deletion function and also deletes the scheduler item from its internal list.

You will need to call 'schItemRemoveAll' before level changes.

That is all the basics. That is how you can delete all the loops from the entity actions. As far as I know there is no way to make it faster.

All this function returning stuff might sound strange but it is mandatory because the scheduler function is running over the static array and it should not be modified by other functions while its execution, which executes every scheduled object function. That is why the looping function must return something in order to make the scheduler know that the list have to be shortened when removing an an object and modify the list localy inside the scheduler. Once you need to take care of the return of the looping functions, it can return pointers to functions by the same prize which acts as object oriented state machine.

The returning function pointer is the function that will be called next frame. This way you can build each state as a separate function and control the execution flux by the return of the functions, as I did in the first example where every state runs for a while until certain condition.

Notice that you can include items in the scheduler with no object or remover function. If there is no object, the scheduler will call the looping function same, but with a null parameter. It might serve to manage global variable changes or whatever. If there is no remover function, the scheduler will not remove the object asociated.

Also notice that the object can be of any kind, entities, panels, bitmaps, custom structs, whatever you need. It is like having engine actions for everything.

Posted By: 3run

Re: Reducing Function Times - 08/13/20 18:18

@txesmi awesome code and explanation, thank you man! :>
Posted By: Dooley

Re: Reducing Function Times - 08/13/20 21:47

I will have to try this. Maybe I will test it on a smaller project until I understand it. It sounds like it has a lot of potential!
Posted By: txesmi

Re: Reducing Function Times - 08/14/20 12:13

Thank you! Hope it is of help laugh

I uploaded the code and explanation to GitHub for convenience. There was a bug in 'schItemRemoveAll'.

--edited--
Forgot to mention that calling 'schItemAdd' inside a scheduled function is totally safe.
Posted By: Jerome8911

Re: Reducing Function Times - 08/14/20 15:16

Thank you txesmi!

I never really recognized any issues with the wait-command and loops, but with this it is way easier to create for example behaviour trees or letting different objects share the same function etc. etc.
Thinking of all the bad workarounds I build for those things, I really regret that I only learned about this now.
Posted By: jumpman

Re: Reducing Function Times - 08/15/20 04:05

Why does while(1) and wait(1); cause so many problems for lots of entities in their own loop?
Posted By: txesmi

Re: Reducing Function Times - 08/15/20 08:58

Originally Posted by jumpman
Why does while(1) and wait(1); cause so many problems for lots of entities in their own loop?

They do not really cause so many. It is just a little bit slower than calling all the functions by yourself. I guess the engine probably checks something or other during the execution of the functions in the scheduler and makes it slower than raw function calls, but that is the thing of been a versatile engine, I think. A single c_move call spends many times the time taken by the scheduler for calling the action. Same happens with ent_animate. Both are pretty intensive functions and it is necessary to reduce their cost as much as possible. Building a function scheduler without taking care of these both functions is futile.
Posted By: 3run

Re: Reducing Function Times - 08/15/20 09:16

I can only quote Superku, as I can't really say better than he already did:
Originally Posted by Superku
Having one game loop is the way to go IMO, so as long as your project isn't that complex already change it.
wait(1) is rather "slow" and you have no real influence on when functions are executed. proc_mode (in particular PROC_GLOBAL) is a game and project killer, leading to seemingly random crashes as it affects all kinds of functions you don't want it to have an impact on. Example:
Code
///////////////////////////////
#include <acknex.h>
#include <default.c>
///////////////////////////////

void projectile()
{
    my.skill1 = 128;
    my.pan = random(20)-10;
    my.tilt = random(20)-10;
    while(my.skill1 > 0)
    {
        c_move(me,vector(16*time_step,0,0),nullvector,0);
        my.skill1 -= time_step;
        
        VECTOR temp;
        vec_set(temp,my.x);
        if(vec_to_screen(temp,camera)) draw_text(str_printf(NULL,"%d",(int)proc_mode),temp.x,temp.y,COLOR_RED);
        
        wait(1);
    }
    ptr_remove(me);
}

void spawnProjectile()
{
    proc_mode = PROC_GLOBAL;
    wait(1); // <- doesn't help as proc_mode is restored after wait
    ent_create(CUBE_MDL,vector(0,random(16)-8,0),projectile);
}

void reload()
{
    level_load(NULL);
}

void main()
{
    fps_max = 60;
    video_mode = 10;
    level_load(NULL);
    on_mouse_left = spawnProjectile; // press left mouse button a few times,
    on_mouse_right = reload; // then the right mouse button to crash the game
}


What I've done for the past few years for new projects was to use on_frame, like this:

Code
///////////////////////////////
#include <acknex.h>
#include <default.c>

// header files
#include "player.h"

// implementation:
#include "player.c"

///////////////////////////////

void mainFrameEvent()
{
	input update function;
	if(gameMode == GAME_MODE_PLAY)
	{
		objectsUpdate();
		enemiesUpdate();
		playerUpdate();
	}
	if(gameMode == other modes) { ... }
}

void main()
{
	fps_max = 60;
	level_load(NULL);
	...
	on_frame = mainFrameEvent;
}


This way you have complete control over what gets executed when and how. You could for example freeze all enemies or projectiles in the game with a simple if(variable) check, while allowing the player to move freely.
You only have to let's say create a list of objects after level_load (on_level_load or what it's called) and free that list on or before level change.

I still use wait(1) entity loops here and there but just for some level decoration/ dynamic objects which don't have an actual influence on gameplay.
And
Originally Posted by Superku
Sorry, but you are wrong. A big project is super tough to manage and debug when using wait. The initial setup is a little easier but that's about it, no other advantages, only disadvantages.
Btw. 10000 waits eat up 2ms of performance already on a 6700k. You'd have to do one hell of an optimization to save 2ms normally, or you could just NOT use wait.

And also a test results performed by txesmi in the past:
Originally Posted by txesmi
The only way of speaking about the offtopic is with numbers.

[Linked Image]

Code
-----------------------------------------
| a wait per entity | own scheduler     |
| 1 byte stack      | 1 byte stack      |
-----------------------------------------
| a wait per entity | own scheduler     |
| 128 bytes stack   | 128 bytes stack   |
-----------------------------------------

Notice that the stack memory size has also its impact in the difference.


Code
#include <acknex.h>
#include <default.c>

#define ENT_COUNT    10000
#define STACK_SIZE   1
#define COMPLEXITY   1

ENTITY *ents[ENT_COUNT];

action actWait() {
	BYTE _n[STACK_SIZE];
	while(1) {
		int _i = 0;
		for(; _i<COMPLEXITY; _i+=1)
			_n[random(STACK_SIZE)] = random(256);
		wait(1);
	}
}

var actList(ENTITY *_ent) {
	BYTE _n[STACK_SIZE];
	int _i = 0;
	for(; _i<COMPLEXITY; _i+=1)
		_n[random(STACK_SIZE)] = random(256);
	return -1;
}

void entLoop () {
	while(!key_esc) {
		wait(1);
		ENTITY **_ent = ents;
		ENTITY **_entLast = _ent + ENT_COUNT;
		for(; _ent<_entLast; _ent++) {
			if(*_ent == NULL)
				continue;
			var _result = actList(*_ent);
			if(_result == 0)
				continue;
			switch(_result) {
				case 1:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 2:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 3:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 4:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 5:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 6:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 7:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 8:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 9:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 10:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 11:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 12:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 13:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 14:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 15:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 16:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 17:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 18:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				case 19:
					ent_remove(*_ent);
					*_ent = NULL;
					break;
				default:
					break;
			}
		}
	}
}

void main () {
	max_entities = ENT_COUNT;
	
	void _act ();
	_act = actWait;
//	_act = actList;
	
	level_load("");
	def_debug();
	int _i = 0;
	for(; _i<ENT_COUNT; _i+=1)
		ents[_i] = ent_create(SPHERE_MDL, vector(0, 0, -1000), _act);
	if(_act == actList)
		entLoop();
}


I gived a bit of complexity to the scheduler loop so it can be considered a complete flux manager. The numbers speak by themself. It is clear it gains performance with a single while loop but it is not that much. Take into account that we are speaking about a difference of 3/10000 ms/ent: the time taken by few operations. Bad programming practices will waste more time.

Salud!

Greets!
Posted By: Aku_Aku

Re: Reducing Function Times - 08/15/20 09:54

Nice collection of thoughts 3run!
© 2024 lite-C Forums