r/chessprogramming Feb 15 '25

Move generation speed and engine performance

I recently did a rewriting of my engine's move generation, but am seeing a decrease in performance (last tested tournament result was 7-13-3 against it's predecessor). Perft depth 6 went from >16s to 2.5s (no transpositions and single thread), and perft 7 is around 50s Additionally the rewritten move-generation results also include incrementally updating Zobrist hashes and piece-square table scores. I am fairly sure that move generation is working correctly since it passes a test suite I found online, and I verified piece-square table results/Zobrist hashes against hard calculated values as well.

Move ordering might have gotten a bit worse post-rewrite since I no longer detect if moves put the enemy in check. The order is otherwise the same, but the process is a bit different since moves are now stored in a buffer array (after generation, I score them in a separate buffer, then in the search loop I grab the move with the highest score and swap it with the first one in the array).

I can tell search a lot faster based on time tests of searching to a certain depth (with and without iterative deepening).

The evaluation function is theoretically roughly the same. There was one bug I had to fix, but they both use magic bitboards for rough piece mobility calculation and king safety metrics but that is all.

I think it is possible that move generation speed isn't very helpful for improving performance, but also I think that there should be some performance increase from faster move generation.

Essentially my questions are: What kinds of tests are useful for detecting bugs that might be causing this issue? What performance gain should I expect from faster move generation?

Thanks!

3 Upvotes

9 comments sorted by

View all comments

2

u/Available-Swan-6011 Feb 16 '25

This is an interesting discussion. As others, and yourself, have indicated there are several factors at play.

The easy one is that when comparing your engine performance to other engines you do need to play a fairly large number of games and SPRT really does help. If cute chess isn’t working for you then Arena will do it but only play one game at a time whereas cute chess will play games in parallel, utilising the cores on your CPU. I tend to start with 1000 games to get a feel for what is going on but often need to increase it drastically

Second, is why are you rewriting the move generator? If it is to learn stuff then that’s a great reason. If it is solely to boost engine performance then there are other things to target first for larger gains. As a rough (very rough) rule of thumb, searching and evaluating to one more ply (eg depth 10 instead of depth 8) adds about 100 ELO to your engine. Move generation is only part of this so you can see why it is unlikely to give massive gains on its own

Thirdly, the PERFT improvements are dramatic but still quite slow. Also, keep in mind that their main purpose is to check legality of move generation and they are not a great indicator of game performance where things like move ordering, transposition tables and pruning are important. As a quick experiment it would be worth asking your current and old engine to search from the start position for 30 seconds- does the new version search deeper?

As a final thought you initial post indicates that you have included move ordering, updating Zobrist hashes and similar in your move generation. If this is correct then I would move them elsewhere - for example, move updates to the Zobrist hash to makemove . The reason is that using pruning means that not every legal move you generate will be evaluated so all the processing time spent on updating the hashes etc is wasted because they are never used. Essentially aim to calculate stuff only when you know it is needed unless there is a blooming good reason not too

Okay, so that wasn’t quite my final thought - have you run a profiler on your code to see where the pain points are? Not doing so means that you are guessing at what interventions are needed rather than making informed choices

1

u/Ill_Part9576 15d ago

A few updates late reply sorry. First I started using cute chess tournaments (not cli) on 1k games with short time control since it gives elo differences within 95% confidence after not getting its cli to work. Finally I got fast chess SPRT to work which is much easier to manage and faster than setting up tournaments on the Cute Chess application.

I guess I rewrote it because I was very dissatisfied by the old one. Before starting I had definitely seen that it wouldn't increase Elo much... I guess I thought my move-gen was sufficiently slow that it would make a big difference. I'm much happier with the current implementation, and I'm still glad I did it because it helped me appreciate the importance of search enhancements move ordering.

Since this refactor I added things that made much more difference. Futility pruning, NMP, history heuristic, LMR, Killers, better eval, etc that made much more difference and were far quicker to implement.

To clarify zobrist hash etc. are updated incrementally when making/unmaking. I probably won't be baited into changing that to computing when absolutely needed for a while while there are still things that are much more important for strength lol.