if statement and calculation faster than constant
Posted: Fri Apr 10, 2020 1:41 am
A few weeks ago there was a discussion about someone pushing to replace large table-arrays with a calculation in Stockfish where possible, because it was deemed faster. I've been noticing this myself several times in my optimization rounds for Rustic's move generator, make, and unmake function.
In make_move, the en passant square needs to be set. At first, I did it like this:
This works fine, but the profiler flags the "let ep_square" line to be expensive.
So, I thought to be smart, and replace this with a lookup-array:
So, if a white pawn moves to square 24, it'll find 16 as the EP-square.
This gets rid of the if-statement, the calculation "to - 8", and even the entire variable "ep_square".
The result is negative; perft 7 is consistently a second slower (+/- 0.2) with the lookup-array than it is with the the original code. The speed loss is about 1% or so; just a fraction, but consistently measurable. I've noticed the same in square_attacked(), where actually calculating the pawn attack on the fly is faster than getting it from mg.get_pawn_attacks(color, square) which is a function that just copies the bitboard from a lookup table. (Entirely replacing the lookup table with a calculation is negative though; strangely enough, the calculation vs. lookup is only beneficial in square_attacked(); not everywhere else as well.)
If the Stockfish developers are finding the same thing, that replacing a lookup-table with a calculation that then speeds up Stockfish by 1% for each replacement, I can imagine them replacing the tables.
(However... In my case I think the original code is MUCH cleaner and less bulky than the lookup table; it's only when the calculation is hard to understand that I think the lookup table is clearly better, even IF it's 1% slower.)
In make_move, the en passant square needs to be set. At first, I did it like this:
Code: Select all
// A double-step is the only move that sets the ep-square.
if double_step {
let ep_square = if us == WHITE { to - 8 } else { to + 8 };
board.set_ep_square(ep_square);
}
So, I thought to be smart, and replace this with a lookup-array:
Code: Select all
const DOUBLE_STEP_SQUARE_MAP: [u8; NR_OF_SQUARES as usize] = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
16, 17, 18, 19, 20, 21, 22, 23,
40, 41, 42, 43, 44, 45, 46, 47,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
];
This gets rid of the if-statement, the calculation "to - 8", and even the entire variable "ep_square".
The result is negative; perft 7 is consistently a second slower (+/- 0.2) with the lookup-array than it is with the the original code. The speed loss is about 1% or so; just a fraction, but consistently measurable. I've noticed the same in square_attacked(), where actually calculating the pawn attack on the fly is faster than getting it from mg.get_pawn_attacks(color, square) which is a function that just copies the bitboard from a lookup table. (Entirely replacing the lookup table with a calculation is negative though; strangely enough, the calculation vs. lookup is only beneficial in square_attacked(); not everywhere else as well.)
If the Stockfish developers are finding the same thing, that replacing a lookup-table with a calculation that then speeds up Stockfish by 1% for each replacement, I can imagine them replacing the tables.
(However... In my case I think the original code is MUCH cleaner and less bulky than the lookup table; it's only when the calculation is hard to understand that I think the lookup table is clearly better, even IF it's 1% slower.)