6 /* Move statistics; we track how good value each move has. */
7 /* These operations are supposed to be atomic - reasonably
8 * safe to perform by multiple threads at once on the same stats.
9 * What this means in practice is that perhaps the value will get
10 * slightly wrong, but not drastically corrupted. */
13 int playouts
; // # of playouts
14 floating_t value
; // BLACK wins/playouts
17 /* Add a result to the stats. */
18 static void stats_add_result(struct move_stats
*s
, floating_t result
, int playouts
);
20 /* Remove a result from the stats. */
21 static void stats_rm_result(struct move_stats
*s
, floating_t result
, int playouts
);
23 /* Merge two stats together. THIS IS NOT ATOMIC! */
24 static void stats_merge(struct move_stats
*dest
, struct move_stats
*src
);
26 /* Reverse stats parity. */
27 static void stats_reverse_parity(struct move_stats
*s
);
30 /* We actually do the atomicity in a pretty hackish way - we simply
31 * rely on the fact that int,floating_t operations should be atomic with
32 * reasonable compilers (gcc) on reasonable architectures (i386,
34 /* There is a write order dependency - when we bump the playouts,
35 * our value must be already correct, otherwise the node will receive
36 * invalid evaluation if that's made in parallel, esp. when
37 * current s->playouts is zero. */
40 stats_add_result(struct move_stats
*s
, floating_t result
, int playouts
)
42 int s_playouts
= s
->playouts
;
43 floating_t s_value
= s
->value
;
44 /* Force the load, another thread can work on the
45 * values in parallel. */
46 __sync_synchronize(); /* full memory barrier */
48 s_playouts
+= playouts
;
49 s_value
+= (result
- s_value
) * playouts
/ s_playouts
;
51 /* We rely on the fact that these two assignments are atomic. */
53 __sync_synchronize(); /* full memory barrier */
54 s
->playouts
= s_playouts
;
58 stats_rm_result(struct move_stats
*s
, floating_t result
, int playouts
)
60 if (s
->playouts
> playouts
) {
61 int s_playouts
= s
->playouts
;
62 floating_t s_value
= s
->value
;
63 /* Force the load, another thread can work on the
64 * values in parallel. */
65 __sync_synchronize(); /* full memory barrier */
67 s_playouts
-= playouts
;
68 s_value
+= (s_value
- result
) * playouts
/ s_playouts
;
70 /* We rely on the fact that these two assignments are atomic. */
72 __sync_synchronize(); /* full memory barrier */
73 s
->playouts
= s_playouts
;
76 /* We don't touch the value, since in parallel, another
77 * thread can be adding a result, thus raising the
78 * playouts count after we zero the value. Instead,
79 * leaving the value as is with zero playouts should
80 * not break anything. */
86 stats_merge(struct move_stats
*dest
, struct move_stats
*src
)
88 /* In a sense, this is non-atomic version of stats_add_result(). */
90 dest
->playouts
+= src
->playouts
;
91 dest
->value
+= (src
->value
- dest
->value
) * src
->playouts
/ dest
->playouts
;
96 stats_reverse_parity(struct move_stats
*s
)
98 s
->value
= 1 - s
->value
;