我目前正在编写一个国际象棋引擎,最近对它进行了分析。我很惊讶地发现第二昂贵的操作(除了搜索函数本身)是我的移动生成对象的创建,几乎低于我的评估函数。我对编程还比较陌生,所以很可能有更好的方法来完成我目前正在做的事情。
就目前而言,移动生成对象在搜索函数中的移动循环之前被初始化,并且位板是从我的位板对象复制过来的(与创建对象本身的 const 相比,它没有任何成本)。
这是 moveGeneration 对象的头文件,它包含一个移动对象数组(创建它也非常昂贵)为了便于查看,我还省略了函数,如果需要我可以发布它们虽然。
class MoveGen
{
public:
MoveGen();
//array of move objects
Move moveAr[256];
bool isWhite;
//number of moves generated this node
int moveCount;
//bitboards
U64 FullTiles;
U64 EmptyTiles;
U64 BBWhitePieces;
U64 BBWhitePawns;
U64 BBWhiteRooks;
U64 BBWhiteKnights;
U64 BBWhiteBishops;
U64 BBWhiteQueens;
U64 BBWhiteKing;
U64 BBBlackPieces;
U64 BBBlackPawns;
U64 BBBlackRooks;
U64 BBBlackKnights;
U64 BBBlackBishops;
U64 BBBlackQueens;
U64 BBBlackKing;
};
为了完整起见,这里是 Move 对象信息:
class Move
{
public:
Move();
U8 from;
U8 to;
char piece;
char captured;
int score;
U8 flag;
bool tried;
};
Move::Move(){
tried = false;
}
有什么明显的方法可以加快移动生成对象的创建速度吗?我愿意完全重新做任何事情,因为这会极大地消耗我的程序速度。
我考虑过删除 MoveGen 类中的本地位板,并将位板对象的 const & 传递给 MoveGen 函数。虽然我认为主要的减速来自对象数组的创建。 MoveGen::MoveGen 在我的分析中包含 23.18% 的样本,而 Move::Move 有 11.31%,与除搜索本身以外的所有内容相比都非常高。
如果这个问题过于笼统,我深表歉意,如果是这样的话,我会删除它。任何关于更有效的方法来做到这一点的建议将不胜感激!
最佳答案
it contains an array of move objects(which the creation of is pretty expensive as well)
仅基于此,在进入递归函数之前分配您需要的任意数量的 MoveGen
对象符合您的最佳利益。这意味着您有两个选择,从堆中分配,或在函数调用之前在堆栈中分配。预先分配和传递指针比在循环中重复创建大对象要快得多。
正如一些评论者所指出的,高百分比是因为您重复构造 MoveGen
对象,每次都需要构造 256 个 Move
对象,并且您的 Move
对象是昂贵的。这是因为每个具有非平凡构造函数(基本上是用户定义的)的类型都必须在分配后就地构造。在您的案例中的每个递归调用中,在分配包含类时分配类中的字段。如果您不自己为非平凡类型执行此操作,编译器将为您执行此操作以确保正确初始化您的对象,这就是您的代码中发生的事情。
通常,您总是希望尽可能多地进行预分配,尤其是在大型和/或昂贵的对象的情况下。
关于c++ - 在递归函数中更有效地创建对象和对象数组? C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45766042/