xr_a_y wrote: ↑Mon Nov 11, 2019 8:31 am
I put some timers inside Minic and I am totally amazed by your 36 cycles SEE !
Here some figures
Code: Select all
cycle % of total calls cycle/call
See 3738155258 16.3821% 4307603 867
Apply 3676719096 16.1129% 17811023 206
Eval 7667154230 33.6006% 3041458 2520
Attack 6080342832 26.6465% 297610485 20
Generate 1852695910 8.11926% 2598478 712
So that my SEE seems to be more around 800 cycles per call.
What are you profiling with only 36 cycles ? only a few specific lines of SEE ?
Maybe something is that I accumulate the timer and count one call for SEE only if it is a "long" one, one that needs to go through the attacker list.
You compute attackers from scratch at every iteration. This is slowing you down:
Code: Select all
bool threatsFound = getAttackers(p2, to, stmAttackers, ~p2.c);
You should only compute it once, and merely refresh for slider attacks along the segment through which a piece was moved (*). Or, even better in my experience, not even bother with see through attacks, and just compute once the attackers, and consume that bitboard in the loop.
Also, you should have some precomputed bitboard of squares attacked by the enemy after playing moves. This really saves time, for example, you can avoid generating king walking into check moves (less to filter afterwards), and in SEE you can know immediately whether or not you need to run the getAttackers() calculation and enter the loop.
But before optimising SEE(), make sure you've really thought things through with the search and move sorting, such that you do not compute too many SEE's (ie. think "do less", before "do faster").
(*) you can even skip that if the moved piece is a knight (another micro-optimisation)
PS: Your "Apply" is very slow. Perhaps you should start with that instead. Again, it could be that the apply is slow, or that you just do too many applies that could be avoided.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.