[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: computer-go: random move execution speed
Thanks for the benchmark algorithm and code. I implemented it also and have
already found several places which need improvement. For example, I had cell
objects which represented each position on the board. I found that creating,
copying or destroying each of these 361 objects, on 19x19 board, adds an
enormous about of unnecessary overhead. I replaced the objects with a simple
integer array.
Our code looks very similar. Our point class is probably identicial, and the
board class has the same methods. The only key difference is I don't have an
Undo-method, so instead I keep a copy of the board object after each move on
the stack - this means I don't account undos as moves. I also haven't
factored in the random number generator (yet).
Here are my disappointing results:
Board::TimingTests()
---> Playing Random Game
***> 1600000 Moves
***> 191.453 seconds.
***> 8357 moves per second.
***> 1564822 legal moves were made.
I look forward to profiling the rest of my code to improve it. Seems, I have
a ways to go to get up to par with the rest of you.
Here's my implementation of the benchmark algorithm:
void Board::TimingTest()
{
const int NUM_GAMES = 5000;
const int NUM_MOVES = 320;
const int N = BOARDSIZE * BOARDSIZE;
clock_t start, finish;
double duration;
const int totalNumMoves = NUM_GAMES*NUM_MOVES;
int numLegalMoves = 0;
std::cout << "Board::TimingTests()" << std:: endl;
std::cout << "---> Playing Random Game" << std::endl;
std::cout << "***> " << totalNumMoves << " Move" << std::endl <<
std::flush;
Point p;
Board back[NUM_MOVES];
start = clock();
srand((unsigned)time( NULL ) );
// srand(0);
for (int g = 0; g < NUM_GAMES; g++)
{
Board b;
b.Init();
for (int n = 0; n < NUM_MOVES; n++)
{
// Keep generating random points until we find an empty point.
do
{
int r = rand() % N;
p.set(r % BOARDSIZE, r / BOARDSIZE); // create point from column and row
}
while
(!b.isEmpty(p));
// Try to execute one move at an empty point. May not be legal
// due to suicide or board repetition.
if (b.DoMove(p,true))
{
numLegalMoves++;
b.Swap();
}
back[n] = b; // keep a copy
}
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
std::cout << "***> " << duration << " seconds.\n";
std::cout << "***> " << ((long) (totalNumMoves/duration)) << " moves per
second.\n";
// std::cout << "***> " << duration << " seconds per move.\n";
std::cout << "***> " << numLegalMoves << " legal moves were made.\n";
std::cout << std::flush;
}
----- Original Message -----
From: "Anders Kierulf" <anders@xxxxxxxxxxxxxxxxx>
To: <computer-go@xxxxxxxxxxxxxxxxx>
Sent: Wednesday, January 03, 2001 10:57 AM
Subject: computer-go: random move execution speed
> > After reviewing my random game test algorithm, I must state that is
> > shouldn't be used as a benchmark (my mistake). It's main purpose was to
> find
> > bugs. It works by generating completely random moves and attempting to
> play
> > them. An illegal move is turned into pass (12% of all moves). This,
> > unfortunately, inflates the performance figures. So that we can compare
> > apples to apples, can anyone propose a benchmark algorithm?
> >
> > Phil
>
> Okay, here's a proposed C++ benchmark algorithm for "random move execution
> speed". It's not perfect, but at least it should allow better
> apples-to-apples comparison. It plays through a number of random games,
and
> computes the time for combined playing and taking back moves. The random
> moves are played on any empty point, and moves that turn out to be illegal
> due to suicide or ko (a bit less than 2% of the moves) are also counted.
The
> time spent generating random moves is factored out, as it may be a
> significant part of the total time.
>
> Point RandomEmptyPoint(const GoBoard& board)
> {
> const int S = board.Size();
> const int N = S*S;
>
> // Keep generating random points until we find an empty point.
> while (true)
> { int r = Random() % N;
> Point p = Pt(r % S + 1, r / S + 1); // create point from column and
> row
> if (board.IsEmpty(p))
> return p;
> }
> } // RandomEmptyPoint
>
> const int NUM_GAMES = 5000;
> const int NUM_MOVES = 320;
>
> void ExecuteRandomGames(GoBoard& board, int numRandom)
> {
> for (int g = 0; g < NUM_GAMES; g++)
> {
> int numLegalMoves = 0;
> for (int n = 0; n < NUM_MOVES; n++)
> {
> // Call random point generation several times to be able to
> // subtract out that overhead.
> Point p;
> for (int r = 0; r < numRandom; r++)
> p = RandomEmptyPoint(board);
>
> // Try to execute one move at an empty point. May not be legal
> // due to suicide or board repetition.
> if (board.ExecuteOneMove(p, board.ToPlay()))
> numLegalMoves++;
> }
> for (int i = 0; i < numLegalMoves; i++)
> board.UndoMove();
> }
> } // ExecuteRandomGames
>
> void CheckExecuteMovePerformance(GoBoard& board)
> {
> // Execute same code with random point generation first called once
> // and then called twice to determine overhead.
> clock_t startTime1 = clock();
> ExecuteRandomGames(board, 1);
> clock_t time1 = clock() - startTime1;
>
> clock_t startTime2 = clock();
> ExecuteRandomGames(board, 2);
> clock_t time2 = clock() - startTime2;
>
> // Also counts calls to ExecuteOneMove that turned out to be illegal
> // due to suicide or ko.
> const int totalNumMoves = NUM_GAMES*NUM_MOVES;
>
> ofstream results("perf.txt", ios::app);
> results << "\nCount : " << totalNumMoves << " Execute+Undo";
> results << "\nTotal time: " << time1 << " mSec";
> results << "\nOverhead : " << time2-time1 << " mSec";
> results << "\nSpeed : " << CLOCKS_PER_SEC*double(totalNumMoves) /
> (2*time1-time2)
> << " Execute+Undo per second";
> results << "\n" << flush;
> } // CheckExecuteMovePerformance
>
> Please let me know if you have any questions. I've already gained some
> benefit from this exercise: I found out that keeping the bitboards
> up-to-date is no longer a significant cost of executing moves, so that is
no
> longer optional.
>
> On a Pentium II 450 MHz, an average run produces the following result for
> SmartGo:
>
> Count : 1600000 Execute+Undo
> Total time: 8282 mSec
> Overhead : 2249 mSec
> Speed : 265208 Execute+Undo per second
>
> And this is SmartGo's performance on a Pentium III 650 MHz:
>
> Count : 1600000 Execute+Undo
> Total time: 4907 mSec
> Overhead : 1512 mSec
> Speed : 471281 Execute+Undo per second
>
> Anders Kierulf
> www.smartgo.com
>
>