The idea is to use searched nodes instead of time, and to convert time to/from nosed searched in as few places as possible.
So, starting from current protocol:
http://wbec-ridderkerk.nl/html/UCIProtocol.html
I propose to add to the 'go' command definition the following line:
Code: Select all
* nps NODE_RATE
tells the engine to use nodes searched instead of wall time to account for elapsed time. Nodes searched will be converted to elapsed seconds through dividing by the given NODE_RATE (given in nodes per second)
Upon receiving 'nps NODE_RATE' parameter, the engine will account for elapsed time using searched nodes. As an example this is a possible implementation of the adjusted_now() function:
Code: Select all
TimePoint adjusted_now() {
if (!Search::Limits.nps)
return now(); // Falback on usual system wall time
return SearchTime + RootPos.nodes_searched() * 1000 / Search::Limits.nps; // In msec, SearchTime is the beginning of the search
}
Upon receiving 'bestmove', the GUI will account for the engine consumed time in the following way:
Code: Select all
elapsed_time = nodes * 1000 / NODE_RATE; // In milliseconds
LIMITATIONS
There are 2 main limitations with this scheme.
1) Engine speed should be known to be the same for both engines in advance, otherwise you have to deal with added complexity (sending 2 different nps to the 2 engines, measuring average speed over the last moves series, etc.)
2) Because nps is fixed while, in real engines, the nps increases toward endgame, this introduces an artifact that is equivalent to an altered time management. Namely it is like the time management gives less available time than what should be in standard case.
The second limitation can be mitigated measuring the average real nps
of the last series of moves and updating the sent nps, this introduces
a slow and stable drift, increasing nps toward the endgame,
compensating, at least partially, the altered time management
artifact.
I think the poster-child of this scheme is parameter tuning, where you
can almost safely assume that engines speed is the same.
GUI SIDE
Just for better clarifying, here is described a possible implementation from the tournament manager side, for instance cutechess.
Cutechess should add a new command line parameter:
-nodestime = x
where 'x' is the size of an internal FIFO called measured_nps, that is initialized with zero values.
cutechess enables the UCI 'nps NODE_RATE' extension and sends parameter 'nps' every time it sends 'go' command.
For the first 'x' moves nps is set to 0, and engines fallback to standard wall time. For the first 'x' moves cutechess gets the real nps after each engine's move and fills measured_nps FIFO.
Starting from move x+1, nps is set to the average of FIFO content, and this value is sent to both engines. The engines enable the adjusted time management and when, at the end of the search, bestmove is sent, cutechess uses the last sent 'nodes' info to calculate the accounted search time:
Code: Select all
elapsed_time = nodes * 1000 / NODE_RATE; // In milliseconds
An alternative to a FIFO, suggested by Michael Van den Berg, could be a simple low pass filter.
Code: Select all
new_nps=((n-1) * old_nps + measured_nps) / n