From eca7f93185ce244086af1624a778a6abd96dc36f Mon Sep 17 00:00:00 2001 From: Jean-loup Gailly Date: Sat, 3 Apr 2010 17:06:17 +0200 Subject: [PATCH] Distributed engine: exchange also amaf stats between master and slaves. --- distributed/distributed.c | 40 +++++++++++++------------ distributed/distributed.h | 6 ++++ uct/internal.h | 5 ++-- uct/uct.c | 75 +++++++++++++++++++++++++++++------------------ 4 files changed, 77 insertions(+), 49 deletions(-) diff --git a/distributed/distributed.c b/distributed/distributed.c index f0aa015..c907d83 100644 --- a/distributed/distributed.c +++ b/distributed/distributed.c @@ -492,12 +492,12 @@ distributed_notify(struct engine *e, struct board *b, int id, char *cmd, char *a } /* genmoves returns a line "=id played_own total_playouts threads keep_looking[ reserved]" - * then a list of lines "coord playouts value". + * then a list of lines "coord playouts value amaf_playouts amaf_value". * Return the move with most playouts, and additional stats. * Keep this code in sync with uct_getstats(). * slave_lock is held on entry and on return. */ static coord_t -select_best_move(struct board *b, struct move_stats *stats, int *played, +select_best_move(struct board *b, struct move_stats2 *stats, int *played, int *total_playouts, int *total_threads, bool *keep_looking) { assert(reply_count > 0); @@ -524,12 +524,15 @@ select_best_move(struct board *b, struct move_stats *stats, int *played, r = strchr(r, '\n'); char move[64]; - struct move_stats s; - while (r && sscanf(++r, "%63s %d %f", move, &s.playouts, &s.value) == 3) { + struct move_stats2 s; + while (r && sscanf(++r, "%63s %d %f %d %f", move, &s.u.playouts, + &s.u.value, &s.amaf.playouts, &s.amaf.value) == 5) { coord_t *c = str2coord(move, board_size(b)); - stats_add_result(&stats[*c], s.value, s.playouts); - if (stats[*c].playouts > best_playouts) { - best_playouts = stats[*c].playouts; + stats_add_result(&stats[*c].u, s.u.value, s.u.playouts); + stats_add_result(&stats[*c].amaf, s.amaf.value, s.amaf.playouts); + + if (stats[*c].u.playouts > best_playouts) { + best_playouts = stats[*c].u.playouts; best_move = *c; } coord_done(c); @@ -548,7 +551,7 @@ select_best_move(struct board *b, struct move_stats *stats, int *played, * slave_lock is held on entry and on return. */ static void genmoves_args(char *args, struct board *b, enum stone color, int played, - struct time_info *ti, struct move_stats *stats, int min_playouts) + struct time_info *ti, struct move_stats2 *stats, int min_playouts) { char *end = args + CMDS_SIZE; char *s = args + snprintf(args, CMDS_SIZE, "%s %d", stone2str(color), played); @@ -561,10 +564,11 @@ genmoves_args(char *args, struct board *b, enum stone color, int played, s += snprintf(s, end - s, "\n"); if (stats) { foreach_point(b) { - if (stats[c].playouts <= min_playouts) continue; - s += snprintf(s, end - s, "%s %d %.7f\n", + if (stats[c].u.playouts <= min_playouts) continue; + s += snprintf(s, end - s, "%s %d %.7f %d %.7f\n", coord2sstr(c, b), - stats[c].playouts, stats[c].value); + stats[c].u.playouts, stats[c].u.value, + stats[c].amaf.playouts, stats[c].amaf.value); } foreach_point_end; } s += snprintf(s, end - s, "\n"); @@ -598,7 +602,7 @@ distributed_genmove(struct engine *e, struct board *b, struct time_info *ti, /* Combined move stats from all slaves, only for children * of the root node, plus 2 for pass and resign. */ - struct move_stats *stats = alloca((board_size2(b)+2) * sizeof(struct move_stats)); + struct move_stats2 *stats = alloca((board_size2(b)+2) * sizeof(struct move_stats2)); stats += 2; pthread_mutex_lock(&slave_lock); @@ -627,13 +631,13 @@ distributed_genmove(struct engine *e, struct board *b, struct time_info *ti, snprintf(buf, sizeof(buf), "temp winner is %s %s with score %1.4f (%d/%d games)" " %d slaves %d threads\n", - stone2str(color), coord, get_value(stats[best].value, color), - stats[best].playouts, playouts, reply_count, threads); + stone2str(color), coord, get_value(stats[best].u.value, color), + stats[best].u.playouts, playouts, reply_count, threads); logline(NULL, "* ", buf); } /* Send the command with the same gtp id, to avoid discarding * a reply to a previous genmoves at the same move. */ - genmoves_args(args, b, color, played, ti, stats, stats[best].playouts / 100); + genmoves_args(args, b, color, played, ti, stats, stats[best].u.playouts / 100); update_cmd(b, cmd, args, false); } int replies = reply_count; @@ -643,7 +647,7 @@ distributed_genmove(struct engine *e, struct board *b, struct time_info *ti, dist->my_last_move.color = color; dist->my_last_move.coord = best; - dist->my_last_stats = stats[best]; + dist->my_last_stats = stats[best].u; /* Tell the slaves to commit to the selected move, overwriting * the last "pachi-genmoves" in the command history. */ @@ -659,8 +663,8 @@ distributed_genmove(struct engine *e, struct board *b, struct time_info *ti, "GLOBAL WINNER is %s %s with score %1.4f (%d/%d games)\n" "genmove %d games in %0.2fs %d slaves %d threads (%d games/s," " %d games/s/slave, %d games/s/thread)\n", - stone2str(color), coord, get_value(stats[best].value, color), - stats[best].playouts, playouts, played, time, replies, threads, + stone2str(color), coord, get_value(stats[best].u.value, color), + stats[best].u.playouts, playouts, played, time, replies, threads, (int)(played/time), (int)(played/time/replies), (int)(played/time/threads)); logline(NULL, "* ", buf); diff --git a/distributed/distributed.h b/distributed/distributed.h index f0c4b6d..fdf8622 100644 --- a/distributed/distributed.h +++ b/distributed/distributed.h @@ -2,6 +2,7 @@ #define ZZGO_DISTRIBUTED_DISTRIBUTED_H #include "engine.h" +#include "stats.h" #define DIST_GAMELEN 1000 @@ -10,6 +11,11 @@ #define move_number(id) ((id) % DIST_GAMELEN) #define reply_disabled(id) ((id) < DIST_GAMELEN) +struct move_stats2 { + struct move_stats u; + struct move_stats amaf; +}; + struct engine *engine_distributed_init(char *arg, struct board *b); #endif diff --git a/uct/internal.h b/uct/internal.h index e44002f..ae3771b 100644 --- a/uct/internal.h +++ b/uct/internal.h @@ -8,6 +8,7 @@ #include "ownermap.h" #include "playout.h" #include "stats.h" +#include "distributed/distributed.h" struct tree; struct tree_node; @@ -19,8 +20,8 @@ struct uct_dynkomi; /* Stats for each child of the root node. */ struct node_stats { - struct move_stats last_sent_own; - struct move_stats added_from_others; + struct move_stats2 last_sent_own; + struct move_stats2 added_from_others; struct tree_node *node; }; diff --git a/uct/uct.c b/uct/uct.c index f593742..d1cc9e6 100644 --- a/uct/uct.c +++ b/uct/uct.c @@ -912,9 +912,10 @@ uct_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone return coord_copy(best_coord); } -/* Get stats updates for the distributed engine. Return a buffer - * with one line "total_playouts threads keep_looking" then a list of lines - * "coord playouts value". The last line must not end with \n. +/* Get stats updates for the distributed engine. Return a buffer with + * one line "played_own root_playouts threads keep_looking" then a list + * of lines "coord playouts value amaf_playouts amaf_value". + * The last line must not end with \n. * If c is pass or resign, add this move with root->playouts weight. * This function is called only by the main thread, but may be * called while the tree is updated by the worker threads. @@ -929,10 +930,12 @@ uct_getstats(struct uct *u, struct board *b, coord_t c, bool keep_looking) r += snprintf(r, end - r, "%d %d %d %d", u->played_own, root->u.playouts, u->threads, keep_looking); int min_playouts = root->u.playouts / 100; - // Give a large weight to pass or resign, but still allow other moves. + /* Give a large weight to pass or resign, but still allow other moves. + * Only root->u.playouts will be used (majority vote) but send amaf + * stats too for consistency. */ if (is_pass(c) || is_resign(c)) - r += snprintf(r, end - r, "\n%s %d %.1f", coord2sstr(c, b), root->u.playouts, - (float)is_pass(c)); + r += snprintf(r, end - r, "\n%s %d %.1f %d %.1f", coord2sstr(c, b), + root->u.playouts, 0.0, root->amaf.playouts, 0.0); /* We rely on the fact that root->children is set only * after all children are created. */ @@ -940,7 +943,7 @@ uct_getstats(struct uct *u, struct board *b, coord_t c, bool keep_looking) if (is_pass(ni->coord)) continue; struct node_stats *ns = &u->stats[ni->coord]; - ns->last_sent_own.playouts = 0; + ns->last_sent_own.u.playouts = ns->last_sent_own.amaf.playouts = 0; ns->node = ni; if (ni->u.playouts <= min_playouts || ni->hints & TREE_HINT_INVALID) continue; @@ -948,14 +951,17 @@ uct_getstats(struct uct *u, struct board *b, coord_t c, bool keep_looking) char *coord = coord2sstr(ni->coord, b); /* We return the values as stored in the tree, so from black's view. * own = total_in_tree - added_from_others */ - struct move_stats s = ni->u; - struct move_stats others = ns->added_from_others; - if (s.playouts - others.playouts <= min_playouts) + struct move_stats2 s = { .u = ni->u, .amaf = ni->amaf }; + struct move_stats2 others = ns->added_from_others; + if (s.u.playouts - others.u.playouts <= min_playouts) continue; - if (others.playouts) - stats_rm_result(&s, others.value, others.playouts); + if (others.u.playouts) + stats_rm_result(&s.u, others.u.value, others.u.playouts); + if (others.amaf.playouts) + stats_rm_result(&s.amaf, others.amaf.value, others.amaf.playouts); - r += snprintf(r, end - r, "\n%s %d %.7f", coord, s.playouts, s.value); + r += snprintf(r, end - r, "\n%s %d %.7f %d %.7f", coord, + s.u.playouts, s.u.value, s.amaf.playouts, s.amaf.value); ns->last_sent_own = s; /* If the master discards these values because this slave * is out of sync, u->stats will be reset anyway. */ @@ -975,10 +981,11 @@ find_top_nodes(struct uct *u) } } -/* genmoves returns a line "=id total_playouts threads keep_looking[ reserved]" - * then a list of lines "coord playouts value" terminated by \n\n. - * It also takes as input a list of lines "coord playouts value" to get stats - * of other slaves, except for the first call at a given move number. */ +/* genmoves gets in the args parameter + * "played_games main_time byoyomi_time byoyomi_periods byoyomi_stones" + * and reads a list of lines "coord playouts value amaf_playouts amaf_value" + * to get stats of other slaves, except for the first call at a given move number. + * See uct_getstats() for the description of the return value. */ static char * uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone color, char *args, bool pass_all_alive) @@ -1004,8 +1011,10 @@ uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone char line[128]; while (fgets(line, sizeof(line), stdin) && *line != '\n') { char move[64]; - struct move_stats s; - if (sscanf(line, "%63s %d %f", move, &s.playouts, &s.value) != 3) + struct move_stats2 s; + if (sscanf(line, "%63s %d %f %d %f", move, + &s.u.playouts, &s.u.value, + &s.amaf.playouts, &s.amaf.value) != 5) return NULL; coord_t *c_ = str2coord(move, board_size(b)); coord_t c = *c_; @@ -1018,7 +1027,7 @@ uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone * but this should be rare so it is not worth creating * the node here. */ if (!ns->node) { - if (DEBUGL(0)) + if (DEBUGL(2)) fprintf(stderr, "can't find node %s %d\n", move, c); continue; } @@ -1027,21 +1036,29 @@ uct_genmoves(struct engine *e, struct board *b, struct time_info *ti, enum stone * but if it sends one it includes the contributions from * all slaves including ours (last_sent_own): * received_others = received_total - last_sent_own */ - if (ns->last_sent_own.playouts) - stats_rm_result(&s, ns->last_sent_own.value, - ns->last_sent_own.playouts); + if (ns->last_sent_own.u.playouts) + stats_rm_result(&s.u, ns->last_sent_own.u.value, + ns->last_sent_own.u.playouts); + if (ns->last_sent_own.amaf.playouts) + stats_rm_result(&s.amaf, ns->last_sent_own.amaf.value, + ns->last_sent_own.amaf.playouts); /* others_delta = received_others - added_from_others */ - struct move_stats delta = s; - if (ns->added_from_others.playouts) - stats_rm_result(&delta, ns->added_from_others.value, - ns->added_from_others.playouts); + struct move_stats2 delta = s; + if (ns->added_from_others.u.playouts) + stats_rm_result(&delta.u, ns->added_from_others.u.value, + ns->added_from_others.u.playouts); /* delta may be <= 0 if some slaves stopped sending this move * because it became below a playouts threshold. In this case * we just keep the old stats in our tree. */ - if (delta.playouts <= 0) continue; + if (delta.u.playouts <= 0) continue; - stats_add_result(&ns->node->u, delta.value, delta.playouts); + if (ns->added_from_others.amaf.playouts) + stats_rm_result(&delta.amaf, ns->added_from_others.amaf.value, + ns->added_from_others.amaf.playouts); + + stats_add_result(&ns->node->u, delta.u.value, delta.u.playouts); + stats_add_result(&ns->node->amaf, delta.amaf.value, delta.amaf.playouts); ns->added_from_others = s; } -- 2.11.4.GIT