pachi_py/pachi/distributed/distributed.h (53 lines of code) (raw):

#ifndef PACHI_DISTRIBUTED_DISTRIBUTED_H #define PACHI_DISTRIBUTED_DISTRIBUTED_H #include <limits.h> #include "engine.h" #include "stats.h" /* A coord path encodes coordinates from root child to a given node: * A1->B2->C3 is encoded as coord(A1)<<18 + coord(B2)<<9 + coord(C3) * for 19x19. In this version the table is not a transposition table * so A1->B2->C3 and C3->B2->A1 are different. * The depth is limited to 7 for 19x19 (9 for 9x9) to fit in 64 bits. * path_t is signed to include pass and resign. */ typedef int64_t path_t; #define PRIpath PRIx64 #define PATH_T_MAX INT64_MAX #define hash_mask(bits) ((1<<(bits))-1) /* parent_path() must never be used if path might be pass or resign. */ #define parent_path(path, board) ((path) >> board_bits2(board)) #define leaf_coord(path, board) ((path) & hash_mask(board_bits2(board))) #define append_child(path, c, board) (((path) << board_bits2(board)) | (c)) #define max_parent_path(u, b) (((path_t)1) << (((u)->shared_levels - 1) * board_bits2(b))) /* For debugging only */ struct hash_counts { long lookups; long collisions; long inserts; long occupied; }; /* Find a hash table entry given its coord path from root. * Set found to false if the entry is empty. * Abort if the table gets too full (should never happen). * We use double hashing and coord_path = 0 for unused entries. */ #define find_hash(hash, table, hash_bits, path, found, counts) \ do { \ if (DEBUG_MODE) counts.lookups++; \ int mask = hash_mask(hash_bits); \ int delta = (int)((path) >> (hash_bits)) | 1; \ hash = ((int)(path) ^ delta ^ (delta >> (hash_bits))) & mask; \ path_t cp = (table)[hash].coord_path; \ found = (cp == path); \ if (found | !cp) break; \ int tries = 1 << ((hash_bits)-2); \ do { \ if (DEBUG_MODE) counts.collisions++; \ hash = (hash + delta) & mask; \ cp = (table)[hash].coord_path; \ found = (cp == path); \ if (found | !cp) break; \ } while (--tries); \ assert(tries); \ } while (0) /* Stats exchanged between master and slave. They are always * incremental values to be added to what was last sent. */ struct incr_stats { path_t coord_path; struct move_stats incr; }; /* A slave machine updates at most 7 (19x19) or 9 (9x9) nodes for each * update of the root node. If we have at most 20 threads at 1500 * games/s each, a slave machine can do at most 30K games/s. */ /* At 30K games/s a slave can output 270K nodes/s or 4.2 MB/s. The master * with a 100 MB/s network can thus support at most 24 slaves. */ #define DEFAULT_MAX_SLAVES 24 /* In a 30s move at 270K nodes/s a slave can send and receive at most * 8.1M nodes so at worst 23 bits are needed for the hash table in the * slave and for the per-slave hash table in the master. However the * same nodes are often sent so in practice 21 bits are sufficient. * Larger hash tables are not desirable because it would take too much * time to clear them at each move in the master. For the default * shared_levels=1, 18 bits are enough. */ #define DEFAULT_STATS_HBITS 18 /* If we select a cycle of at most 40ms, a slave machine can update at * most 10K different nodes per cycle. In practice the updates are * biased so we update much fewer nodes. As shorter cyle is preferable * because the stats are more fresh. The cycle time does not affect * the number of slaves and the hash table size. */ #define DEFAULT_SHARED_NODES 10240 /* Maximum game length. Power of 10 jut to ease debugging. */ #define DIST_GAMELEN 1000 #define force_reply(id) ((id) + DIST_GAMELEN) #define prevent_reply(id) ((id) % DIST_GAMELEN) #define move_number(id) ((id) % DIST_GAMELEN) #define reply_disabled(id) ((id) < DIST_GAMELEN) char *path2sstr(path_t path, struct board *b); struct engine *engine_distributed_init(char *arg, struct board *b); #endif