[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnugo-devel] more board.c optimizations
From: |
Paul Pogonyshev |
Subject: |
[gnugo-devel] more board.c optimizations |
Date: |
Sun, 22 Jun 2003 23:24:44 -0400 |
User-agent: |
KMail/1.5.9 |
after a long delay, here is another one :)
- added cache for results of approxlib() and accuratelib()
- new static functions do_approxlib() and do_accuratelib() in board.c
i also took the opportunity to remove old (#if 0'ed) versions of
fastlib(), accurate_approxlib() and is_self_atari(). it seems the
new ones are working just fine. however, if anyone objects, i can
take these changes out.
speedup seems to be around 1.5% - 2.0%.
results on fifth_batch are available:
before:
real 78m35.788s
user 78m33.537s
sys 0m1.473s
after:
real 77m16.205s
user 77m12.645s
sys 0m1.494s
with a speedup of 1.7%.
no regression changes on fifth batch and i'm quite certain there are
no changes at all.
Paul
p.s. i also tried to make cache depth-dependant (separate cache for
each depth level up to 15), but it turned out to be a bad idea.
probably because such a cache was quite large (almost 400k).
Index: engine/board.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/board.c,v
retrieving revision 1.76
diff -u -p -r1.76 board.c
--- engine/board.c 17 Jun 2003 10:54:47 -0000 1.76
+++ engine/board.c 22 Jun 2003 20:06:24 -0000
@@ -297,13 +297,17 @@ static int string_mark;
/* Forward declarations. */
static int do_trymove(int pos, int color, int ignore_ko);
static void undo_trymove(void);
+
+static int do_approxlib(int pos, int color, int maxlib, int *libs);
+static int slow_approxlib(int pos, int color, int maxlib, int *libs);
+static int do_accuratelib(int pos, int color, int maxlib, int *libs);
+
static void new_position(void);
static int propagate_string(int stone, int str);
static void find_liberties_and_neighbors(int s);
static int do_remove_string(int s);
static void do_commit_suicide(int pos, int color);
static void do_play_move(int pos, int color);
-static int slow_approxlib(int pos, int color, int maxlib, int *libs);
/* Statistics. */
@@ -2183,137 +2187,147 @@ fastlib(int pos, int color, int ignore_c
}
-/* Enable this to check that the newer implementation above gives
- * correct answers (using the older implementation). Don't forget to
- * rename the newer implementation to `fastlib_new'.
- */
-#if 0
-static int
-fastlib_old(int pos, int color, int ignore_capture)
-{
- int k;
- int ally1 = NO_MOVE;
- int ally2 = NO_MOVE;
- int fast_liberties = 0;
- int other = OTHER_COLOR(color);
+/* Effectively true unless we store full position in hash. */
+#define USE_BOARD_CACHES (NUM_HASHVALUES <= 4 && !FULL_POSITION_IN_HASH)
- ASSERT1(board[pos] == EMPTY, pos);
- ASSERT1(IS_STONE(color), pos);
+struct board_cache_entry {
+ int threshold;
+ int liberties;
+ Hash_data position_hash;
+};
- for (k = 0; k < 4; k++) {
- int neighbor = pos + delta[k];
- if (board[neighbor] == color) {
- if (ally1 != NO_MOVE) {
- if (string_number[ally1] != string_number[neighbor]) {
- if (ally2 != NO_MOVE) {
- if (string_number[ally2] != string_number[neighbor]) {
- /* More than 2 allies not implemented (yet).*/
- return -1;
- }
- }
- else
- ally2 = neighbor;
- }
- }
- else
- ally1 = neighbor;
- }
- }
-
- for (k = 0; k < 4; k++) {
- int neighbor = pos + delta[k];
- int neighbor_color = board[neighbor];
- if (!ignore_capture
- && neighbor_color == other
- && LIBERTIES(neighbor) == 1) {
- int neighbor_size = COUNTSTONES(neighbor);
-#if 0
- if ((neighbor_size <= 2 && !ally1)
- || (neighbor_size == 1
- && ally1
- && !ally2
- && COUNTSTONES(ally1) == 1)) {
-#else
- if (neighbor_size == 1
- || (neighbor_size == 2 && !ally1)) {
-#endif
- /* Here, we can gain only the adjacent new liberty. */
- fast_liberties++;
- }
- else
- return -1;
- }
- if (neighbor_color == EMPTY) {
- if ((!ally1 || !neighbor_of_string(neighbor, ally1))
- && (!ally2 || !neighbor_of_string(neighbor, ally2))) {
- fast_liberties++;
- }
- }
- }
- if (ally1)
- fast_liberties += LIBERTIES(ally1) - 1;
- if (ally2)
- fast_liberties += LIBERTIES(ally2) - count_common_libs(ally1, ally2);
-
- return fast_liberties;
-}
+/* approxlib() cache. */
+struct board_cache_entry approxlib_cache[BOARDMAX][2];
-int
-fastlib(int pos, int color, int ignore_captures)
+/* Clears approxlib() cache. This function should be called only once
+ * during engine initialization. Sets thresholds to zero.
+ */
+void
+clear_approxlib_cache(void)
{
- int liberties1 = fastlib_old(pos, color, ignore_captures);
- int liberties2 = fastlib_new(pos, color, ignore_captures);
-
- ASSERT1(liberties1 == liberties2, pos);
+ int pos;
- return liberties1;
+ for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+ approxlib_cache[pos][0].threshold = 0;
+ approxlib_cache[pos][1].threshold = 0;
+ }
}
-#endif
+
/* Find the liberties a stone of the given color would get if played
* at (pos), ignoring possible captures of opponent stones. (pos)
* must be empty. If libs != NULL, the locations of up to maxlib
- * liberties are written into libs[]. The counting of
- * liberties may or may not be halted when maxlib is reached. The
- * number of liberties found is returned.
+ * liberties are written into libs[]. The counting of liberties may
+ * or may not be halted when maxlib is reached. The number of liberties
+ * found is returned.
*
* If you want the number or the locations of all liberties, however
* many they are, you should pass MAXLIBS as the value for maxlib and
* allocate space for libs[] accordingly.
*/
-
int
approxlib(int pos, int color, int maxlib, int *libs)
{
- int k;
- int liberties = 0;
+ int liberties;
+
+#ifdef USE_BOARD_CACHES
+
+ struct board_cache_entry *entry = &approxlib_cache[pos][color - 1];
ASSERT1(board[pos] == EMPTY, pos);
ASSERT1(IS_STONE(color), pos);
if (!libs) {
- int fast_liberties = fastlib(pos, color, 1);
- if (fast_liberties >= 0)
- return fast_liberties;
+ /* First see if this result is cached. */
+ if (hashdata.hashval[0] == entry->position_hash.hashval[0]
+ && maxlib <= entry->threshold) {
+ int k;
+ for (k = 1; k < NUM_HASHVALUES; k++) {
+ if (hashdata.hashval[k] != entry->position_hash.hashval[k])
+ break;
+ }
+
+ /* If position hash values match and `maxlib' is not greater than
+ * the threshold, we already know the result.
+ */
+ if (k == NUM_HASHVALUES)
+ return entry->liberties;
+ }
+
+ liberties = fastlib(pos, color, 1);
+ if (liberties >= 0) {
+ /* Since fastlib() always returns precise result and doesn't take
+ * `maxlib' into account, we set threshold to MAXLIBS so that this
+ * result is used regardless of any `maxlib' passed.
+ */
+ entry->threshold = MAXLIBS;
+ entry->liberties = liberties;
+ entry->position_hash = hashdata;
+
+ return liberties;
+ }
}
+ /* We initialize the cache entry threshold to `maxlib'. If do_appoxlib()
+ * or slow_approxlib() finds all the liberties (that is, they don't use
+ * `maxlib' value for an early return), they will set threshold to
+ * MAXLIBS themselves.
+ */
+ entry->threshold = maxlib;
+
+ if (maxlib <= MAX_LIBERTIES)
+ liberties = do_approxlib(pos, color, maxlib, libs);
+ else
+ liberties = slow_approxlib(pos, color, maxlib, libs);
+
+ entry->liberties = liberties;
+ entry->position_hash = hashdata;
+
+#else /* not USE_BOARD_CACHES */
+
+ ASSERT1(board[pos] == EMPTY, pos);
+ ASSERT1(IS_STONE(color), pos);
+
+ if (!libs) {
+ liberties = fastlib(pos, color, 1);
+ if (liberties >= 0)
+ return liberties;
+ }
+
+ if (maxlib <= MAX_LIBERTIES)
+ liberties = do_approxlib(pos, color, maxlib, libs);
+ else
+ liberties = slow_approxlib(pos, color, maxlib, libs);
+
+#endif /* not USE_BOARD_CACHES */
+
+ return liberties;
+}
+
+
+/* Does the real work of approxlib(). */
+static int
+do_approxlib(int pos, int color, int maxlib, int *libs)
+{
+ int k;
+ int liberties = 0;
+
/* Look for empty neighbors and the liberties of the adjacent
* strings of the given color. The algorithm below won't work
* correctly if any of the adjacent strings have more than
* MAX_LIBERTIES liberties AND maxlib is larger than MAX_LIBERTIES.
- * If this might be the case, we use a more robust fallback.
+ * therefore approxlib() calls more robust slow_approxlib() if
+ * this might be the case.
*/
- if (maxlib > MAX_LIBERTIES)
- return slow_approxlib(pos, color, maxlib, libs);
-
+
/* Start by marking pos itself so it isn't counted among its own
* liberties.
*/
liberty_mark++;
MARK_LIBERTY(pos);
-
+
if (UNMARKED_LIBERTY(SOUTH(pos))) {
if (libs != NULL)
libs[liberties] = SOUTH(pos);
@@ -2390,11 +2404,10 @@ approxlib(int pos, int color, int maxlib
if (libs != NULL)
libs[liberties] = EAST(pos);
liberties++;
- /* Stop counting if we reach maxlib. */
- if (liberties >= maxlib)
- return liberties;
/* Unneeded since we're about to leave. */
#if 0
+ if (liberties >= maxlib)
+ return liberties;
MARK_LIBERTY(EAST(pos));
#endif
}
@@ -2413,10 +2426,98 @@ approxlib(int pos, int color, int maxlib
}
}
+#if USE_BOARD_CACHES
+ /* If we reach here, then we have counted _all_ the liberties, so
+ * we set threshold to MAXLIBS (the result is the same regardless
+ * of `maxlib' value).
+ */
+ if (!libs)
+ approxlib_cache[pos][color - 1].threshold = MAXLIBS;
+#endif
+ return liberties;
+}
+
+
+/* Find the liberties a move of the given color at pos would have,
+ * excluding possible captures, by traversing all adjacent friendly
+ * strings. This is a fallback used by approxlib() when a faster
+ * algorithm can't be used.
+ */
+static int
+slow_approxlib(int pos, int color, int maxlib, int *libs)
+{
+ int k;
+ int liberties = 0;
+
+ liberty_mark++;
+ MARK_LIBERTY(pos);
+ string_mark++;
+ for (k = 0; k < 4; k++) {
+ int d = delta[k];
+ if (UNMARKED_LIBERTY(pos + d)) {
+ if (libs)
+ libs[liberties] = pos + d;
+ liberties++;
+ if (liberties == maxlib)
+ return liberties;
+ MARK_LIBERTY(pos + d);
+ }
+ else if (board[pos + d] == color
+ && UNMARKED_STRING(pos + d)) {
+ int s = string_number[pos + d];
+ int pos2;
+ pos2 = FIRST_STONE(s);
+ do {
+ int l;
+ for (l = 0; l < 4; l++) {
+ int d2 = delta[l];
+ if (UNMARKED_LIBERTY(pos2 + d2)) {
+ if (libs)
+ libs[liberties] = pos2 + d2;
+ liberties++;
+ if (liberties == maxlib)
+ return liberties;
+ MARK_LIBERTY(pos2 + d2);
+ }
+ }
+
+ pos2 = NEXT_STONE(pos2);
+ } while (!BACK_TO_FIRST_STONE(s, pos2));
+ MARK_STRING(pos + d);
+ }
+ }
+
+#if USE_BOARD_CACHES
+ /* If we reach here, then we have counted _all_ the liberties, so
+ * we set threshold to MAXLIBS (the result is the same regardless
+ * of `maxlib' value).
+ */
+ if (!libs)
+ approxlib_cache[pos][color - 1].threshold = MAXLIBS;
+#endif
return liberties;
}
+/* accuratelib() cache. */
+struct board_cache_entry accuratelib_cache[BOARDMAX][2];
+
+
+/* Clears accuratelib() cache. This function should be called only once
+ * during engine initialization. Sets thresholds to zero.
+ */
+void
+clear_accuratelib_cache(void)
+{
+ int pos;
+
+ for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+ accuratelib_cache[pos][0].threshold = 0;
+ accuratelib_cache[pos][1].threshold = 0;
+ }
+}
+
+
/* Find the liberties a stone of the given color would get if played
* at (pos). This function takes into consideration all captures. Its
* return value is exact in that sense it counts all the liberties,
@@ -2434,25 +2535,88 @@ approxlib(int pos, int color, int maxlib
* many they are, you should pass MAXLIBS as the value for maxlib and
* allocate space for libs[] accordingly.
*/
-
int
accuratelib(int pos, int color, int maxlib, int *libs)
{
- int k, l;
- int liberties = 0;
- int lib;
- int captured[4];
- int captures = 0;
+ int liberties;
+
+#ifdef USE_BOARD_CACHES
+
+ struct board_cache_entry *entry = &accuratelib_cache[pos][color - 1];
ASSERT1(board[pos] == EMPTY, pos);
ASSERT1(IS_STONE(color), pos);
if (!libs) {
- int fast_liberties = fastlib(pos, color, 0);
- if (fast_liberties >= 0)
- return fast_liberties;
+ /* First see if this result is cached. */
+ if (hashdata.hashval[0] == entry->position_hash.hashval[0]
+ && maxlib <= entry->threshold) {
+ int k;
+ for (k = 1; k < NUM_HASHVALUES; k++) {
+ if (hashdata.hashval[k] != entry->position_hash.hashval[k])
+ break;
+ }
+
+ /* If position hash values match and `maxlib' is not greater than
+ * the threshold, we already know the result.
+ */
+ if (k == NUM_HASHVALUES)
+ return entry->liberties;
+ }
+
+ liberties = fastlib(pos, color, 0);
+ if (liberties >= 0) {
+ /* Since fastlib() always returns precise result and doesn't take
+ * `maxlib' into account, we set threshold to MAXLIBS so that this
+ * result is used regardless of any `maxlib' passed.
+ */
+ entry->threshold = MAXLIBS;
+ entry->liberties = liberties;
+ entry->position_hash = hashdata;
+
+ return liberties;
+ }
}
+ liberties = do_accuratelib(pos, color, maxlib, libs);
+
+ /* If accuratelib() found less than `maxlib' liberties, then its
+ * result is certainly independent of `maxlib' and we set threshold
+ * to MAXLIBS.
+ */
+ entry->threshold = liberties < maxlib ? MAXLIBS : maxlib;
+ entry->liberties = liberties;
+ entry->position_hash = hashdata;
+
+#else /* not USE_BOARD_CACHES */
+
+ ASSERT1(board[pos] == EMPTY, pos);
+ ASSERT1(IS_STONE(color), pos);
+
+ if (!libs) {
+ liberties = fastlib(pos, color, 0);
+ if (liberties >= 0)
+ return liberties;
+ }
+
+ liberties = do_accuratelib(pos, color, maxlib, libs);
+
+#endif /* not USE_BOARD_CACHES */
+
+ return liberties;
+}
+
+
+/* Does the real work of accuratelib(). */
+static int
+do_accuratelib(int pos, int color, int maxlib, int *libs)
+{
+ int k, l;
+ int liberties = 0;
+ int lib;
+ int captured[4];
+ int captures = 0;
+
string_mark++;
liberty_mark++;
MARK_LIBERTY(pos);
@@ -2602,76 +2766,6 @@ accuratelib(int pos, int color, int maxl
}
-#if 0
-
-/* Functionally identical to accuratelib(), within the flexibility
- * allowed by maxlib. Older implementation.
- *
- * Play a stone at (pos) and count the number of liberties for the
- * resulting string. This requires (pos) to be empty.
- *
- * This function differs from approxlib() by the fact that it removes
- * captured stones before counting the liberties.
- */
-
-int
-accurate_approxlib(int pos, int color, int maxlib, int *libs)
-{
- int fast_liberties = -1;
- int liberties = 0;
- SGFTree *save_sgf_dumptree = sgf_dumptree;
- int save_count_variations = count_variations;
-
- ASSERT1(board[pos] == EMPTY, pos);
- ASSERT1(IS_STONE(color), pos);
-
- if (!libs) {
- fast_liberties = fastlib(pos, color, 0);
- if (fast_liberties >= 0) {
- /* uncomment to confirm equivalence with accuratelib */
- if (0) {
- int dlibs[MAXLIBS];
- int accuratelib_result = accuratelib(pos, color, maxlib, dlibs);
-
- ASSERT1(gg_min(maxlib, fast_liberties)
- == gg_min(maxlib, accuratelib_result), pos);
- }
- return fast_liberties;
- }
- }
-
- sgf_dumptree = 0;
- /* Use tryko() since we don't care whether the move would violate
- * the ko rule.
- */
- if (tryko(pos, color, "accurate approxlib", EMPTY, 0)) {
- if (libs != NULL)
- liberties = findlib(pos, maxlib, libs);
- else
- liberties = countlib(pos);
- popgo();
- }
-
- if (fast_liberties >= 0 && liberties > 0) {
- ASSERT1(fast_liberties == liberties, pos);
- }
-
- sgf_dumptree = save_sgf_dumptree;
- count_variations = save_count_variations;
-
- /* uncomment to confirm equivalence with accuratelib */
- if (0) {
- int dlibs[MAXLIBS];
- int accuratelib_result = accuratelib(pos, color, maxlib, dlibs);
-
- ASSERT1(gg_min(maxlib, liberties)
- == gg_min(maxlib, accuratelib_result), pos);
- }
- return liberties;
-}
-
-#endif
-
/* Find the number of common liberties of the two strings at str1 and str2.
*/
@@ -3146,42 +3240,6 @@ is_self_atari(int pos, int color)
}
-/* Alternative implementation.
- * Note that incremental_sloppy_self_atari() is already integrated
- * into the newer implementation above.
- */
-
-#if 0
-
-int
-is_self_atari(int pos, int color)
-{
- int liberties;
- int result;
-
- ASSERT_ON_BOARD1(pos);
- ASSERT1(board[pos] == EMPTY, pos);
- ASSERT1(IS_STONE(color), pos);
-
- /* 1. Try first without really putting the stone on the board. */
- /* FIXME: Integrate incremental_sloppy_self_atari() here. */
- result = incremental_sloppy_self_atari(pos, color);
- if (result != -1)
- return result;
-
- /* 2. It was not so easy. Now see if we can put the stone on the board.
- * If we can't, this is a self atari.
- */
- if (!do_trymove(pos, color, 1))
- return 1;
- liberties = string[string_number[pos]].liberties;
- silent_popgo();
-
- return liberties <= 1;
-}
-
-#endif
-
/*
* Returns true if pos is a liberty of the string at str.
*/
@@ -4388,59 +4446,6 @@ do_play_move(int pos, int color)
board_ko_pos = string[s].libs[0];
hashdata_invert_ko(&hashdata, board_ko_pos);
}
-}
-
-
-/* Find the liberties a move of the given color at pos would have,
- * excluding possible captures, by traversing all adjacent friendly
- * strings. This is a fallback used by approxlib() when a
- * faster algorithm can't be used.
- */
-
-static int
-slow_approxlib(int pos, int color, int maxlib, int *libs)
-{
- int liberties = 0;
- int k;
-
- liberty_mark++;
- MARK_LIBERTY(pos);
- string_mark++;
- for (k = 0; k < 4; k++) {
- int d = delta[k];
- if (UNMARKED_LIBERTY(pos + d)) {
- if (libs)
- libs[liberties] = pos + d;
- liberties++;
- if (liberties == maxlib)
- return liberties;
- MARK_LIBERTY(pos + d);
- }
- else if (board[pos + d] == color
- && UNMARKED_STRING(pos + d)) {
- int s = string_number[pos + d];
- int pos2;
- pos2 = FIRST_STONE(s);
- do {
- int l;
- for (l = 0; l < 4; l++) {
- int d2 = delta[l];
- if (UNMARKED_LIBERTY(pos2 + d2)) {
- if (libs)
- libs[liberties] = pos2 + d2;
- liberties++;
- if (liberties == maxlib)
- return liberties;
- MARK_LIBERTY(pos2 + d2);
- }
- }
-
- pos2 = NEXT_STONE(pos2);
- } while (!BACK_TO_FIRST_STONE(s, pos2));
- MARK_STRING(pos + d);
- }
- }
- return liberties;
}
Index: engine/interface.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/interface.c,v
retrieving revision 1.40
diff -u -p -r1.40 interface.c
--- engine/interface.c 1 May 2003 20:55:26 -0000 1.40
+++ engine/interface.c 22 Jun 2003 20:06:26 -0000
@@ -48,6 +48,9 @@ init_gnugo(float memory, unsigned int se
#if EXPERIMENTAL_READING
tree_match_init();
#endif
+
+ clear_approxlib_cache();
+ clear_accuratelib_cache();
}
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.183
diff -u -p -r1.183 liberty.h
--- engine/liberty.h 19 Jun 2003 18:57:45 -0000 1.183
+++ engine/liberty.h 22 Jun 2003 20:06:33 -0000
@@ -202,6 +202,10 @@ void incremental_order_moves(int move, i
int *captured_stones, int *threatened_stones,
int *saved_stones, int *number_open);
+/* Board caches initialization functions. */
+void clear_approxlib_cache(void);
+void clear_accuratelib_cache(void);
+
void transformation_init(void);
- [gnugo-devel] more board.c optimizations,
Paul Pogonyshev <=