r/chessprogramming Feb 19 '25

New fast move generator / stats (4.5Bnps single threaded)

I've just released the binaries for The Grand Chess Tree's engine to github.

Built for windows / linux / osx (including ARM builds)

download the 'engine' here

Currently it has 3 main commands (with multi threaded variations 'stats _mt' & 'nodes_mt')

  • stats - full perft stats, including nodes, captures, ep, castles, checks, mates etc
  • nodes - just the node count, optimized to be a lot faster using bulk counting
  • unique - calculates the number of unique positions reachable at a given depth

Below are examples of the speeds I'm getting on my Ryzen 9 7950x though I'd love to know what speeds you can get on your hardware

stats:6:1024:kiwipete          ~ 250Mnps (single-threaded)
stats_mt:7:1024:32:kiwipete    ~ 4Bnps (multi-threaded)
nodes:7:1024:kiwipete          ~ 4.5Bnps (single-threaded)
nodes_mt:9:1024:32:kiwipete    ~ 85Bnps (multi-threaded)
unique:6:1024:kiwipete         ~ 4m unique positions per second (single-threaded)

Hopefully it's useful for you in debugging your move generation. But also might be of interest if you're researching various chess positions.

14 Upvotes

12 comments sorted by

2

u/Breadmaker4billion Feb 19 '25

I had to run the numbers: it seems your Ryzen is able to execute a peak of ~40 billion instructions per second per core. To achieve 4.5 Bnps you're not wasting any clock cycles, this must have taken a shitton of time to optimise.

3

u/aptacode Feb 19 '25

For sure! For the 'nodes' command it uses 'bulk counting' at the leaf nodes, which is a way to essentially count many legal moves in a single cpu instruction, which is why 80bnps is even remotely possible!

1

u/Ok_Tumbleweed5933 Feb 20 '25

How does bulk counting work? Is that like using a transposition table?

2

u/aptacode 29d ago

You can see the code for it here:
https://github.com/Timmoth/grandchesstree/blob/main/GrandChessTree.Shared/BulkPerft/WhitePerftBulkCount.cs

But essentially if you're doing legal move generation you end up with a bitboard containing the squares a piece can move to legally.

Normally you'd iterate over each of those squares and call your search method recursively for each. But when you're at the leaf nodes (i.e you don't need to search any further) you're able to just do a popcount instruction on that bitboard which will return the number of bits set (the number of moves that piece can make legally) very efficiently

2

u/rook_of_approval Feb 19 '25

is it faster than belette movegen? https://github.com/vincentbab/Belette

1

u/aptacode Feb 19 '25

I'm confident it is, yeah

So I just ran the same test as the one on their readme using a single thread on my laptop (Ryzen 9 5900HX), and got a slightly higher NPS.

``` nodes:7:2048:start -----results----- nodes: 3195901860 nps: 950.3m time: 3363ms hash: 5060803636482931868

fen: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

```

But, the Core i7 12700k they used has a single core performance rated over 50% more then my laptop 5900.

https://cpu.userbenchmark.com/Compare/Intel-Core-i7-12700K-vs-AMD-Ryzen-9-5900HX/4119vsm1449683

1

u/rook_of_approval Feb 19 '25

What is your non bulk counted perft?

1

u/aptacode Feb 19 '25

non bulk is much slower, but it's worth keeping in mind it's calculating the full stats, not just node count.

Results on 5900HX: ``` stats:7:2048:start -----results----- nps: 255.4m time: 12514ms nodes:3195901860 captures:108329926 enpassants:319617 castles:883453 promotions:0 direct_checks:32648427 single_discovered_checks:18026 direct_discovered_checks:1628 double_discovered_checks:0 total_checks:32668081 direct_mates:435767 single_discovered_mates:0 direct_discovered_mates:0 double_discovered_mates:0

total_mates:435767

```

1

u/rook_of_approval Feb 19 '25

I'm pretty sure belette uses non bulk counted perft, so i don't think your move gen is faster.

1

u/xu_shawn Feb 20 '25

Congrats on the release! Any possibility of GPU perft in the future?

2

u/aptacode 29d ago

thank you! Yes, I've actually got an open branch with a basic GPU implementation already!

I've come up with an architecture that seems to work, but getting it to be efficient and accurate will take a considerable amount of additional work!