好的,伙计们:内存优化肯定不是我的事情,因为我目前正在开发一个大型,CPU和内存密集型项目,我想我需要你的帮助。
该项目是一个国际象棋引擎,实际问题在于(我猜)以下两种方法之一(上面的代码不是100%准确,但它或多或少):
树搜索(MiniMax) - 实际代码是具有不同添加内容的Alpha-Beta,但这个非常基本的示例更具说明性:
int Board::miniMax_(int ply)
{
if (ply == this->searchDepth) return this->eval();
int best = MINUS_INF;
vector<Move*> moves = this->possibleMoves();
FOREACH(Move*, move, moves)
{
HASHMAKE((*move),this);
int val = -this->miniMax_(ply+1);
UNHASHMAKE((*move),this);
if (val>best) {
if (ply==0) this->bestMove = (*move);
best = val;
}
}
return best;
}
移动世代 (如果你还没有玩过国际象棋编程和位板,下面的内容可能看起来几乎没有感觉;但你仍然可以得到这个想法记忆处理 - 好吧,希望......):
vector<Move*> Board::possibleMoves()
{
U64 ownPieces = this->piecesForColor(this->playing);
U64 occupied = ~(this->pieces[empty]);
vector<Move*> moves;
//-----------------------------------------
// "Normal" Piece Moves
//-----------------------------------------
const int from = (1+(this->playing))*3;
const int to = (1+(this->playing))*3+6;
for (int pieceType=from; pieceType<to; pieceType++)
{
U64 piece = this->pieces[pieceType];
for (; piece != 0; piece &= (piece - 1))
{
UINT pos = log2(piece & ~(piece-1));
U64 move;
switch (pieceType)
{
case bPawns: move = BPAWN_(pos,ownPieces,occupied); break;
case wPawns: move = WPAWN_(pos,ownPieces,occupied); break;
case bRooks:
case wRooks: move = ROOK_(pos,ownPieces,occupied); break;
case bKnights:
case wKnights: move = KNIGHT_(pos,ownPieces,occupied); break;
case bBishops:
case wBishops: move = BISHOP_(pos,ownPieces,occupied); break;
case bQueen:
case wQueen: move = QUEEN_(pos,ownPieces,occupied); break;
case bKing:
case wKing: move = KING_(pos,ownPieces,occupied); break;
default:break;
}
for (; move !=0; move &= (move-1))
{
moves += new Move(pos, log2(move&~(move-1)),this);
}
}
}
return moves;
}
Move类
//=======================================================
// Prototype
//=======================================================
class Move
{
public:
Move (string m, Board* b) : from(ALG2FROM(m)), to(ALG2TO(m)), pieceFrom(b->atPosition[from]), pieceTo(b->atPosition[to]) {}
Move (int f, int t, Board* b) : from(f), to(t), pieceFrom(b->atPosition[from]), pieceTo(b->atPosition[to]) {}
Move (int val) : value(val) {}
inline string notation();
inline string out();
//----------------------
int from, to;
int pieceFrom, pieceTo;
int value;
};
//=======================================================
// Inline Functions
//=======================================================
inline string Move::notation()
{
return SSTR(SPOS(this->from) << SPOS(this->to));
}
inline string Move::out()
{
return SSTR(this->notation() << " :: " << this->value);
}
显然,第一个函数是递归的并且被调用了数百万次,有一些预期的负载。事情是,一旦搜索达到第4,第5层或者其他什么,该应用程序已经占用了2GB。事情是,一旦它完成(搜索),内存仍然没有被释放 - 所以我认为这个表明存在问题。
那么,有什么想法吗?
如果您需要了解有关实施的任何其他信息,请告诉我。
提示:
FOREACH
只是矢量迭代器+=
来自Boost 答案 0 :(得分:3)
下面:
vector<Move*> moves = this->possibleMoves();
指针向量不会释放指针。您可以尝试使用std::unique_ptr<Move>
的矢量。
如果您不单独分配动作,您将获得更好的表现。使用内存池。在这方面,你可能根本不需要矢量。
除非Move
是多态类,否则我建议你根本不分配它。相反,在Set
上制作一堆Move
函数并声明您的possibleMoves
函数:
void Board::possibleMoves( vector<Move> & moves )
显然可以这样称呼:
vector<Move> moves;
possibleMoves( moves );
因此,这意味着当您添加移动而不是执行new
时,您可以执行以下操作:
moves.push_back( Move(pos, log2(move&~(move-1)),this) );
调用复制构造函数。如果你想避免额外的副本,那么为Move
创建一个空构造函数并制作上述'setter'函数:
moves.resize( moves.size()+1 );
Move & m = moves.back();
m.Set( pos, log2(move&~(move-1)),this );
我不能100%确定这是否会更快。无论如何,另一件事......如果您希望典型的电路板几乎总是少于50个可能的移动,那么您可以在possibleMoves
的开头执行此操作来提高性能:
moves.reserve(50)
这意味着矢量几乎不需要调整大小,从而使push_back
操作更快。
答案 1 :(得分:2)
我不确定在这种情况下std :: vector是最好的容器。
可能的移动次数是有限的,应该可以计算它。我不打算猜测你是怎么做的,但假设你可以做到(或者只是使用一个const大数字)......那么可能值得使用数组:
Move *moves = new Move[maxMoves];
FillPossibleMoves(moves, maxMoves)
// do stuff
delete [] moves;
然后您可以将可能的移动功能更新为:
int Board::FillPossibleMoves(Move *moves, int maxMoves)
{
int i = 0;
...
moves[i].Something = Whatever;
i++;
...
return i;
}
这样你只需要分配一次内存,并在完成后清理它。
如果您同意:Using arrays or std::vectors in C++, what's the performance gap?表示要避免使用动态数组,那么:
std::vector<Move> moves(maxMoves);
// if you use a const, then you can keep declaration above as a member
// and call moves.clear() here instead
int movesAdded = board->FillPossibleMoves(moves);
// do stuff
int Board::FillPossibleMoves(std::vector<Move> &moves)
{
int i = 0;
...
moves[i].Something = Whatever;
i++;
...
return i;
}
答案 2 :(得分:0)
没有任何析构函数实现
为什么你认为应该释放内存。你整整都在调用新的东西,但你永远不会删除你创建的Move
。
另外,向我们展示Move
类