Merge pull request #1241 from wilkemeyer/optimize/pathsearch-memallocations

Optimized path_search() by reducing amount of allocations per call.
Thanks to @wilkemeyer!
This commit is contained in:
Aleos 2016-05-07 13:00:36 -04:00
commit 0da7136ae7
4 changed files with 49 additions and 17 deletions

View File

@ -1338,6 +1338,15 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...);
}while(0) }while(0)
/// Resets the length and clears content, so the vector is empty
///
/// @param __vec Vector
#define VECTOR_RESET(__vec) \
if( VECTOR_LENGTH(__vec) > 0 ) { \
memset(VECTOR_DATA(__vec), 0, (VECTOR_LENGTH(__vec)*sizeof(VECTOR_FIRST(__vec)))); /* clear data */ \
} \
VECTOR_LENGTH(__vec) = 0; /* clear current length */
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Binary heap library based on defines. (uses the vector defines above) // Binary heap library based on defines. (uses the vector defines above)
@ -1622,6 +1631,11 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...);
#define BHEAP_CLEAR(__heap) VECTOR_CLEAR(__heap) #define BHEAP_CLEAR(__heap) VECTOR_CLEAR(__heap)
/// Resets the binary heap and clears content so it can be treated as empty
///
/// @parm __heap Binary heap
#define BHEAP_RESET(__heap) VECTOR_RESET(__heap)
/// Generic comparator for a min-heap. (minimum value at top) /// Generic comparator for a min-heap. (minimum value at top)
/// Returns -1 if v1 is smaller, 1 if v2 is smaller, 0 if equal. /// Returns -1 if v1 is smaller, 1 if v2 is smaller, 0 if equal.

View File

@ -4411,6 +4411,7 @@ void do_final(void)
do_final_channel(); //should be called after final guild do_final_channel(); //should be called after final guild
do_final_vending(); do_final_vending();
do_final_buyingstore(); do_final_buyingstore();
do_final_path();
map_db->destroy(map_db, map_db_final); map_db->destroy(map_db, map_db_final);
@ -4716,6 +4717,7 @@ int do_init(int argc, char *argv[])
add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000); add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000);
map_do_init_msg(); map_do_init_msg();
do_init_path();
do_init_atcommand(); do_init_atcommand();
do_init_battle(); do_init_battle();
do_init_instance(); do_init_instance();

View File

