gnugo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnugo-devel] persistent semeai caching


From: Arend Bayer
Subject: [gnugo-devel] persistent semeai caching
Date: Thu, 19 Aug 2004 20:42:37 +0200 (CEST)


- persistent semeai caching

As explained when I posted my cache to reorganize the persistent caching,
there is now rather little to do to implement this. The most crucial part of
computing the "active area" (the area that has to match to declare the
result valid when trying to reuse it). I solved this by reusing the function
to do this in the owl case, and applying it once for each goal dragon in
the semeai.
Other than that, the only noteworthy thing might be that I also added a hash
of the goal areas. This e.g. makes sure we don't reuse a result that
was obtained without "recompute_dragons" when we are supposed to recompute
it.

I haven't done measurements of the performance improvement yet. There is
one FAIL in nicklas1:1405 but I _think_ this is due to a bug in existing
code.

Arend


Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.221
diff -u -p -r1.221 liberty.h
--- engine/liberty.h    19 Jul 2004 12:23:08 -0000      1.221
+++ engine/liberty.h    19 Aug 2004 18:29:21 -0000
@@ -80,6 +80,7 @@ enum routine_id {
   OWL_CONNECTION_DEFENDS,
   OWL_SUBSTANTIAL,
   OWL_CONFIRM_SAFETY,
+  ANALYZE_SEMEAI,
   NUM_CACHE_ROUTINES
 };
 
