From 99cb385f3443b9b199f6031caf6f33115e2b8f4a Mon Sep 17 00:00:00 2001 From: Florian Wilkemeyer Date: Wed, 4 May 2016 13:56:20 +0200 Subject: [PATCH 1/2] map-server: optimized path_search(), by removing dynamic memory allocation for each call. Pathfinding uses BHEAP_* (vector) operations from db.h, before this optimization each pathsearch resulted in an call to aMalloc() / aFree() which is quite costly as it fragments memory over time & may cause software stalls when the application's heap must be resized. Now it allocates the Heap once (& may grow it accordingly) and re-uses the allocated memory every time/call. This makes also tons of monsters less cpu-time intensive. --- src/common/db.h | 14 ++++++++++++++ src/map/map.c | 2 ++ src/map/path.c | 45 ++++++++++++++++++++++++++++----------------- src/map/path.h | 5 +++++ 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/common/db.h b/src/common/db.h index 6a985dedbc..143e40f922 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -1338,6 +1338,15 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...); }while(0) +/// Resets the Legnth / 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) @@ -1622,6 +1631,11 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...); #define BHEAP_CLEAR(__heap) VECTOR_CLEAR(__heap) +/// Resets the binary heap, so it can be used as an empty heap +/// +/// @parm __heap Binary heap +#define BHEAP_RESET(__heap) VECTOR_RESET(__heap) + /// Generic comparator for a min-heap. (minimum value at top) /// Returns -1 if v1 is smaller, 1 if v2 is smaller, 0 if equal. diff --git a/src/map/map.c b/src/map/map.c index 13ebb5e7d2..f725629990 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -4411,6 +4411,7 @@ void do_final(void) do_final_channel(); //should be called after final guild do_final_vending(); do_final_buyingstore(); + do_final_path(); 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); map_do_init_msg(); + do_init_path(); do_init_atcommand(); do_init_battle(); do_init_instance(); diff --git a/src/map/path.c b/src/map/path.c index d662ae7f86..96436cca07 100644 --- a/src/map/path.c +++ b/src/map/path.c @@ -39,6 +39,9 @@ struct path_node { /// Binary heap of path nodes 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) #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}, }; + +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). * 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: &2 = call path_search_long instead * 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) { @@ -313,8 +328,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x // A* (A-star) pathfinding // 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. - - BHEAP_STRUCT_VAR(node_heap, open_set); // 'Open' set + BHEAP_RESET(g_open_set); // 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). @@ -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].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(;;) { 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; - if (BHEAP_LENGTH(open_set) == 0) { - BHEAP_CLEAR(open_set); + if (BHEAP_LENGTH(g_open_set) == 0) { return false; } - current = BHEAP_PEEK(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 + current = BHEAP_PEEK(g_open_set); // Look for the lowest f_cost node in the 'open' set + BHEAP_POP2(g_open_set, NODE_MINTOPCMP, swap_ptr); // Remove it from 'open' set x = current->x; 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 if (x == x1 && y == y1) { - BHEAP_CLEAR(open_set); 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)) // Process neighbors of current node 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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)) - 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 if (e) { - BHEAP_CLEAR(open_set); return false; } } diff --git a/src/map/path.h b/src/map/path.h index 14191e16d1..aba1fdf4ee 100644 --- a/src/map/path.h +++ b/src/map/path.h @@ -66,4 +66,9 @@ unsigned int distance(int dx, int dy); bool check_distance_client(int dx, int dy, int distance); int distance_client(int dx, int dy); +// +void do_init_path(); +void do_final_path(); + + #endif /* _PATH_H_ */ From ce97b9ae0f8a08edd63d1547187eaaefa00daec0 Mon Sep 17 00:00:00 2001 From: Florian Wilkemeyer Date: Wed, 4 May 2016 14:46:29 +0200 Subject: [PATCH 2/2] corrected some typos in descriptive comments --- src/common/db.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/db.h b/src/common/db.h index 143e40f922..3dcc473349 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -1338,7 +1338,7 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...); }while(0) -/// Resets the Legnth / content, so the vector is empty +/// Resets the length and clears content, so the vector is empty /// /// @param __vec Vector #define VECTOR_RESET(__vec) \ @@ -1631,7 +1631,7 @@ void linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...); #define BHEAP_CLEAR(__heap) VECTOR_CLEAR(__heap) -/// Resets the binary heap, so it can be used as an empty 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)