@ -39,6 +39,9 @@ struct path_node {
/// Binary heap of path nodes /// Binary heap of path nodes
BHEAP_STRUCT_DECL(node_heap, struct path_node*); BHEAP_STRUCT_DECL(node_heap, struct path_node*);
static BHEAP_STRUCT_VAR(node_heap, g_open_set); // use static heap for all path calculations
// it get's initialized in do_init_path, freed in do_final_path.
/// Comparator for binary heap of path nodes (minimum cost at top) /// Comparator for binary heap of path nodes (minimum cost at top)
#define NODE_MINTOPCMP(i,j) ((i)->f_cost - (j)->f_cost) #define NODE_MINTOPCMP(i,j) ((i)->f_cost - (j)->f_cost)
@ -58,6 +61,16 @@ static const unsigned char walk_choices [3][3] =
{DIR_SOUTHWEST,DIR_SOUTH,DIR_SOUTHEAST}, {DIR_SOUTHWEST,DIR_SOUTH,DIR_SOUTHEAST},
}; };
void do_init_path(){
BHEAP_INIT(g_open_set); // [fwi]: BHEAP_STRUCT_VAR already initialized the heap, this is rudendant & just for code-conformance/readability
}//
void do_final_path(){
BHEAP_CLEAR(g_open_set);
}//
/*========================================== /*==========================================
* Find the closest reachable cell, 'count' cells away from (x0,y0) in direction (dx,dy). * Find the closest reachable cell, 'count' cells away from (x0,y0) in direction (dx,dy).
* Income after the coordinates of the blow * Income after the coordinates of the blow
@ -249,6 +262,8 @@ static int add_path(struct node_heap *heap, struct path_node *tp, int16 x, int16
* flag: &1 = easy path search only * flag: &1 = easy path search only
* flag: &2 = call path_search_long instead * flag: &2 = call path_search_long instead
* cell: type of obstruction to check for * cell: type of obstruction to check for
*
* Note: uses global g_open_set, therefore this method can't be called in parallel or recursivly.
*------------------------------------------*/ *------------------------------------------*/
bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int flag, cell_chk cell) bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int flag, cell_chk cell)
{ {
@ -313,8 +328,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
// A* (A-star) pathfinding // A* (A-star) pathfinding
// We always use A* for finding walkpaths because it is what game client uses. // We always use A* for finding walkpaths because it is what game client uses.
// Easy pathfinding cuts corners of non-walkable cells, but client always walks around it. // Easy pathfinding cuts corners of non-walkable cells, but client always walks around it.
BHEAP_RESET(g_open_set);
BHEAP_STRUCT_VAR(node_heap, open_set); // 'Open' set
// FIXME: This array is too small to ensure all paths shorter than MAX_WALKPATH // FIXME: This array is too small to ensure all paths shorter than MAX_WALKPATH
// can be found without node collision: calc_index(node1) = calc_index(node2). // can be found without node collision: calc_index(node1) = calc_index(node2).
@ -337,7 +351,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
tp[i].f_cost = heuristic(x0, y0, x1, y1); tp[i].f_cost = heuristic(x0, y0, x1, y1);
tp[i].flag = SET_OPEN; tp[i].flag = SET_OPEN;
heap_push_node(&open_set, &tp[i]); // Put start node to 'open' set heap_push_node(&g_open_set, &tp[i]); // Put start node to 'open' set
for(;;) { for(;;) {
int e = 0; // error flag int e = 0; // error flag
@ -352,13 +366,12 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
int g_cost; int g_cost;
if (BHEAP_LENGTH(open_set) == 0) { if (BHEAP_LENGTH(g_open_set) == 0) {
BHEAP_CLEAR(open_set);
return false; return false;
} }
current = BHEAP_PEEK(open_set); // Look for the lowest f_cost node in the 'open' set current = BHEAP_PEEK(g_open_set); // Look for the lowest f_cost node in the 'open' set
BHEAP_POP2(open_set, NODE_MINTOPCMP, swap_ptr); // Remove it from 'open' set BHEAP_POP2(g_open_set, NODE_MINTOPCMP, swap_ptr); // Remove it from 'open' set
x = current->x; x = current->x;
y = current->y; y = current->y;
@ -367,7 +380,6 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
current->flag = SET_CLOSED; // Add current node to 'closed' set current->flag = SET_CLOSED; // Add current node to 'closed' set
if (x == x1 && y == y1) { if (x == x1 && y == y1) {
BHEAP_CLEAR(open_set);
break; break;
} }
@ -379,24 +391,23 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
#define chk_dir(d) ((allowed_dirs & (d)) == (d)) #define chk_dir(d) ((allowed_dirs & (d)) == (d))
// Process neighbors of current node // Process neighbors of current node
if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_EAST) && !map_getcellp(md, x+1, y-1, cell)) if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_EAST) && !map_getcellp(md, x+1, y-1, cell))
e += add_path(&open_set, tp, x+1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y-1, x1, y1)); // (x+1, y-1) 5 e += add_path(&g_open_set, tp, x+1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y-1, x1, y1)); // (x+1, y-1) 5
if (chk_dir(PATH_DIR_EAST)) if (chk_dir(PATH_DIR_EAST))
e += add_path(&open_set, tp, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, x1, y1)); // (x+1, y) 6 e += add_path(&g_open_set, tp, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, x1, y1)); // (x+1, y) 6
if (chk_dir(PATH_DIR_NORTH|PATH_DIR_EAST) && !map_getcellp(md, x+1, y+1, cell)) if (chk_dir(PATH_DIR_NORTH|PATH_DIR_EAST) && !map_getcellp(md, x+1, y+1, cell))
e += add_path(&open_set, tp, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, x1, y1)); // (x+1, y+1) 7 e += add_path(&g_open_set, tp, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, x1, y1)); // (x+1, y+1) 7
if (chk_dir(PATH_DIR_NORTH)) if (chk_dir(PATH_DIR_NORTH))
e += add_path(&open_set, tp, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, x1, y1)); // (x, y+1) 0 e += add_path(&g_open_set, tp, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, x1, y1)); // (x, y+1) 0
if (chk_dir(PATH_DIR_NORTH|PATH_DIR_WEST) && !map_getcellp(md, x-1, y+1, cell)) if (chk_dir(PATH_DIR_NORTH|PATH_DIR_WEST) && !map_getcellp(md, x-1, y+1, cell))
e += add_path(&open_set, tp, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, x1, y1)); // (x-1, y+1) 1 e += add_path(&g_open_set, tp, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, x1, y1)); // (x-1, y+1) 1
if (chk_dir(PATH_DIR_WEST)) if (chk_dir(PATH_DIR_WEST))
e += add_path(&open_set, tp, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, x1, y1)); // (x-1, y) 2 e += add_path(&g_open_set, tp, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, x1, y1)); // (x-1, y) 2
if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_WEST) && !map_getcellp(md, x-1, y-1, cell)) if (chk_dir(PATH_DIR_SOUTH|PATH_DIR_WEST) && !map_getcellp(md, x-1, y-1, cell))
e += add_path(&open_set, tp, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, x1, y1)); // (x-1, y-1) 3 e += add_path(&g_open_set, tp, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, x1, y1)); // (x-1, y-1) 3
if (chk_dir(PATH_DIR_SOUTH)) if (chk_dir(PATH_DIR_SOUTH))
e += add_path(&open_set, tp, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, x1, y1)); // (x, y-1) 4 e += add_path(&g_open_set, tp, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, x1, y1)); // (x, y-1) 4
#undef chk_dir #undef chk_dir
if (e) { if (e) {
BHEAP_CLEAR(open_set);
return false; return false;
} }
} }

View File

@ -66,4 +66,9 @@ unsigned int distance(int dx, int dy);
bool check_distance_client(int dx, int dy, int distance); bool check_distance_client(int dx, int dy, int distance);
int distance_client(int dx, int dy); int distance_client(int dx, int dy);
//
void do_init_path();
void do_final_path();
#endif /* _PATH_H_ */ #endif /* _PATH_H_ */