@@ -99,7 +100,8 @@ enum routine_id {
   "owl_does_attack", \
   "owl_connection_defends", \
   "owl_substantial", \
-  "owl_confirm_safety"
+  "owl_confirm_safety", \
+  "analyze_semeai"
 
 /* To prioritize between different types of reading, we give a cost
  * ranking to each of the routines above:
@@ -113,7 +115,7 @@ enum routine_id {
  * -1 is left at the end for a consistency check.
  */
 #define ROUTINE_COSTS \
-  3, 3, 4, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, -1
+  3, 3, 4, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, -1
   
 
 const char *routine_id_to_string(enum routine_id routine);
@@ -268,6 +270,18 @@ void store_persistent_owl_cache(enum rou
                                int tactical_nodes, char goal[BOARDMAX],
                                int goal_color);
 void owl_hotspots(float values[BOARDMAX]);
+int search_persistent_semeai_cache(enum routine_id routine,
+                                  int apos, int bpos, int cpos, int color,
+                                  Hash_data *goal_hash,
+                                  int *resulta, int *resultb,
+                                  int *move, int *certain);
+void store_persistent_semeai_cache(enum routine_id routine,
+                                  int apos, int bpos, int cpos, int color,
+                                  Hash_data *goal_hash,
+                                  int resulta, int resultb,
+                                  int move, int certain, int tactical_nodes,
+                                  char goala[BOARDMAX], char goalb[BOARDMAX]);
+
 
 /* readconnect.c */
 int string_connect(int str1, int str2, int *move);
Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.216
diff -u -p -r1.216 owl.c
--- engine/owl.c        2 Jun 2004 02:53:26 -0000       1.216
+++ engine/owl.c        19 Aug 2004 18:29:21 -0000
@@ -351,10 +351,12 @@ owl_analyze_semeai_after_move(int move, 
   int dummy_semeai_move;
   double start = 0.0;
   int reading_nodes_when_called = get_reading_node_counter();
+  int nodes_used;
   int new_dragons[BOARDMAX];
   
   struct local_owl_data *owla;
   struct local_owl_data *owlb;
+  Hash_data goal_hash;
   
   if (!resulta)
     resulta = &dummy_resulta;
@@ -362,7 +364,7 @@ owl_analyze_semeai_after_move(int move, 
     resultb = &dummy_resultb;
   if (!semeai_move)
     semeai_move = &dummy_semeai_move;
-  
+
   if (debug & DEBUG_OWL_PERFORMANCE)
     start = gg_cputime();
 
@@ -397,6 +399,8 @@ owl_analyze_semeai_after_move(int move, 
       }
     }
   }
+
+
   
   sgf_dumptree = NULL;
   if (verbose > 0)
@@ -448,9 +452,9 @@ owl_analyze_semeai_after_move(int move, 
   ASSERT1(board[apos] == OTHER_COLOR(board[bpos]), apos);
   count_variations = 1;
   if (move == PASS_MOVE)
-    TRACE("owl_analyze_semeai: %1m vs. %1m\n", apos, bpos);
+    DEBUG(DEBUG_SEMEAI, "owl_analyze_semeai: %1m vs. %1m\n", apos, bpos);
   else
-    TRACE("owl_analyze_semeai_after_move %C %1m: %1m vs. %1m\n",
+    DEBUG(DEBUG_SEMEAI, "owl_analyze_semeai_after_move %C %1m: %1m vs. %1m\n",
          color, move, apos, bpos);
   
   if (owl) {
@@ -474,6 +478,29 @@ owl_analyze_semeai_after_move(int move, 
 
   result_certain = 1;
 
+  {
+    Hash_data temp = goal_to_hashvalue(owla->goal);
+    goal_hash = goal_to_hashvalue(owlb->goal);
+    hashdata_xor(goal_hash, temp);
+  }
+  if (owl
+      && search_persistent_semeai_cache(ANALYZE_SEMEAI,
+                                       apos, bpos, move, color, &goal_hash,
+                                       resulta, resultb, semeai_move,
+                                       semeai_result_certain)) {
+    if (move == PASS_MOVE) {
+      DEBUG(DEBUG_OWL_PERFORMANCE,
+           "analyze_semeai %1m vs. %1m, result %d %d %1m (cached)\n",
+           apos, bpos, *resulta, *resultb, *semeai_move);
+    }
+    else {
+      DEBUG(DEBUG_OWL_PERFORMANCE,
+           "analyze_semeai_after_move %C %1m: %1m vs. %1m, result %d %d %1m 
(cached)\n",
+           color, move, apos, bpos, *resulta, *resultb, *semeai_move);
+    }
+    return;
+  }
+
   /* In some semeai situations one or both players have the option to
    * choose between seki and ko for the life and death of both. In
    * general this choice depends on the ko threat situation, the
@@ -504,24 +531,30 @@ owl_analyze_semeai_after_move(int move, 
     *resultb = REVERSE_RESULT(*resultb);
   }
 
+  nodes_used = get_reading_node_counter() - reading_nodes_when_called;
   if (move == PASS_MOVE) {
     DEBUG(DEBUG_OWL_PERFORMANCE,
          "analyze_semeai %1m vs. %1m, result %d %d %1m (%d, %d nodes, %f 
seconds)\n",
          apos, bpos, *resulta, *resultb, *semeai_move, local_owl_node_counter,
-         get_reading_node_counter() - reading_nodes_when_called,
-         gg_cputime() - start);
+         nodes_used, gg_cputime() - start);
   }
   else {
     DEBUG(DEBUG_OWL_PERFORMANCE,
          "analyze_semeai_after_move %C %1m: %1m vs. %1m, result %d %d %1m (%d, 
%d nodes, %f seconds)\n",
          color, move, apos, bpos, *resulta, *resultb, *semeai_move,
          local_owl_node_counter,
-         get_reading_node_counter() - reading_nodes_when_called,
-         gg_cputime() - start);
+         nodes_used, gg_cputime() - start);
   }
   
   if (semeai_result_certain)
     *semeai_result_certain = result_certain;
+
+  if (owl)
+    store_persistent_semeai_cache(ANALYZE_SEMEAI, apos, bpos, move, color,
+                                 &goal_hash,
+                                 *resulta, *resultb, *semeai_move,
+                                 result_certain, nodes_used,
+                                 owla->goal, owlb->goal);
 }
 
 
Index: engine/persistent.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/persistent.c,v
retrieving revision 1.25
diff -u -p -r1.25 persistent.c
--- engine/persistent.c 19 Jul 2004 12:23:08 -0000      1.25
+++ engine/persistent.c 19 Aug 2004 18:29:21 -0000
@@ -74,6 +74,9 @@
 #define MAX_BREAKIN_CACHE_DEPTH 1
 #define MAX_BREAKIN_CACHE_SIZE 150
 
+#define MAX_SEMEAI_CACHE_DEPTH 0
+#define MAX_SEMEAI_CACHE_SIZE 150
+
 #define MAX_CACHE_DEPTH        5
 
 
@@ -90,13 +93,15 @@ struct persistent_cache_entry {
   int apos; /* first input coordinate */
   int bpos; /* second input coordinate */
   int cpos; /* third input coordinate */
-  Hash_data goal_hash; /* hash of the goal in break-in reading */
+  int color; /* Move at (cpos) by (color) in analyze_semeai_after_move() */
+  Hash_data goal_hash; /* hash of the goals in break-in and semeai reading */
   int result;
+  int result2;
   int result_certain;
   int remaining_depth;
   int node_limit; 
   int move; /* first result coordinate */
-  int move2; /* second result coordinate */
+  int move2;/* second result coordinate */
   int cost; /* Usually no. of tactical nodes spent on this reading result. */
   int score; /* Heuristic guess of the worth of the cache entry. */
 };
@@ -122,6 +127,9 @@ struct persistent_cache {
 static void compute_active_owl_area(struct persistent_cache_entry *entry,
                                    const char goal[BOARDMAX],
                                    int goal_color);
+static void compute_active_semeai_area(struct persistent_cache_entry *entry,
+                                      const char goal[BOARDMAX],
+                                      int dummy);
 static void compute_active_reading_area(struct persistent_cache_entry *entry,
                                        const char reading_shadow[BOARDMAX],
                                        int dummy);
@@ -153,6 +161,10 @@ struct persistent_cache owl_cache =
     "owl cache", compute_active_owl_area,
     NULL, 0, -1 };
 
+struct persistent_cache semeai_cache =
+  { MAX_SEMEAI_CACHE_SIZE, MAX_SEMEAI_CACHE_DEPTH, 0.75,
+    "semeai cache", compute_active_semeai_area,
+    NULL, 0, -1 };
 
 /* ================================================================ */
 /* Common helper functions.                                        */
@@ -245,6 +257,8 @@ print_persistent_cache_entry(struct pers
   if (entry->cpos != NO_MOVE)
     gprintf("%ocpos            = %1m\n", entry->cpos);
   gprintf("%oresult          = %s\n",  result_to_string(entry->result));
+  if (entry->result2 != 0)
+    gprintf("%oresult2         = %s\n",  result_to_string(entry->result2));
   if (entry->result_certain != -1)
     gprintf("%oresult_certain  = %d\n",  entry->result_certain);
   if (entry->node_limit != -1)
@@ -362,7 +376,8 @@ purge_persistent_cache(struct persistent
 static struct persistent_cache_entry *
 find_persistent_cache_entry(struct persistent_cache *cache,
                            enum routine_id routine, int apos, int bpos,
-                           int cpos, Hash_data *goal_hash, int node_limit)
+                           int cpos, int color,
+                           Hash_data *goal_hash, int node_limit)
 {
   int k;
   for (k = 0; k < cache->current_size; k++) {
@@ -371,6 +386,7 @@ find_persistent_cache_entry(struct persi
        && entry->apos == apos
        && entry->bpos == bpos
        && entry->cpos == cpos
+       && entry->color == color
         && depth - stackp <= entry->remaining_depth
         && (entry->node_limit >= node_limit || entry->result_certain)
         && (goal_hash == NULL
@@ -388,18 +404,22 @@ find_persistent_cache_entry(struct persi
 static int 
 search_persistent_cache(struct persistent_cache *cache,
                        enum routine_id routine, int apos, int bpos,
-                       int cpos, Hash_data *goal_hash, int node_limit, 
-                       int *result, int *move, int *move2, int *certain)
+                       int cpos, int color,
+                       Hash_data *goal_hash, int node_limit, 
+                       int *result, int *result2, int *move, int *move2,
+                       int *certain)
 {
   /* Try to find entry. */
   struct persistent_cache_entry *entry;
-  entry = find_persistent_cache_entry(cache, routine, apos, bpos, cpos,
+  entry = find_persistent_cache_entry(cache, routine, apos, bpos, cpos, color,
                                      goal_hash, node_limit);
   if (entry == NULL)
     return 0;
 
   /* Set return values. */
   *result = entry->result;
+  if (result2)
+    *result2 = entry->result2;
   if (move)
     *move = entry->move;
   if (move2)
@@ -414,6 +434,7 @@ search_persistent_cache(struct persisten
     gprintf("%oRetrieved position from %s:\n", cache->name);
     print_persistent_cache_entry(entry);
   }
+  /* FIXME: This is an ugly hack. */
   if (cache->name == "reading cache"
       && (debug & DEBUG_READING_PERFORMANCE)
       && entry->cost >= MIN_READING_NODES_TO_REPORT) {
@@ -439,8 +460,9 @@ search_persistent_cache(struct persisten
 static void
 store_persistent_cache(struct persistent_cache *cache,
                       enum routine_id routine,
-                      int apos, int bpos, int cpos, Hash_data *goal_hash,
-                      int result, int move, int move2,
+                      int apos, int bpos, int cpos, int color,
+                      Hash_data *goal_hash,
+                      int result, int result2, int move, int move2,
                       int certain, int node_limit,
                       int cost, const char goal[BOARDMAX], int goal_color)
 {
@@ -479,9 +501,11 @@ store_persistent_cache(struct persistent
   entry->apos           = apos;
   entry->bpos           = bpos;
   entry->cpos           = cpos;
+  entry->color          = color;
   if (goal_hash)
     entry->goal_hash    = *goal_hash;
   entry->result         = result;
+  entry->result2        = result2;
   entry->result_certain  = certain;
   entry->node_limit      = node_limit;
   entry->remaining_depth = depth - stackp;
@@ -535,6 +559,7 @@ persistent_cache_init()
   init_cache(&breakin_cache);
   init_cache(&connection_cache);
   init_cache(&owl_cache);
+  init_cache(&semeai_cache);
 }
 
 
@@ -546,6 +571,7 @@ clear_persistent_caches()
   connection_cache.current_size = 0;
   breakin_cache.current_size = 0;
   owl_cache.current_size = 0;
+  semeai_cache.current_size = 0;
 }
 
 /* Discards all persistent cache entries that are no longer useful. 
@@ -559,13 +585,13 @@ purge_persistent_caches()
   purge_persistent_cache(&connection_cache);
   purge_persistent_cache(&breakin_cache);
   purge_persistent_cache(&owl_cache);
+  purge_persistent_cache(&semeai_cache);
 }
 
 /* ================================================================ */
 /*                  Tactical reading functions                      */
 /* ================================================================ */
 
-
 /* Look for a valid read result in the persistent cache.
  * Return 1 if found, 0 otherwise.
  */
@@ -574,8 +600,8 @@ search_persistent_reading_cache(enum rou
                                int *result, int *move)
 {
   return search_persistent_cache(&reading_cache,
-                                routine, str, NO_MOVE, NO_MOVE, NULL,
-                                -1, result, move, NULL, NULL);
+                                routine, str, NO_MOVE, NO_MOVE, EMPTY, NULL,
+                                -1, result, NULL, move, NULL, NULL);
 }
 
 
@@ -585,8 +611,8 @@ store_persistent_reading_cache(enum rout
                               int result, int move, int nodes)
 {
   store_persistent_cache(&reading_cache, routine,
-                        str, NO_MOVE, NO_MOVE, NULL,
-                        result, move, NO_MOVE, -1, -1,
+                        str, NO_MOVE, NO_MOVE, EMPTY, NULL,
+                        result, NO_MOVE, move, NO_MOVE, -1, -1,
                         nodes, shadow, EMPTY);
 }
 
@@ -783,9 +809,9 @@ search_persistent_connection_cache(enum 
                                   int str2, int *result, int *move)
 {
   return search_persistent_cache(&connection_cache, routine,
-                                str1, str2, NO_MOVE, NULL,
+                                str1, str2, NO_MOVE, EMPTY, NULL,
                                 connection_node_limit,
-                                result, move, NULL, NULL);
+                                result, NULL, move, NULL, NULL);
 }
 
 /* Store a new connection result in the persistent cache. */
@@ -795,8 +821,10 @@ store_persistent_connection_cache(enum r
                                  int result, int move, int tactical_nodes,
                                  char connection_shadow[BOARDMAX])
 {
-  store_persistent_cache(&connection_cache, routine, str1, str2, NO_MOVE, NULL,
-                        result, move, NO_MOVE, -1, connection_node_limit,
+  store_persistent_cache(&connection_cache, routine,
+                        str1, str2, NO_MOVE, EMPTY, NULL,
+                        result, NO_MOVE, move, NO_MOVE, -1,
+                        connection_node_limit,
                         tactical_nodes, connection_shadow, EMPTY);
 }
 
@@ -930,8 +958,8 @@ search_persistent_breakin_cache(enum rou
                                int node_limit, int *result, int *move)
 {
   return search_persistent_cache(&breakin_cache, routine,
-                                str, NO_MOVE, NO_MOVE, goal_hash, 
-                                node_limit, result, move, NULL, NULL);
+                                str, NO_MOVE, NO_MOVE, EMPTY, goal_hash, 
+                                node_limit, result, NULL, move, NULL, NULL);
 }
                          
 /* Store a new breakin result in the persistent cache. */
@@ -943,8 +971,8 @@ store_persistent_breakin_cache(enum rout
                               char breakin_shadow[BOARDMAX])
 {
   store_persistent_cache(&breakin_cache, routine,
-                        str, NO_MOVE, NO_MOVE, goal_hash,
-                        result, move, NO_MOVE, -1, breakin_node_limit,
+                        str, NO_MOVE, NO_MOVE, EMPTY, goal_hash,
+                        result, NO_MOVE, move, NO_MOVE, -1, breakin_node_limit,
                         tactical_nodes, breakin_shadow, EMPTY);
 }
   
@@ -1070,9 +1098,9 @@ search_persistent_owl_cache(enum routine
                            int *result, int *move, int *move2, int *certain)
 {
   return search_persistent_cache(&owl_cache,
-                                routine, apos, bpos, cpos, NULL,
+                                routine, apos, bpos, cpos, EMPTY, NULL,
                                 owl_node_limit,
-                                result, move, move2, certain);
+                                result, NULL, move, move2, certain);
 }
   
 
@@ -1083,20 +1111,25 @@ store_persistent_owl_cache(enum routine_
                           int tactical_nodes,
                           char goal[BOARDMAX], int goal_color)
 {
-  store_persistent_cache(&owl_cache, routine, apos, bpos, cpos, NULL,
-                        result, move, move2, certain, owl_node_limit,
+  store_persistent_cache(&owl_cache, routine, apos, bpos, cpos, EMPTY, NULL,
+                        result, NO_MOVE, move, move2, certain, owl_node_limit,
                         tactical_nodes, goal, goal_color);
 }
 
 
-static void compute_active_owl_area(struct persistent_cache_entry *entry,
-                                   const char goal[BOARDMAX],
-                                   int goal_color)
+/* This function is used by owl and semai active area computation. We assume
+ * that (goal) marks a dragon of color (goal_color), i.e. all intersections
+ * in the goal that are not a stone of this color are ignored. The calling
+ * functions must have zeroed the active area, and is allowed to preset
+ * some intersection to be active.
+ */
+static void
+compute_active_owl_type_area(const char goal[BOARDMAX], int goal_color,
+                            signed char active[BOARDMAX])
 {
   int k, r;
   int pos;
   int other = OTHER_COLOR(goal_color);
-  signed char active[BOARDMAX];
 
   /* We let the active area be the goal +
    * distance four expansion through empty intersections and own stones +
@@ -1108,14 +1141,8 @@ static void compute_active_owl_area(stru
    */
   for (pos = BOARDMIN; pos < BOARDMAX; pos++)
     if (ON_BOARD(pos))
-      active[pos] = (goal[pos] != 0);
-
-  /* Also add critical moves to the active area. */
-  if (ON_BOARD1(entry->move))
-    active[entry->move] = 1;
-
-  if (ON_BOARD1(entry->move2))
-    active[entry->move2] = 1;
+      active[pos] |= (goal[pos]
+                     && board[pos] == goal_color);
 
   /* Distance four expansion through empty intersections and own stones. */
   for (k = 1; k < 5; k++) {
@@ -1180,7 +1207,25 @@ static void compute_active_owl_area(stru
       }
     }
   }
-  
+}
+
+static void
+compute_active_owl_area(struct persistent_cache_entry *entry,
+                       const char goal[BOARDMAX], int goal_color)
+{
+  int pos;
+  signed char active[BOARDMAX];
+  memset(active, 0, BOARDMAX);
+
+  /* Add critical moves to the active area. */
+  if (ON_BOARD1(entry->move))
+    active[entry->move] = 1;
+
+  if (ON_BOARD1(entry->move2))
+    active[entry->move2] = 1;
+
+  compute_active_owl_type_area(goal, goal_color, active);
+
   for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
     int value = board[pos];
     if (!ON_BOARD(pos))
@@ -1195,6 +1240,88 @@ static void compute_active_owl_area(stru
 }
 
 
+/* ================================================================ */
+/*                    Semeai reading functions                      */
+/* ================================================================ */
+
+/* Look for stored result in semeai cache. Returns 1 if result found, 0
+ * otherwise.
+ */
+int
+search_persistent_semeai_cache(enum routine_id routine,
+                              int apos, int bpos, int cpos, int color,
+                              Hash_data *goal_hash,
+                              int *resulta, int *resultb,
+                              int *move, int *certain)
+{
+  return search_persistent_cache(&semeai_cache, routine, apos, bpos, cpos,
+                                color, goal_hash, semeai_node_limit,
+                                resulta, resultb, move, NULL, certain);
+}
+
+
+/* Store a new read result in the persistent semeai cache. */
+void
+store_persistent_semeai_cache(enum routine_id routine,
+                             int apos, int bpos, int cpos, int color,
+                             Hash_data *goal_hash,
+                             int resulta, int resultb, int move, int certain,
+                             int tactical_nodes,
+                             char goala[BOARDMAX], char goalb[BOARDMAX])
+{
+  char goal[BOARDMAX];
+  int pos;
+
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+    goal[pos] = goala[pos] || goalb[pos];
+  store_persistent_cache(&semeai_cache, routine,
+                        apos, bpos, cpos, color, goal_hash,
+                        resulta, resultb, move, NO_MOVE,
+                        certain, semeai_node_limit,
+                        tactical_nodes, goal, EMPTY);
+}
+
+
+static void
+compute_active_semeai_area(struct persistent_cache_entry *entry,
+                          const char goal[BOARDMAX], int dummy)
+{
+  int pos;
+  signed char active_b[BOARDMAX];
+  signed char active_w[BOARDMAX];
+  UNUSED(dummy);
+  memset(active_b, 0, BOARDMAX);
+  memset(active_w, 0, BOARDMAX);
+
+  /* Add critical move to the active area. */
+  if (ON_BOARD1(entry->move)) {
+    active_b[entry->move] = 1;
+    active_w[entry->move] = 1;
+  }
+  if (ON_BOARD1(entry->cpos)) {
+    active_b[entry->cpos] = 1;
+    active_w[entry->cpos] = 1;
+  }
+
+  compute_active_owl_type_area(goal, BLACK, active_b);
+  compute_active_owl_type_area(goal, WHITE, active_b);
+
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+    int value = board[pos];
+    if (!ON_BOARD(pos))
+      continue;
+    if (!active_b[pos] && !active_w[pos])
+      value = GRAY;
+    else if (IS_STONE(board[pos]) && countlib(pos) > 4
+            && (active_b[pos] > 0 || active_w[pos] > 0))
+      value |= HIGH_LIBERTY_BIT;
+    
+    entry->board[pos] = value;
+  }
+}
+
+
+
 /* Helper for the owl_hotspots() function below. */
 static void
 mark_dragon_hotspot_values(float values[BOARDMAX], int dr,




reply via email to

[Prev in Thread] Current Thread [Next in Thread]