我正在用C从头开始制作基于位板的国际象棋引擎。移动生成过程特别耗时。目前,我的代码是这样的:
void generate_moves(Position *p, Colour side, Move *list) {
if(side == WHITE) {
Bitboard b1 = p->piece_bitboards[BLACK_KNIGHT];
Bitboard top_rank_mask = MASK_RANK[RANK8];
...
} else {
Bitboard b1 = p->piece_bitboards[WHITE_KNIGHT];
Bitboard top_rank_mask = MASK_RANK[RANK1];
...
}
}
和其他几百行代码,这些代码或者镜像为黑白,或者相同。但是,这似乎容易出错并且对我感到困惑。
因此将在每个fork处用三元运算符替换大型if-else,以提高可读性:
void generate_moves(Position *p, Colour side, Move *list) {
Bitboard b1 = p->piece_bitboards[side == WHITE ? WHITE_KNIGHT :
BLACK_KNIGHT];
Bitboard top_rank_mask = MASK_RANK[side == WHITE ? RANK8 : RANK1];
...
}
有类似的表现吗?所有三元运算符都取决于变量侧,变量侧只能假定常量值为WHITE或BLACK。
答案 0 :(得分:3)
除非禁用优化,否则任何中等质量的编译器都会将两个代码序列视为相同。
第二个序列名义上具有两个或多个测试,但是编译器应识别重复的表达式以及它不能从一个语句更改为下一个语句的事实,因为其中的操作数不受先前语句中任何内容的影响( s)(Bitboard b1 = p->piece_bitboards[side == WHITE ? WHITE_KNIGHT :
BLACK_KNIGHT];
。 (大概WHITE
是一个常数。)这种优化甚至对于中等质量的编译器也是期望的。
关于是选择if
序列中的两个块之一,还是选择三元操作数)是在编译时进行,除非编译器知道side
。由于side
是一个函数参数,因此编译器无法仅从函数的源代码中得知其值。如果编译器可以看到调用函数的位置,并且为side
传递的参数是常量表达式,或者可以由编译器推导,则可以知道其值。例如,如果调用代码包含两个序列,其中一个序列为generate_moves
调用WHITE
,而另一个序列为generate_moves
调用BLACK
,则编译器可能会生成generate_moves
的内联代码,其中每种情况下都对选择进行了优化。
执行此操作的情况不太清楚,并且受制于编译器和其他代码(未显示)的属性。
像您所确定的条件选择一样,这样的代码性能可能受其他因素的影响更大。