Selaa lähdekoodia

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.
Florian Wilkemeyer 9 vuotta sitten
vanhempi
commit
99cb385f34
4 muutettua tiedostoa jossa 49 lisäystä ja 17 poistoa
  1. 14 0
      src/common/db.h
  2. 2 0
      src/map/map.c
  3. 28 17
      src/map/path.c
  4. 5 0
      src/map/path.h

+ 14 - 0
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.

+ 2 - 0
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();

+ 28 - 17
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;
 			}
 		}

+ 5 - 0
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_ */