Compare commits

..
Author SHA1 Message Date
Joost VandeVondeleandGitHub e904a2a188 Merge pull request #6497 from sesse/cluster-master-fmt
Merge cluster up to latest master
2026-01-02 09:41:20 +01:00
Steinar H. Gunderson 7ed6af120a Fix -Wunused-parameter warning.
When compiling the cluster branch but in non-MPI mode, we'd get some
warnings in cluster.h about unused ThreadPool parameters.

No functional change.
2026-01-02 09:29:38 +01:00
Steinar H. Gunderson d3a46e40b2 Update cluster copyright year.
No functional change.
2026-01-02 09:29:33 +01:00
Steinar H. Gunderson bd3fd8ba79 Extract bestMove and ponderMove after the PV.
Extracting ponderMove may have the unintentional effect of extending the
PV by one move (which should not be displayed), so we need to extract it
after we've extracted the PV. We still need to both before the MPI exchange,
though.

Patch by vondele, merely committed by me.
2026-01-02 09:24:56 +01:00
Steinar H. Gunderson 3d577337c2 Merge branch 'master' into cluster 2026-01-01 17:33:27 +01:00
Joost VandeVondele 28844fc697 Update of the year
Happy New Year!

closes https://github.com/official-stockfish/Stockfish/pull/6514

No functional change
2026-01-01 16:17:38 +01:00
mstemberaandJoost VandeVondele 8815d1ef02 Minor cleanup in full_threats.cpp
closes https://github.com/official-stockfish/Stockfish/pull/6509

No functional change
2026-01-01 16:13:44 +01:00
FauziAkramandJoost VandeVondele 5b9259e51f Replacing nested loops with a single range-based for loop
closes https://github.com/official-stockfish/Stockfish/pull/6503

No functional change
2026-01-01 16:12:35 +01:00
anematodeandJoost VandeVondele 593eeaf24c simplify find_nnz a bit
This code path is never taken for vector sizes >= 512, so we can simplify it.

closes https://github.com/official-stockfish/Stockfish/pull/6501

No functional change
2026-01-01 16:08:03 +01:00
ppigazziniandJoost VandeVondele 0317c6ccec build: rename WINE_PATH to RUN_PREFIX for wrapper execution
WINE_PATH started as a Wine-specific knob, but it’s now used more generally
as a command prefix to run the built engine under wrappers
like Intel SDE, qemu-user, etc.

- Add RUN_PREFIX as the supported “run wrapper/prefix” variable in Makefile
- Set WINE_PATH as a deprecated alias
- Update CI and scripts to use RUN_PREFIX

closes https://github.com/official-stockfish/Stockfish/pull/6500

No functional change
2026-01-01 16:06:25 +01:00
anematodeandJoost VandeVondele aeb3bf33a9 port get_changed_pieces to ARM NEON
passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 71968 W: 18833 L: 18489 D: 34646
Ptnml(0-2): 192, 7310, 20643, 7640, 199
https://tests.stockfishchess.org/tests/view/69509e5c572093c1986d7a0a

closes https://github.com/official-stockfish/Stockfish/pull/6512

No functional change
2026-01-01 16:04:43 +01:00
Michael ChalyandJoost VandeVondele ced9f69834 Adjust main history with every new root position
this patch dampens down main history to 3/4 of it value for all possible moves
at the start of ID loop, making it partially refresh with every new root
position.

Passed STC:
https://tests.stockfishchess.org/tests/view/694e33ff572093c1986d7234
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 115520 W: 30164 L: 29735 D: 55621
Ptnml(0-2): 395, 13192, 30192, 13551, 430

Passed LTC:
https://tests.stockfishchess.org/tests/view/6950cbe6572093c1986d816c
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 63672 W: 16480 L: 16114 D: 31078
Ptnml(0-2): 46, 6524, 18329, 6892, 45

closes https://github.com/official-stockfish/Stockfish/pull/6504

bench 2710946
2026-01-01 15:53:04 +01:00
Steinar H. GundersonandJoost VandeVondele 1453691496 Fix feature check
Use _POSIX_C_SOURCE to check for PTHREAD_MUTEX_ROBUST support. The latter is a enum, not a defined variable.

closes https://github.com/official-stockfish/Stockfish/pull/6510

No functional change
2026-01-01 15:52:01 +01:00
Joost VandeVondele e0fb783c30 Fix incorrect initialization
Fixes https://github.com/official-stockfish/Stockfish/issues/6505

Missing initialization seemingly resulting in side effects, as discussed in the issue.

Credit to Sopel for spotting the bug.

PR used as a testcase for CoPilot, doing the right thing https://github.com/official-stockfish/Stockfish/pull/6478#discussion_r2655467218

closes https://github.com/official-stockfish/Stockfish/pull/6511

No functional change
2026-01-01 15:51:09 +01:00
Timothy HerchenandDisservin 44d5467bbe Remove -Wstack-usage on (apple) clang
Clang pretends to be GCC, but is enraged by `-Wstack-usage`:

closes https://github.com/official-stockfish/Stockfish/pull/6499

No functional change
2025-12-28 14:59:26 +01:00
969285fa5d Shared pawn history
[Passed STC SMP](https://tests.stockfishchess.org/tests/view/694e506c572093c1986d7276):
```
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 14992 W: 3924 L: 3653 D: 7415
Ptnml(0-2): 20, 1547, 4090, 1820, 19
```

[Passed LTC SMP](https://tests.stockfishchess.org/tests/live_elo/694ead61572093c1986d7365):
```
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 41146 W: 10654 L: 10342 D: 20150
Ptnml(0-2): 17, 3999, 12225, 4319, 13
```

[Passed a sanity check STC SMP post-refactoring](https://tests.stockfishchess.org/tests/view/69503997572093c1986d763a):
```
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 46728 W: 12178 L: 11863 D: 22687
Ptnml(0-2): 82, 5093, 12685, 5436, 68
```

(The large gain of the first STC was probably a fluke, and this result
is more reasonable!)

After shared correction history, Viz suggested we try sharing other
histories, especially `pawnHistory`. As far as we're aware, sharing
history besides correction history (like Caissa does) is novel. The
implementation follows the same pattern as shared correction history –
the size of the history table is scaled with
`next_power_of_two(threadsInNumaNode)` and the entry is prefetched in
`do_move`.

A bit of refactoring was done to accommodate this new history. Note that
we prefetch `&history->pawn_entry(*this)[pc][to]` rather than
`&history->pawn_entry(*this)` because unlike the other entries, each
entry contains multiple cache lines.

closes https://github.com/official-stockfish/Stockfish/pull/6498

Bench: 2503391

Co-authored-by: Michael Chaly <Vizvezdenec@gmail.com>
2025-12-28 14:59:09 +01:00
Stefan GeschwentnerandDisservin 1780c1fd6e For expected ALL nodes scale up reduction with depth dependent factor.
Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 141120 W: 36860 L: 36390 D: 67870
Ptnml(0-2): 470, 16441, 36314, 16819, 516
https://tests.stockfishchess.org/tests/view/694978e93c8768ca45072763

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 66576 W: 17078 L: 16700 D: 32798
Ptnml(0-2): 45, 7093, 18628, 7483, 39
https://tests.stockfishchess.org/tests/view/694bb608572093c1986d6ba6

closes https://github.com/official-stockfish/Stockfish/pull/6496

Bench: 2503391
2025-12-28 14:55:55 +01:00
KazAppsandDisservin b2e60960b3 Fix nonPawnKey
Fix incorrect nonPawnKey update

Passed non-reg SMP STC:
```
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 139424 W: 35792 L: 35690 D: 67942
Ptnml(0-2): 197, 15783, 37665, 15855, 212
```
https://tests.stockfishchess.org/tests/view/694b7b7e572093c1986d6b0d

Passed non-reg SMP LTC:
```
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 88880 W: 22863 L: 22718 D: 43299
Ptnml(0-2): 16, 8947, 26401, 9028, 48
```
https://tests.stockfishchess.org/tests/view/694d2ceb572093c1986d6fc8

fixes https://github.com/official-stockfish/Stockfish/issues/6492

closes https://github.com/official-stockfish/Stockfish/pull/6494

Bench: 2475788
2025-12-28 14:55:47 +01:00
Daniel MonroeandDisservin 1047f844d1 Simplify doDeeperSearch
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 92096 W: 23888 L: 23728 D: 44480
Ptnml(0-2): 336, 10796, 23608, 10988, 320
https://tests.stockfishchess.org/tests/view/694b6b9d572093c1986d6ae0

Passed simplification LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 50064 W: 12789 L: 12598 D: 24677
Ptnml(0-2): 24, 5350, 14103, 5521, 34
https://tests.stockfishchess.org/tests/view/694d49aa572093c1986d7021

closes https://github.com/official-stockfish/Stockfish/pull/6493

Bench: 2494221
2025-12-28 14:53:12 +01:00
Joost VandeVondeleandDisservin 06819ad54c Update Top CPU Contributors
update to current

closes https://github.com/official-stockfish/Stockfish/pull/6491

No functional change
2025-12-28 14:51:56 +01:00
FauziAkramandDisservin 9d69577e19 Removing redundant parentheses
closes https://github.com/official-stockfish/Stockfish/pull/6490

No functional change
2025-12-28 14:50:36 +01:00
FauziAkramandDisservin cd3a837324 Refine reduction logic based on next-ply cutoff count
Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 38208 W: 10076 L: 9754 D: 18378
Ptnml(0-2): 139, 4390, 9742, 4676, 157
https://tests.stockfishchess.org/tests/view/6945bb6446f342e1ec211d93

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 64086 W: 16529 L: 16157 D: 31400
Ptnml(0-2): 34, 6808, 17990, 7174, 37
https://tests.stockfishchess.org/tests/view/69479d303c8768ca45072446

closes https://github.com/official-stockfish/Stockfish/pull/6489

Bench: 2442415
2025-12-28 14:49:19 +01:00
Steinar H. Gunderson bbdab9889a Unbreak MPI compilation.
3c2c7b01bd deleted the TTCache default constructor, but we need it
when MPI is active. This wasn't discovered during the merging,
and it is hard to put it properly into the commit history due to
rebase/rerere shortcomings. (This means that some of the merge
commits will not compile with MPI on, and this patch needs to be
added on top if bisecting.)
2025-12-26 23:20:44 +01:00
Steinar H. Gunderson 1b3830fad4 Merge commit 'c467fe5ba42545827d769c360aa404767308ac18' into cluster 2025-12-25 20:22:33 +01:00
Steinar H. Gunderson bf5f8c5543 Merge commit '5297ba0a1a1aa0a15332e0d64ce6b32952342cac' into cluster 2025-12-25 20:22:26 +01:00
Steinar H. Gunderson 0bcfc6c651 Merge commit 'abd835dcbc3a28481224f6253b00b7420d062513' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:19:41 +01:00
Steinar H. Gunderson a8f8f97eb1 Merge commit '8e5392d79a36aba5b997cf6fb590937e3e624e80' into cluster 2025-12-25 20:19:19 +01:00
Steinar H. Gunderson 8cc99e1314 Merge commit '69a01b88f35db2a5003d42116f573207ca5c275b' into cluster 2025-12-25 20:16:11 +01:00
Steinar H. Gunderson bfc8c24999 Merge commit 'bc9f08731f10896a306bcc34e30e5606087af79a' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:13:41 +01:00
Steinar H. Gunderson 8b776c3f04 Merge commit '75edbee01e6f8cb53a2555499192ccaddb883577' into cluster 2025-12-25 20:13:34 +01:00
Steinar H. Gunderson 725acd7449 Merge commit '315f8ba4bf7d846b35f984d5e6040c14a512d9b9' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:12:54 +01:00
Steinar H. Gunderson f5a171d82e Merge commit 'e2aa1255707628aa1d70f13c88211724e34fa38e' into cluster 2025-12-25 20:12:35 +01:00
Steinar H. Gunderson b5c4fd2b7a Merge commit '8c2d21f91a5840a67c36267e5043070ffad06860' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:08:43 +01:00
Steinar H. Gunderson e1f2a899b7 Merge commit 'd2d046c2a497b2d70debde07ccc414ca633d550b' into cluster 2025-12-25 20:07:02 +01:00
Steinar H. Gunderson f44f4f356c Merge commit '44efbaddea909e146c6c41afaf458da8c9e4b4e4' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:03:12 +01:00
Steinar H. Gunderson 20d5986235 Merge commit 'd7c04a942950f1fe3f655bf8b608e8ef21c07628' into cluster 2025-12-25 20:03:00 +01:00
Steinar H. Gunderson ab03e55a60 Merge commit 'd942e13398aa5de55224c7d81bfad6b0f5b9e488' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:02:14 +01:00
Steinar H. Gunderson 7c50916a9d Merge commit '291a429cdd54f5298b8dc3d19cd08c3a64de4d10' into cluster 2025-12-25 20:02:02 +01:00
Steinar H. Gunderson 60f5e6c211 Merge commit '43b2d65d7275b11fd47c7225f8a0d19afbab4cd1' into cluster
This is the last commit before there are more conflicts.
2025-12-25 20:01:02 +01:00
Steinar H. Gunderson 0eb376ce8d Merge commit 'f00d91f8ac72de8d201f8b50968bb66b1235dc9a' into cluster 2025-12-25 20:00:51 +01:00
Steinar H. Gunderson 6653af1549 Merge commit '738ac2a10025ca58198e3d2d7f0bc70d83c2cb7f' into cluster
This is the last commit before there are more conflicts.
2025-12-25 19:59:45 +01:00
Steinar H. Gunderson 4662a58e1d Merge commit '28c07fb456855c4e082571ed7dd723a3e71fdcff' into cluster 2025-12-25 19:59:29 +01:00
Steinar H. Gunderson 21d7128260 Merge commit 'ea71a088435d4f1e51433c0a321f2afdff7814b1' into cluster
This is the last commit before there are more conflicts.
2025-12-25 19:58:36 +01:00
Steinar H. Gunderson 75dd7d80ba Merge commit 'f414d490bc0e4013c211a066623f313883f49106' into cluster 2025-12-25 19:53:08 +01:00
Steinar H. Gunderson 8a6a8e8a42 Merge commit 'b822fdf2f2f00758c794cb61a25a044424d2bc0a' into cluster
This is the last commit before there are more conflicts.
2025-12-25 19:50:17 +01:00
Steinar H. Gunderson 03732aa685 Merge commit '57e06be71f0177a69843750a9f456462d02f23b9' into cluster 2025-12-25 17:57:11 +01:00
Steinar H. Gunderson f2643877eb Merge commit '70bb317afe870c8bc1979ef955f120e4d81f504e' into cluster
This is the last commit before there are more conflicts.
2025-12-25 17:33:17 +01:00
Steinar H. Gunderson a5ea0b2b2b Merge commit 'c2611efe5c317969b583a5ff81352439f905e722' into cluster 2025-12-25 17:32:39 +01:00
Steinar H. Gunderson 110fdc486a Merge commit '16fee2a7da25c6d0267930eb9677862cb1f009c7' into cluster
This is the last commit before there are more conflicts.
2025-12-25 17:30:06 +01:00
Steinar H. Gunderson b3580e942d Merge commit 'c15113554f53890d7944c00a70d0f2d8a78916fb' into cluster 2025-12-25 17:26:35 +01:00
Steinar H. Gunderson 5d3f71f72d Merge commit '2ce47573b4d3664dca4cbc4354c8c600540d16ad' into cluster
This is the last commit before there are more conflicts.
2025-12-25 17:22:27 +01:00
Steinar H. Gunderson 34a07438c0 Merge commit '3ac75cd27d914da29280163c9d391bbca414d766' into cluster 2025-12-25 17:10:14 +01:00
Steinar H. Gunderson fa1ab27e15 Merge commit 'aff1f67997cd2584ea7c82d967ac7bfd4cc77861' into cluster
This is the last commit before there are more conflicts.
2025-12-25 16:59:53 +01:00
Steinar H. Gunderson e8bc8639fc Merge commit '93869d5d0aab2f7121bdf227def3a942c9fcde17' into cluster 2025-12-25 16:59:18 +01:00
Steinar H. Gunderson a5c89f4592 Merge commit '60351b9df901ff5278f208a9cf3a40059ff54832' into cluster
This is the last commit before there are more conflicts.
2025-12-25 16:56:54 +01:00
Steinar H. Gunderson a449238954 Merge commit '703f17975bd9c29172a27f795ca6b5a7d0a32b25' into cluster 2025-12-25 16:55:54 +01:00
Steinar H. Gunderson 0043886170 Merge commit 'e57fba7fc9be461cbb97c063b269a1e231cdd284' into cluster
This is the last commit before there are more conflicts.
2025-12-25 16:54:13 +01:00
Steinar H. Gunderson 0814961843 Merge commit '6135a0e2f830a587d2ac7a332bb62188fa924aad' into cluster 2025-12-25 16:52:43 +01:00
Steinar H. Gunderson 1cffdc1ce4 Merge commit '7e72b37e4ce3351399bb0ac08686bd84cdc4fba9' into cluster 2025-12-25 16:03:31 +01:00
Steinar H. Gunderson 0c451731c6 Merge commit '98a7bb4436f04505a17f37befab0207252e97897' into cluster
This is the last commit before there are more conflicts.
2025-12-25 16:00:06 +01:00
Steinar H. Gunderson a20c6d296a Merge commit '362a77a3450335e1940020c080bf3b7b361c594a' into cluster 2025-12-25 15:53:03 +01:00
Steinar H. Gunderson d002e92294 Merge commit 'b209f14b1ee0cda8cbd7fa3a8349e65701d1869f' into cluster
This is the last commit before there are more conflicts.
2025-12-25 15:48:01 +01:00
Steinar H. Gunderson d83e63c163 Merge commit '5d3517c601c64d026824251784dd44f0cbf14873' into cluster 2025-12-25 15:30:32 +01:00
Steinar H. Gunderson d7cf6e22fd Merge commit '5752529cabb3270e055147709ff0847e4d59ec22' into cluster
This is the last commit before there are more conflicts.
2025-12-25 15:27:42 +01:00
Steinar H. Gunderson d1b19aad2f Merge commit '2cbc20e846e46da8bfc8e254a7703a0bfad3b850' into cluster 2025-12-25 15:26:23 +01:00
Steinar H. Gunderson 8f9e750782 Merge commit '3c379e55d9d92a5704632c6255e72892a4db9a2f' into cluster
This is the last commit before there are more conflicts.
2025-12-25 15:22:41 +01:00
Steinar H. Gunderson cefd309d92 Merge commit '25361e514bffb81284d4311601a9f7a4a7ced79b' into cluster 2025-12-25 15:22:02 +01:00
Steinar H. Gunderson 46264c6ed7 Merge commit '74a8fc060465a822f0c047f908d5fb07ebc6ad96' into cluster
This is the last commit before there are more conflicts.
2025-12-25 15:16:24 +01:00
Steinar H. Gunderson 5c9ed18b48 Merge commit 'b01fdb596a196f966549f7132c042ab67962fbbd' into cluster 2025-12-25 15:16:01 +01:00
Steinar H. Gunderson 7df644a00a Merge commit '44cddbd962c738678f407a7414efa5b93f0710d9' into cluster
This is the last commit before there are more conflicts.
2025-12-25 15:10:02 +01:00
Steinar H. Gunderson 876495975e Merge commit 'c8213ba0d047569141ed58f5eb86579d976b5614' into cluster
As part of this work, we needed to rename the namespace Stockfish::Cluster
to Stockfish::Distributed, as Cluster is now a type name.
2025-12-25 00:17:08 +01:00
Steinar H. Gunderson f358a30ea0 Merge commit '7e890fd048e22bfd213d46ec8eb88f7931f0315d' into cluster
This is the last commit before there are more conflicts.
2025-12-24 16:29:33 +01:00
Steinar H. Gunderson 5034cf6593 Merge commit '66ed4312f22a951aaa01bbb87b2d730656b8f2c1' into cluster 2025-12-24 16:28:21 +01:00
Steinar H. Gunderson 52d8388101 Merge commit 'e6c83beed12a6d3d17c69bea4bcf1a397bc60c86' into cluster
This is the last commit before there are more conflicts.
2025-12-24 16:27:16 +01:00
Steinar H. Gunderson 9770899bf7 Merge commit '5688b188cc8560e107815c83a7084220fddebdb9' into cluster 2025-12-24 16:26:53 +01:00
Steinar H. Gunderson 639294636e Merge commit '36eb9bc783d35842571d0d4313349b964892d9ca' into cluster
This is the last commit before there are more conflicts.
2025-12-24 16:26:07 +01:00
Steinar H. Gunderson 75138525c7 Merge commit '00a28ae325688346e63a452b2050bd1491085359' into cluster 2025-12-24 16:25:32 +01:00
Steinar H. Gunderson 784db2cbed Merge commit 'a2a7edf4c8fa145667135bf1bc7f4f67016f7608' into cluster
This is the last commit before there are more conflicts.
2025-12-24 16:19:29 +01:00
Steinar H. Gunderson bc81aa0911 Merge commit 'a169c78b6d3b082068deb49a39aaa1fd75464c7f' into cluster 2025-12-24 16:18:37 +01:00
Steinar H. Gunderson e56697e3f1 Merge commit 'b0287dcb1c436887075962b596cf2068d2ca9ba8' into cluster
This is the last commit before there are more conflicts.
2025-12-24 15:23:14 +01:00
Steinar H. Gunderson 125f4f3df0 Merge commit 'ed79745bb9e7207b604c62758ea45dd5c597ed8d' into cluster 2025-12-24 15:22:12 +01:00
Steinar H. Gunderson ea9a328340 Merge commit 'c14b69790a62aad89fcc471cde482923dfe57f1e' into cluster
This is the last commit before there are more conflicts.
2025-12-24 15:17:55 +01:00
Steinar H. Gunderson fb9a3d3323 Merge commit '2dbb44e28d2e5b3c72ddbbd6f436d41f75031a22' into cluster 2025-12-24 15:17:18 +01:00
Steinar H. Gunderson 649004ed47 Merge commit 'db147fe2586527a854516016699949af53dc5b17' into cluster
This is the last commit before there are more conflicts.
2025-12-24 15:14:23 +01:00
Steinar H. Gunderson b7045f1bf6 Merge commit 'bc45cbc820a53a9fc405c06ca67bd7be3970344e' into cluster 2025-12-24 15:13:28 +01:00
Steinar H. Gunderson 5f5f991780 Merge commit '3502c8ae426506453ca64e87e48d962b327c2356' into cluster 2025-12-24 15:12:37 +01:00
Steinar H. Gunderson ba47c8eeeb Merge commit 'ddd250b9d655117920dd65a973cea2f8b3c57fce' into cluster 2025-12-24 15:03:08 +01:00
Steinar H. Gunderson 6b6279098d Merge commit 'd47aa639bd614b37a59f87e6ab68496580f0cf3e' into cluster
This is the last commit before there are more conflicts.
2025-12-24 15:02:03 +01:00
Steinar H. Gunderson 2a1ed1267e Merge commit 'd3fc1d835e5144cc98d6a7658fb8cfd9370792f1' into cluster 2025-12-24 14:37:41 +01:00
Steinar H. Gunderson 8e509e240a Merge commit '432995ad82119070afa0bf720eb65d800bcbf817' into cluster
This is the last commit before there are more conflicts.
2025-12-24 13:57:03 +01:00
Steinar H. Gunderson 1b80a18fac Merge commit '4912f5b0b5f2656bc5fcdb0af480765ad5aa8932' into cluster 2025-12-24 13:55:47 +01:00
Steinar H. Gunderson ee41dc5c98 Merge commit '14f6eab07d1d1e1a59372974e5534128676e9440' into cluster
This is the last commit before there are more conflicts.
2025-12-24 13:51:24 +01:00
Steinar H. Gunderson f0793a2080 Merge commit '9032c6cbe74ccf7e8963755501e7e6cc473ae471' into cluster
This is a rather involved merge, since the cluster changes interact
fairly badly with the refactoring we're merging:

The new code wants the search to know nothing at all about the UCI protocol,
it just calls a callback with some worker information (including a Position),
which then transforms that information into a InfoFull which is then sent
through another callback, upon which UCIEngine converts it to UCI and writes
it to stdout.

On the other hand, the MPI information wants to exchange PVs between workers
at this point, and it wants to use the UCI string as the primary medium of
exchange. Position is a lot of work to serialize, so we choose a middle road;
we capture the InfoFull by switching out the callback, serializes it, does the
MPI exchange on that, unserializes it and then continues with the print callback
(if there is new information).
2025-12-24 13:50:14 +01:00
Taras VukandDisservin c475024be7 Incorporate statscore into history bonus
Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 80128 W: 20879 L: 20498 D: 38751
Ptnml(0-2): 274, 9318, 20496, 9705, 271
https://tests.stockfishchess.org/tests/view/6945d11f3c8768ca45072218

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 134298 W: 34497 L: 33983 D: 65818
Ptnml(0-2): 81, 14334, 37812, 14834, 88
https://tests.stockfishchess.org/tests/view/6947bf033c8768ca45072491

closes https://github.com/official-stockfish/Stockfish/pull/6488

Bench: 2325401
2025-12-23 21:42:45 +01:00
Disservin 73b3b18595 Init threat offsets at compile time
Init threat offsets at compile time. Avoid another global init function call.

Passed STC Non-Regression:
https://tests.stockfishchess.org/tests/view/694971a83c8768ca4507275c
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 43296 W: 11284 L: 11077 D: 20935
Ptnml(0-2): 152, 4611, 11924, 4800, 161

closes https://github.com/official-stockfish/Stockfish/pull/6487

No functional change
2025-12-23 21:42:45 +01:00
FauziAkramandDisservin 4d4c6ebd02 Simplify doDeeperSearch formula
Passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 211776 W: 54939 L: 54911 D: 101926
Ptnml(0-2): 714, 24971, 54484, 25011, 708
https://tests.stockfishchess.org/tests/view/6938971875b70713ef796b70

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 216774 W: 55346 L: 55326 D: 106102
Ptnml(0-2): 105, 23599, 60980, 23577, 126
https://tests.stockfishchess.org/tests/view/693fc91f46f342e1ec20f9f6

closes https://github.com/official-stockfish/Stockfish/pull/6486

Bench: 3267755
2025-12-23 21:42:45 +01:00
Michael ChalyandDisservin 447f66acac Less penalty for quiet late moves that didn't beat the best move.
This moves since they are late in move ordering probably already have pretty bad stats anyway.
Passed STC:
https://tests.stockfishchess.org/tests/view/6943bcd546f342e1ec210e25
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 96704 W: 25206 L: 24798 D: 46700
Ptnml(0-2): 357, 11244, 24767, 11602, 382
Passed LTC:
https://tests.stockfishchess.org/tests/view/6946a8723c8768ca450722f0
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 89814 W: 23193 L: 22770 D: 43851
Ptnml(0-2): 53, 9532, 25321, 9941, 60
bench 2717363

closes https://github.com/official-stockfish/Stockfish/pull/6485

Bench: 2791988
2025-12-23 21:42:45 +01:00
anematodeandDisservin 1a67ccc72e Share correction history between threads
We did quite a few tests because this is a pretty involved change with
unknown scaling behavior, but results are decent.

[STC 10+0.1 1th, non-regression](https://tests.stockfishchess.org/tests/live_elo/6941ce3b46f342e1ec210180)
```
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 83200 W: 21615 L: 21452 D: 40133
Ptnml(0-2): 247, 9064, 22844, 9169, 276
```

[STC 5+0.05 8th](https://tests.stockfishchess.org/tests/live_elo/693dc38346f342e1ec20f555)
```
LLR: 3.48 (-2.94,2.94) <0.00,2.00>
Total: 58536 W: 15067 L: 14688 D: 28781
Ptnml(0-2): 87, 6474, 15781, 6825, 101
```

[LTC 20+0.2 8th](https://tests.stockfishchess.org/tests/live_elo/693f2afb46f342e1ec20f847)
```
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 27716 W: 7211 L: 6925 D: 13580
Ptnml(0-2): 8, 2674, 8207, 2962, 7
```

[LTC 10+0.1 64th](https://tests.stockfishchess.org/tests/live_elo/694003aa46f342e1ec20fac4):
```
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 16918 W: 4439 L: 4182 D: 8297
Ptnml(0-2): 3, 1493, 5213, 1744, 6
```

[NUMA test, 5+0.05 256th](https://tests.stockfishchess.org/tests/view/6941ee4e46f342e1ec210203)
```
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 7124 W: 1910 L: 1678 D: 3536
Ptnml(0-2): 0, 560, 2211, 790, 1
```

[LTC 60+0.6 64th](https://tests.stockfishchess.org/tests/live_elo/6940a85346f342e1ec20fcde):
```
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 15504 W: 4045 L: 3826 D: 7633
Ptnml(0-2): 0, 1002, 5530, 1219, 1
```

Bonus (courtesy of Viz): The 1 double kill in this last test was master
blundering a cool mate in 3: https://lichess.org/jyNZuRl4

Basically the idea here is to share correction history between threads.
That way, T1 can use the correction values produced by T2, which already
searched positions with that pawn structure etc., so that T1 can search
more efficiently. The table size per thread is about the same, so we
shouldn't get a large increase in hash collisions; in fact, I'd expect a
lower collision rate overall.

Although I came up with and implemented the idea independently,
[Caissa](https://github.com/Witek902/Caissa) was the first engine to
implement corrhist sharing (and corrhist in the first place) – this idea
is not completely novel.

The table size is rounded to a power of two. In particular, it's `65536
* nextPowerOfTwo(threadCount)`. That way, the indexing operation becomes
an AND of the key bits with a mask, rather than something more expensive
(e.g., a `mul_hi64`-style approach or a modulo).

The updates are racy, like the TT, but because `entry` is hoisted into a
register, there's no risk of writing back a value that's out of the
designated range `[-D, D]`. Various attempts at rewriting using atomics
led to substantial slowdowns, so we begrudgingly ignored the functions
in thread sanitizer, but at some point we'd like to make this better.

We allocate one shared correction history per NUMA node, because the
penalty associated with crossing nodes is substantial – I get a 40% hit
with NPS=4 and 256 threads, which is intolerable. With separate tables
per NUMA node I get a 6% penalty for nodes per second, which isn't ideal
but apparently compensated for.

closes https://github.com/official-stockfish/Stockfish/pull/6478

Bench: 2690604

Co-authored-by: Disservin <disservin.social@gmail.com>
2025-12-23 21:42:29 +01:00
FauziAkramandDisservin fb41f2953f Remove low ply history for check evasions scoring
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 81888 W: 21336 L: 21166 D: 39386
Ptnml(0-2): 284, 9438, 21342, 9584, 296
https://tests.stockfishchess.org/tests/view/692ada47b23dfeae38cffce5

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 107328 W: 27534 L: 27404 D: 52390
Ptnml(0-2): 55, 11390, 30659, 11490, 70
https://tests.stockfishchess.org/tests/view/692d7a01b23dfeae38d011ab

closes https://github.com/official-stockfish/Stockfish/pull/6467

Bench: 3006182
2025-12-23 21:41:34 +01:00
Steinar H. Gunderson a3cb287405 Merge commit '299707d2c2cbf1694bb21ed4a375b54ef35d719e' into cluster 2025-12-23 16:21:07 +01:00
AliceRoseliaandDisservin c467fe5ba4 Simplify futility pruning
Passed non regression STC:
https://tests.stockfishchess.org/tests/view/693e642c46f342e1ec20f68d
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 107968 W: 28080 L: 27937 D: 51951
Ptnml(0-2): 381, 12708, 27626, 12925, 344

Passed non regression LTC:
https://tests.stockfishchess.org/tests/view/693ff10c46f342e1ec20fa6a
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 334266 W: 85271 L: 85370 D: 163625
Ptnml(0-2): 179, 36395, 94086, 36292, 181

closes https://github.com/official-stockfish/Stockfish/pull/6484

Bench: 2987379
2025-12-21 15:43:32 +01:00
Shawn XuandDisservin 495296fc76 Remove Secondary TT Aging
Passed VVLTC w/ STC Bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 107006 W: 27676 L: 27326 D: 52004
Ptnml(0-2): 21, 9505, 34097, 9863, 17
https://tests.stockfishchess.org/tests/view/6939ac5b75b70713ef796f11

Passed VVLTC w/ LTC Bounds:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 263190 W: 67547 L: 66837 D: 128806
Ptnml(0-2): 32, 23939, 82942, 24651, 31
https://tests.stockfishchess.org/tests/view/692165823b03dd3a060e666d

closes https://github.com/official-stockfish/Stockfish/pull/6480

Bench: 2800411
2025-12-21 15:43:32 +01:00
sscg13andDisservin 32292d1e62 Represent threat weights directly as i8
LEB128 adds no additional compression and adds extra complexity to the
code currently.

closes https://github.com/official-stockfish/Stockfish/pull/6479

No functional change
2025-12-21 15:43:32 +01:00
Daniel MonroeandDisservin b4b01d0ca2 Remove rootDepth condition in newDepth clamping
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 132256 W: 34462 L: 34347 D: 63447
Ptnml(0-2): 470, 15625, 33833, 15720, 480
https://tests.stockfishchess.org/tests/view/69325fe0a24a6df719fcca16

Passed simplification LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 128220 W: 32503 L: 32392 D: 63325
Ptnml(0-2): 94, 13900, 35982, 14069, 65
https://tests.stockfishchess.org/tests/view/6934c2b3a24a6df719fcdf1e

closes https://github.com/official-stockfish/Stockfish/pull/6472

Bench: 2926903
2025-12-21 15:43:32 +01:00
ppigazziniandDisservin d92e6b458a chore(ci): bump runner to macos-15
closes https://github.com/official-stockfish/Stockfish/pull/6475

No functional change
2025-12-21 15:43:32 +01:00
Pieter te BrakeandDisservin 955c927265 Removed redundant board updates
Contrary to what the comment says, `remove_piece` does in fact set the
relevant `board` elements to `NO_PIECE`.

closes https://github.com/official-stockfish/Stockfish/pull/6471

No functional change
2025-12-21 15:43:32 +01:00
Timothy HerchenandDisservin e0e6fdf094 Tweak nnue_accumulator indexing
```
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 92736 W: 24149 L: 23764 D: 44823
Ptnml(0-2): 280, 10056, 25334, 10395, 303
```

The use of `IndexType` in the loops is suboptimal because it requires
truncation to 32 bits, and thereby defeats some optimizations. Using
`size_t` for the loop body works nicely, and a signed index into the
index lists allows the compiler to assume the case `added.size() ==
UINT_MAX`, which would be an infinite loop, to not happen.

Passed STC
https://tests.stockfishchess.org/tests/live_elo/692fd98ab23dfeae38d01d98

closes https://github.com/official-stockfish/Stockfish/pull/6466

No functional change
2025-12-21 15:43:32 +01:00
mstemberaandDisservin e1c919fd7e Fix one error and all warnings on MSVC 2026
The error is "C1001: Internal compiler error." in uci.cpp on line 370 of
master.  For some reason the compiler can't handle the
std::size(hashfullAges) inside the lambda.  Older MSVC versions had no
problem.

Most of the warnings are due to implicit type conversions "conversion
from type A to type B, possible loss of data"
many of which have been present for a while.

closes https://github.com/official-stockfish/Stockfish/pull/6464

No functional change
2025-12-21 15:43:32 +01:00
Daniel MonroeandDisservin 8449e5eb9d Remove non-functional term in isShuffling
closes https://github.com/official-stockfish/Stockfish/pull/6462

No functional change
2025-12-21 15:43:32 +01:00
Timothy HerchenandDisservin a98c3f6878 Small threat-related cleanups
Remove a couple unused things, mark `noRaysContaining` as
`[[maybe_unused]]` (suggested by Viz) to silence a warning on GCC 10,
update a comment, and replace inline constants

closes https://github.com/official-stockfish/Stockfish/pull/6460

No functional change
2025-12-21 15:43:32 +01:00
FauziAkramandDisservin 863c0ec6d0 Simplify piece threat calculation
Passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 105984 W: 27206 L: 27067 D: 51711
Ptnml(0-2): 260, 11556, 29245, 11647, 284
https://tests.stockfishchess.org/tests/view/6914798e7ca87818523317cf

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 53028 W: 13635 L: 13456 D: 25937
Ptnml(0-2): 28, 5184, 15908, 5369, 25
https://tests.stockfishchess.org/tests/view/6918c5fc7ca8781852332312

closes https://github.com/official-stockfish/Stockfish/pull/6459

No functional change
2025-12-21 15:43:25 +01:00
FauziAkramandDisservin 5edfabd070 Fix Typo
closes https://github.com/official-stockfish/Stockfish/pull/6436

No functional change
2025-12-21 15:22:06 +01:00
Timothy HerchenandJoost VandeVondele c109a88ebe fix missing condition
Fixes a bug in https://github.com/official-stockfish/Stockfish/pull/6453
https://github.com/official-stockfish/Stockfish/commit/abd835dcbc3a28481224f6253b00b7420d062513
The modifications to the DirtyThreats bitboards should only happen if PutPiece is true.
This somehow didn't affect bench at the default parameters.

Reference AVX2:
./stockfish.killdeer-fix.avx2 bench 64 1 23
Nodes searched  : 178140156
Nodes/second    : 1503152

before patch:
./stockfish.master.avx512icl  bench 64 1 23
Nodes searched  : 218349728
Nodes/second    : 1743450

after patch:
./stockfish.killdeer-fix.avx512icl bench 64 1 23
Nodes searched  : 178140156
Nodes/second    : 1727520

passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 39328 W: 10293 L: 10080 D: 18955
Ptnml(0-2): 113, 4306, 10629, 4487, 129
https://tests.stockfishchess.org/tests/view/692fb2d8b23dfeae38d01c81

closes https://github.com/official-stockfish/Stockfish/pull/6463

Bench: 2912398
2025-12-03 12:32:29 +01:00
Daniel MonroeandJoost VandeVondele 5297ba0a1a Move hindsight reductions above cutoffs
Passed STC
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 52640 W: 13701 L: 13361 D: 25578
Ptnml(0-2): 168, 6143, 13356, 6487, 166
https://tests.stockfishchess.org/tests/view/6918dc407ca8781852332339

Passed LTC
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 207690 W: 53187 L: 52520 D: 101983
Ptnml(0-2): 93, 22515, 57994, 23118, 125
https://tests.stockfishchess.org/tests/view/6919dd307ca87818523324c7

closes https://github.com/official-stockfish/Stockfish/pull/6448

Bench: 2912398
2025-12-01 17:35:34 +01:00
Timothy HerchenandJoost VandeVondele abd835dcbc Improve update_piece_threats
passed on avx512ICL:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 30240 W: 8026 L: 7726 D: 14488
Ptnml(0-2): 95, 3235, 8171, 3513, 106
https://tests.stockfishchess.org/tests/view/69281d9ab23dfeae38cfeeb8

passed on generic architectures:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 73184 W: 19183 L: 18821 D: 35180
Ptnml(0-2): 258, 7988, 19744, 8338, 264
https://tests.stockfishchess.org/tests/view/6928ba11b23dfeae38cff276

subsequent cleanups tested as:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 72480 W: 18678 L: 18502 D: 35300
Ptnml(0-2): 242, 7925, 19718, 8125, 230
https://tests.stockfishchess.org/tests/view/692a26adb23dfeae38cff566

We add an argument noRaysContaining, which skips all discoveries which contain
all bits in the argument; if the argument is from | to, then this will
eliminate the discovery. Separately, on AVX512ICL we can speed up the
computation of DirtyThreats by moving from a pop_lsb loop to a vector
extraction with vpcompressb. See PR for details.

closes https://github.com/official-stockfish/Stockfish/pull/6453

No functional change
2025-12-01 17:26:50 +01:00
Joost VandeVondeleandDisservin 9e2ee13e77 Update main network to nn-2962dca31855.nnue
trained with https://github.com/vondele/nettest/blob/dd921672bd4ad8eb09fa45248078450857e81884/threats.yaml

on top off and tested against https://github.com/official-stockfish/Stockfish/pull/6452

passed STC
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 130720 W: 34028 L: 33570 D: 63122
Ptnml(0-2): 435, 15348, 33384, 15710, 483
https://tests.stockfishchess.org/tests/view/69294539b23dfeae38cff3ad

passed LTC
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 51144 W: 13140 L: 12794 D: 25210
Ptnml(0-2): 38, 5370, 14408, 5720, 36
https://tests.stockfishchess.org/tests/view/692a9c37b23dfeae38cffa4f

closes https://github.com/official-stockfish/Stockfish/pull/6457

Bench: 2823033
2025-11-30 21:58:35 +01:00
KazAppsandDisservin 74303ca7f9 Update AUTHORS (KazApps)
closes https://github.com/official-stockfish/Stockfish/pull/6454

No functional change
2025-11-30 21:58:35 +01:00
Joost VandeVondeleandDisservin 4fe04a2cc6 Update main network to nn-87a9d7857d88.nnue
trained with https://github.com/vondele/nettest/blob/842d95177f882c0e9b5262247c38d8fcb3e0f3db/threats.yaml

passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 114880 W: 30291 L: 29851 D: 54738
Ptnml(0-2): 499, 13547, 28947, 13909, 538
https://tests.stockfishchess.org/tests/view/6923ec4bba083df4ca63dd39

passed LTC:
LLR: 2.97 (-2.94,2.94) <0.50,2.50>
Total: 113268 W: 29115 L: 28634 D: 55519
Ptnml(0-2): 114, 12295, 31340, 12766, 119
https://tests.stockfishchess.org/tests/view/6925cd01b23dfeae38cfe134

closes https://github.com/official-stockfish/Stockfish/pull/6452

Bench: 3028457
2025-11-30 21:58:35 +01:00
FauziAkramandDisservin c95386256e Simplify the highBestMoveEffort formula in Time management
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 22272 W: 5885 L: 5655 D: 10732
Ptnml(0-2): 76, 2329, 6108, 2535, 88
https://tests.stockfishchess.org/tests/view/69244d07ba083df4ca63e02b

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 48690 W: 12405 L: 12221 D: 24064
Ptnml(0-2): 29, 4755, 14597, 4931, 33
https://tests.stockfishchess.org/tests/view/6924de9aba083df4ca63e327

closes https://github.com/official-stockfish/Stockfish/pull/6451

No functional change
2025-11-30 21:58:35 +01:00
Timothy HerchenandDisservin 7c7c574e6b Fix msvc macro
closes https://github.com/official-stockfish/Stockfish/pull/6449

No functional change
2025-11-30 21:58:35 +01:00
Shawn XuandDisservin 45d034fa50 Formatting fixups
closes https://github.com/official-stockfish/Stockfish/pull/6446

No functional change
2025-11-30 21:58:35 +01:00
pb00067andDisservin c73f21df97 Fix for SF getting stuck during search (issue #5023)
Among several passed similar versions this is the simpliest one.

Passed STC non-regression
https://tests.stockfishchess.org/tests/view/691f3b4aacb6dbdf23d07d11
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 126368 W: 32607 L: 32489 D: 61272
Ptnml(0-2): 408, 13974, 34298, 14100, 404

Passed LTC non-regression
https://tests.stockfishchess.org/tests/view/69209712acb6dbdf23d0836a
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 114786 W: 29097 L: 28976 D: 56713
Ptnml(0-2): 53, 11841, 33488, 11954, 57

closes https://github.com/official-stockfish/Stockfish/pull/6447

Bench: 2941767
2025-11-30 21:58:34 +01:00
Carlos EsparzaandDisservin f4244e13e4 Some more work on FullThreats::make_index
[Passed STC](https://tests.stockfishchess.org/tests/live_elo/691fc321acb6dbdf23d07e4b):

```
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 215360 W: 55651 L: 55108 D: 104601
Ptnml(0-2): 520, 22399, 61290, 22960, 511
```

This PR is on top of ces42's work so I'll rebase if that PR changes.
Essentially, it comprises my adjustments to `make_index` that remained
unmerged before threat inputs was finalized.

The unsigned intermediate variables let us skip a bunch of useless sign
extensions (because Square and Piece inherit from `int8_t`, and C/C++
semantics require narrow integers to be promoted to `int` before doing
arithmetic on them). Additionally, by removing the special usage of
`offsets[][64]` and `[65]` the indexing becomes more efficient. (This
usage was a temporary hack from sscg anyway, so I think he'll like that
it's gone.) Finally, the `sf_assume` condition was fixed so that it
actually makes a difference to the compiler.

The speedup is fairly nice locally (combining both ces42's and these changes):

```
Result of 100 runs
==================
base (...kfish.master) =    1691982  +/- 1907
test (./stockfish    ) =    1714349  +/- 1789
diff                   =     +22367  +/- 2465

speedup        = +0.0132
P(speedup > 0) =  1.0000
```

closes https://github.com/official-stockfish/Stockfish/pull/6445

No functional change
2025-11-30 21:45:14 +01:00
Shawn XuandDisservin 93f2d14d95 Simplify Incremental Updates
Passed Non-regression STC:
LLR: 3.03 (-2.94,2.94) <-1.75,0.25>
Total: 277856 W: 71575 L: 71611 D: 134670
Ptnml(0-2): 842, 30719, 75836, 30695, 836
https://tests.stockfishchess.org/tests/view/69169dc17ca8781852331d76

Supersedes #6430

closes https://github.com/official-stockfish/Stockfish/pull/6434

No functional change
2025-11-30 21:43:54 +01:00
Shawn XuandDisservin 1132d893e0 Simplify Accumulator Updates
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 146848 W: 37915 L: 37820 D: 71113
Ptnml(0-2): 415, 16159, 40186, 16244, 420
https://tests.stockfishchess.org/tests/view/691772217ca878185233202b

closes https://github.com/official-stockfish/Stockfish/pull/6421

No functional change
2025-11-30 21:41:51 +01:00
Daniel MonroeandJoost VandeVondele d9fd516547 Post-NNUEv10 tune
Tune search parameters after the switch to NNUEv10.

The change is neutral at STC but increases with the TC. The main changes are more aggressive corrections, futility pruning, and extensions.

Failed STC
LLR: -2.96 (-2.94,2.94) <0.00,2.00>
Total: 108320 W: 27616 L: 27719 D: 52985
Ptnml(0-2): 332, 12833, 27884, 12828, 283
https://tests.stockfishchess.org/tests/view/69212e623b03dd3a060e6114

Passed LTC
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 43272 W: 11117 L: 10788 D: 21367
Ptnml(0-2): 20, 4543, 12180, 4874, 19
https://tests.stockfishchess.org/tests/view/692130813b03dd3a060e6123

Passed VLTC
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 22714 W: 5840 L: 5581 D: 11293
Ptnml(0-2): 2, 2152, 6795, 2401, 7
https://tests.stockfishchess.org/tests/view/6921387b3b03dd3a060e6191

Passed VLTC SMP
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 11868 W: 3142 L: 2896 D: 5830
Ptnml(0-2): 0, 1007, 3676, 1249, 2
https://tests.stockfishchess.org/tests/view/69212e953b03dd3a060e611b

closes https://github.com/official-stockfish/Stockfish/pull/6444

bench 2907929
2025-11-22 07:56:47 +01:00
Shawn XuandJoost VandeVondele 035cb146d4 Do more futility pruning
closes https://github.com/official-stockfish/Stockfish/pull/6433

Bench: 2469519
2025-11-17 14:03:02 +01:00
Joost VandeVondele 61149ac9ee Update main net to nn-c0ae49f08b40.nnue
Trained using the recipe https://github.com/vondele/nettest/blob/d39f72420504e474a716b9977c1380541a7b482e/threats.yaml

fix CI check for negative evals.

fix format

passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 84032 W: 21876 L: 21490 D: 40666
Ptnml(0-2): 239, 9821, 21553, 10121, 282
https://tests.stockfishchess.org/tests/view/6914b81e7ca87818523318aa

passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 126420 W: 32429 L: 31927 D: 62064
Ptnml(0-2): 56, 13748, 35118, 14214, 74
https://tests.stockfishchess.org/tests/view/6916c1277ca8781852331dd8

closes https://github.com/official-stockfish/Stockfish/pull/6431

Bench: 2513286
2025-11-17 14:00:33 +01:00
Joost VandeVondele a27fcd6274 Fix format
No functional change
2025-11-17 13:57:53 +01:00
Andreas MatthiesandJoost VandeVondele 563b4a9c4d Cleanup benchmark
Avoid unnecessary consecutive ucinewgame commands in benchmark setup.
Remove measuring time and nodes in speedtest warmup.

closes https://github.com/official-stockfish/Stockfish/pull/6427

No functional change
2025-11-17 13:37:55 +01:00
Joost VandeVondele 4b8fffe3b3 Silence warning
for systems without shared memory support

Fixes https://github.com/official-stockfish/Stockfish/issues/6425

closes https://github.com/official-stockfish/Stockfish/pull/6426

No functional change
2025-11-17 13:36:11 +01:00
Carlos EsparzaandJoost VandeVondele 2084d94266 inline make_index() and avoid templating perspective
combination of two patches.

pass perspective as argument
passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 52832 W: 13725 L: 13528 D: 25579
Ptnml(0-2): 154, 5756, 14412, 5927, 167
https://tests.stockfishchess.org/tests/view/69162e307ca8781852331c6a

inline make_index
passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 68768 W: 17786 L: 17607 D: 33375
Ptnml(0-2): 187, 7591, 18694, 7680, 232
https://tests.stockfishchess.org/tests/view/6916859e7ca8781852331d36

closes https://github.com/official-stockfish/Stockfish/pull/6429

No functional change
2025-11-17 13:34:45 +01:00
Timothy HerchenandJoost VandeVondele b083049fe0 Use non-locking operations for nodes count and tbHits
fetch_add and friends must produce a locking instruction because they guarantee
consistency in the presence of multiple writers. Because only one thread ever
writes to nodes and tbHits, we may use relaxed load and store to emit regular
loads and stores (on both x86-64 and arm64), while avoiding undefined behavior
and miscounting nodes.

Credit must go to Arseniy Surkov of Reckless (codedeliveryservice) for
uncovering this performance gotcha.

failed yellow as a gainer on fishtest:
LLR: -2.95 (-2.94,2.94) <0.00,2.00>
Total: 219616 W: 57165 L: 57105 D: 105346
Ptnml(0-2): 732, 24277, 59720, 24357, 722
https://tests.stockfishchess.org/tests/view/69086abfea4b268f1fac2809

potential small speedup on large core system:
```
==== master ====
1 Nodes/second : 294269736
2 Nodes/second : 295217629
Average (over 2):  294743682
==== patch ====
1 Nodes/second : 299003603
2 Nodes/second : 298111320
Average (over 2):  298557461
```

closes https://github.com/official-stockfish/Stockfish/pull/6392

No functional change
2025-11-17 13:24:32 +01:00
Shawn XuandJoost VandeVondele 1d504b927f Fix undefined behavior
C++ standard does not define `uint8_t` as an allowed pointer alias type. Despite
`uint8_t` being `unsigned char` on most platforms, this is not enforced by the standard.

https://eel.is/c++draft/basic.lval#11
https://stackoverflow.com/a/16138470

closes https://github.com/official-stockfish/Stockfish/pull/6420

No functional change
2025-11-14 17:43:44 +01:00
Robert NurnbergandJoost VandeVondele 4b71d8e202 Allow time checking after each DTZ probe
This PR allows for time checking within Tablebases::rank_root_moves(). The
principal application for now is syzygy_extend_pv().

In the past, Stockfish suffered some time losses at e.g. TCEC when the HDD with
the DTZ tables was too slow for the chosen move overhead setting, see #5894.

```
> ./fastchess -engine name="patch" cmd=./stockfish.patch -engine name="master" cmd=./stockfish.master -each tc=10+0.1 option.SyzygyPath=/disk1/syzygy/3-4-5-6/WDL:/disk2/syzygy/3-4-5-6/DTZ -draw movenumber=34 movecount=8 score=20 -openings file=UHO_Lichess_4852_v1.epd format=epd -rounds 50 -repeat -concurrency 16
...
Results of patch vs master (10+0.1, 1t, 16MB, UHO_Lichess_4852_v1.epd):
Elo: 59.64 +/- 35.35, nElo: 117.61 +/- 68.10
LOS: 99.96 %, DrawRatio: 56.00 %, PairsRatio: 4.50
Games: 100, Wins: 34, Losses: 17, Draws: 49, Points: 58.5 (58.50 %)
Ptnml(0-2): [0, 4, 28, 15, 3], WL/DD Ratio: 0.87
--------------------------------------------------

Player: master
  Timeouts: 19
  Crashed: 0

Finished match
```

closes https://github.com/official-stockfish/Stockfish/pull/6422

No functional change
2025-11-14 17:38:55 +01:00
Shawn XuandJoost VandeVondele db824e26be Pass accumulator caches by reference
closes https://github.com/official-stockfish/Stockfish/pull/6416

No functional change
2025-11-14 17:32:17 +01:00
Daniel MonroeandJoost VandeVondele a191791f46 Clean up code and comments
closes https://github.com/official-stockfish/Stockfish/pull/6416

No functional change
2025-11-14 17:30:55 +01:00
Shawn XuandJoost VandeVondele bd82b9e01f Cleanup, fix style issues
use naming convention for variables, functions, constants

closes https://github.com/official-stockfish/Stockfish/pull/6416

no functional change
2025-11-14 17:29:01 +01:00
AliceRoseliaandJoost VandeVondele 7b7a9485d6 Simplify indexing
Removes 1 function (from_to) from the Move type, change them to simpler raw.
Remove the "and" use in the correction history structure calculation. Adjust
the indexing.

Passed simplification STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 42880 W: 11327 L: 11113 D: 20440
Ptnml(0-2): 161, 4852, 11212, 5042, 173
https://tests.stockfishchess.org/tests/view/690593b8ea4b268f1fac1e8a

Passed simplification LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 43560 W: 11276 L: 11079 D: 21205
Ptnml(0-2): 17, 4679, 12197, 4864, 23
https://tests.stockfishchess.org/tests/view/6906f819ea4b268f1fac216b

closes https://github.com/official-stockfish/Stockfish/pull/6391

Bench: 2523092
2025-11-14 17:28:17 +01:00
FauziAkramandJoost VandeVondele df88db16c6 Simplify NMP reduction formula
Passed STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 178912 W: 46625 L: 46559 D: 85728
Ptnml(0-2): 540, 21167, 45975, 21235, 539
https://tests.stockfishchess.org/tests/view/69060677ea4b268f1fac1f25

Passed LTC:
LLR: 2.99 (-2.94,2.94) <-1.75,0.25>
Total: 140988 W: 36278 L: 36176 D: 68534
Ptnml(0-2): 82, 15520, 39215, 15568, 109
https://tests.stockfishchess.org/tests/view/6908bc32ea4b268f1fac288f

closes https://github.com/official-stockfish/Stockfish/pull/6416

bench: 2959834
2025-11-14 17:24:16 +01:00
Timothy HerchenandJoost VandeVondele 229bd1e2e3 check for material key validity in tbprobe
During recent work on threat inputs, there was a hard-to-debug crash in tbprobe
caused by incorrectly computing st->materialKey. This PR adds correctness
checking to pos_is_ok and a faster check in tbprobe that will actually run in
CI (because pos_is_ok checking is disabled even in debug mode, for performance
purposes).

closes https://github.com/official-stockfish/Stockfish/pull/6410

No functional change
2025-11-13 22:42:10 +01:00
Torsten HellwigandJoost VandeVondele 3f2405bf4e Print NEON before POPCNT in compilation settings
Normally, Stockfish outputs the compilation settings from advanced to less
advanced, e.g.: `AVX512ICL VNNI AVX512 BMI2 AVX2 SSE41 SSSE3 SSE2 POPCNT`

With NEON, however, POPCNT is printed first, followed by the more advanced NEON
options: `POPCNT NEON_DOTPROD`

This PR places POPCNT behind the NEON options as well.

closes https://github.com/official-stockfish/Stockfish/pull/6402

no functional change
2025-11-13 22:36:30 +01:00
Daniel MonroeandJoost VandeVondele 9e38023a8c Simplify threat term in movepick
Passed simplification STC
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 71296 W: 18605 L: 18419 D: 34272
Ptnml(0-2): 250, 8374, 18183, 8622, 219
https://tests.stockfishchess.org/tests/view/690454c7ea4b268f1fac1bbe

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 240552 W: 61870 L: 61874 D: 116808
Ptnml(0-2): 113, 26300, 67460, 26284, 119
https://tests.stockfishchess.org/tests/view/69063956ea4b268f1fac1f66

closes https://github.com/official-stockfish/Stockfish/pull/6401

bench 2697501
2025-11-13 22:35:33 +01:00
Didier DurandandJoost VandeVondele 55643baa3f Fix some doc typos
closes https://github.com/official-stockfish/Stockfish/pull/6400

No functional change
2025-11-13 22:32:12 +01:00
Daniel MonroeandJoost VandeVondele 8551f86efc Remove check term in capture movepick
Passed simplification STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 248448 W: 64697 L: 64708 D: 119043
Ptnml(0-2): 784, 29393, 63971, 29202, 874
https://tests.stockfishchess.org/tests/view/68fc7afb637acd2a11e72d86

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 193626 W: 49808 L: 49764 D: 94054
Ptnml(0-2): 162, 21415, 53621, 21447, 168
https://tests.stockfishchess.org/tests/view/6901ad09637acd2a11e73828

closes https://github.com/official-stockfish/Stockfish/pull/6395

bench 2920273
2025-11-13 22:31:02 +01:00
Guenther DemetzandJoost VandeVondele 4784ff2b3b Unify do_move & do_null_move workload
While being there also remove and unused assignment.

Tested for non-regression at STC:
https://tests.stockfishchess.org/tests/view/69060d81ea4b268f1fac1f36
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 94496 W: 24570 L: 24421 D: 45505
Ptnml(0-2): 264, 10145, 26275, 10306, 258

closes https://github.com/official-stockfish/Stockfish/pull/6383

no functional change
2025-11-13 22:26:35 +01:00
mstemberaandJoost VandeVondele 84148586e5 Fix MSVC compile
broken after Shared Memory patch.

closes https://github.com/official-stockfish/Stockfish/pull/6397

No functional change
2025-11-13 22:21:35 +01:00
Timothy HerchenandJoost VandeVondele fa4f05d3ef Don't copy around DirtyThreats
The contents of DirtyThreats gets memcpyed twice for each call to do_move.
(Return value optimization doesn't apply to the do_move function itself because
it constructs a std::pair, so it gets copied; and the calls to reset also
require a copy.) This patch inserts the dirty info in place.

Sometimes the caller of do_move ignores the DirtyThreats info, so we pass in
scratch objects. I found that stack-allocating these scratch objects was bad on
Fishtest, so I begrudgingly put them in the Position struct. Both templating
the do_move function on whether dirty threats are needed and putting a
null-check branch for each use of dirty threats were slowdowns locally. Of
course, nothing prevents a future attempt at cleaning this up.

passed STC
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 68448 W: 17770 L: 17418 D: 33260
Ptnml(0-2): 198, 7425, 18630, 7769, 202
https://tests.stockfishchess.org/tests/view/6914c01a7ca87818523318ba

closes https://github.com/official-stockfish/Stockfish/pull/6414

No functional change
2025-11-13 22:17:03 +01:00
Viren6andJoost VandeVondele 3ae7684714 Improve Threats Speed
Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 23168 W: 6132 L: 5845 D: 11191
Ptnml(0-2): 77, 2405, 6325, 2708, 69
https://tests.stockfishchess.org/tests/view/69148c3c7ca8781852331831

```
Result of  50 runs
==================
base (./stockfish.master       ) =     985641  +/- 4249
test (./stockfish.patch        ) =    1038567  +/- 5679
diff                             =     +52926  +/- 4473

speedup        = +0.0537
P(speedup > 0) =  1.0000

CPU: 16 x AMD Ryzen 9 3950X 16-Core Processor
Hyperthreading: on
```

closes https://github.com/official-stockfish/Stockfish/pull/6415

No functional change
2025-11-13 22:13:47 +01:00
Daniel MonroeandJoost VandeVondele 7ac8e62219 Refine constant in correction history update
Passed STC
LLR: 3.04 (-2.94,2.94) <0.00,2.00>
Total: 250112 W: 65277 L: 64635 D: 120200
Ptnml(0-2): 841, 29326, 64134, 29860, 895
https://tests.stockfishchess.org/tests/view/69096c46ea4b268f1fac2a39

Passed LTC
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 142908 W: 37141 L: 36604 D: 69163
Ptnml(0-2): 100, 15478, 39742, 16053, 81
https://tests.stockfishchess.org/tests/view/690e7dfbec1d00d2c195c351

closes https://github.com/official-stockfish/Stockfish/pull/6405

bench 2613869
2025-11-13 22:10:30 +01:00
+1 8e5392d79a Update NNUE architecture to SFNNv10 with Threat Inputs and net nn-49c1193b131c.nnue
This commit introduces Full Threat Input features, which are a subset of Piece(Square)-Piece(Square) pairs. In any given position, the active features consist of pairs where the second piece’s square lies in the attack set of the first piece. This is an extremely simplified explanation that leaves out many details. The already-used HalfKAv2_hm feature set completes the input features.
Minor quantization changes have also been made.

The net nn-49c1193b131c.nnue was trained by vondele using the following setup: https://github.com/vondele/nettest/blob/7de71238e9b295e3f88ed7c9c5936af632c9b981/threats.yaml

A graphical version of an earlier scheme (with less refinement) that illustrates the core concepts can be found attached.
[NewInputs.pdf](https://github.com/user-attachments/files/23478441/NewInputs.pdf)

Further information, as well as a brief description of the history of development, can be found attached.
[Stockfish threat inputs PR summary.pdf](https://github.com/user-attachments/files/23478634/Stockfish.threat.inputs.PR.summary.pdf)

This has been a huge effort spanning over half a year, with the original [discussion thread](https://discord.com/channels/435943710472011776/1336647760388034610) reaching over 11k messages. Thanks to everyone who has contributed.

Monty PRs:
https://github.com/official-monty/Monty/pull/87 (Initial threat input PR)
https://github.com/official-monty/Monty/pull/114 (Fixed threat indexing to take into account colour correctly)
https://github.com/official-monty/Monty/pull/116 (i8 quantisation of weights whilst keeping calculations in i16)

Yukari commit:
https://github.com/yukarichess/yukari/commit/2d482c64a79cec03cf4987d5289334b9cdc737bc (Threat inputs merged)

Plentychess PRs:
https://github.com/Yoshie2000/PlentyChess/pull/400 (Threat inputs merged)
https://github.com/Yoshie2000/PlentyChess/pull/411 (Threat input weights quantised to i8)

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 63424 W: 16956 L: 16591 D: 29877
Ptnml(0-2): 276, 7522, 15797, 7795, 322
https://tests.stockfishchess.org/tests/view/69105b3dec1d00d2c195c569

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 27876 W: 7417 L: 7110 D: 13349
Ptnml(0-2): 23, 3033, 7530, 3318, 34
https://tests.stockfishchess.org/tests/view/6910d817ec1d00d2c195c66e

Passed VVLTC (Hash accidentally set to 1/2 normal value for both sides):
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 12458 W: 3353 L: 3102 D: 6003
Ptnml(0-2): 0, 1106, 3767, 1355, 1
https://tests.stockfishchess.org/tests/view/69115a26ec1d00d2c195c7cd

This version has also passed non-regression LTC against the originally passed version:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 51144 W: 13086 L: 12903 D: 25155
Ptnml(0-2): 22, 5167, 15018, 5336, 29
https://tests.stockfishchess.org/tests/view/69138a317ca87818523314bf

LTC elo estimate on ARM:
1 patch     :    13.9    1.9  38296.5   73728    52
2 master    :     0.0   ----  35431.5   73728    48

closes https://github.com/official-stockfish/Stockfish/pull/6411

bench: 2626086

Co-authored-by: Shawn Xu <xu107288696@gmail.com>
Co-authored-by: Timothy Herchen <timothy.herchen@gmail.com>
Co-authored-by: Viren6 <94880762+Viren6@users.noreply.github.com>
Co-authored-by: Yoshie2000 <patrick.leonhardt@gmx.net>
Co-authored-by: Joost Vandevondele <Joost.VandeVondele@gmail.com>
Co-authored-by: rn5f107s2 <clemens.lerchl@gmail.com>
Co-authored-by: cj5716 <125858804+cj5716@users.noreply.github.com>
Co-authored-by: AliceRoselia <63040919+AliceRoselia@users.noreply.github.com>
Co-authored-by: Linmiao Xu <linmiao.xu@gmail.com>
Co-authored-by: Disservin <disservin.social@gmail.com>
2025-11-12 10:49:39 +01:00
69a01b88f3 Use shared memory for network weights
This enables different Stockfish processes that use the same weights to use the
same memory. The approach establishes equivalence by memory content, and is
compatible with NUMA replication.  The benefit of sharing is reduced memory usage
and a speedup thanks to improved (inter-process) caching of the network in the
CPUs cache, and thus reduced bandwidth usage to main memory. Even though this
change doesn't benefit a user running a single process, this helps on fishtest
or e.g.  for Lichess, when multiple games run concurrently, or multiple
positions are analyzed in parallel.

This concept was probably first introduced in the Monty engine
(https://github.com/official-monty/Monty/pull/62), after a discussion in
https://github.com/official-stockfish/fishtest/issues/2077 on the issue of
memory pressure. Measurements based on Torch
(https://github.com/user-attachments/files/21386224/verbatim.pdf) further
suggested that large gains were possible. Multiple other engines have
adopted this 'verbatim' format as well.

The implementation here adds the flexibility needed for SF, for example, retains
the ability to bundle compressed networks with the binary, to load nets by uci
option, and to distribute the shared nets to the proper NUMA region. This
flexibility comes with a fair amount of complexity in the implementation, such
as OS specific code, and fallback code.

For most users this should be transparent. However, for example, those running
docker containers should ensure the `--ipc` flag is set correctly, and
`--shm-size` is sufficiently large.

The benefits of this patch significantly depend on hardware, with systems with
many cores and a large (O(150MB), the net size) L3 cache benefitting typically
most.  On such systems SF speedups (as measured via nps playing games with
large concurrency but just 1 thread) can be 38%, which results in master vs.
patch Elo which gains about 25 Elo.

```
   # PLAYER             :  RATING  ERROR   POINTS  PLAYED   (%)
   1 shared_memoryPR    :    24.8    1.9  39432.0   73728    53
   2 master             :     0.0   ----  34296.0   73728    47
```

In a multithreaded setup, where weights are already shared, that benefit is smaller,
for example on the same HW as above, but with 8t for each side.
```
   # PLAYER             :  RATING  ERROR  POINTS  PLAYED   (%)
   1 shared_memoryPR    :     5.2    3.5  9351.0   18432    51
   2 master             :     0.0   ----  9081.0   18432    49
```

On fishtest with a typical hardware mix of our contributors, the following was measured:

STC, 60k games
https://tests.stockfishchess.org/tests/view/69074a49ea4b268f1fac236c
Elo: 4.69 ± 1.4 (95%) LOS: 100.0%
Total: 60000 W: 16085 L: 15275 D: 28640
Ptnml(0-2): 154, 6440, 16053, 7148, 205
nElo: 9.38 ± 2.8 (95%) PairsRatio: 1.12

To verify correctness with a single process on a NUMA architecture,
speedtest was used, confirming near equivalence:
```
master:        Average (over 10):  296236186
shared_memory: Average (over 10):  295769332
```
Currently, using large pages for the shared network weights is not always possible,
which can lead to a small slowdown (1-2%), in case a single process is run.

closes https://github.com/official-stockfish/Stockfish/pull/6173

No functional change

Co-authored-by: disservin <disservin.social@gmail.com>
Co-authored-by: Joost VandeVondele <Joost.VandeVondele@gmail.com>
2025-11-02 16:04:09 +01:00
FauziAkramandJoost VandeVondele bc9f08731f Simplify Futility pruning formula
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 170112 W: 44266 L: 44193 D: 81653
Ptnml(0-2): 599, 20062, 43611, 20235, 549
https://tests.stockfishchess.org/tests/view/68f50b94637acd2a11e721ad

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 164658 W: 42393 L: 42318 D: 79947
Ptnml(0-2): 120, 18127, 45747, 18228, 107
https://tests.stockfishchess.org/tests/view/68fa6683637acd2a11e72ac3

closes https://github.com/official-stockfish/Stockfish/pull/6389

bench: 2351426
2025-11-01 11:39:00 +01:00
Shawn XuandJoost VandeVondele cd7880c030 Simplify Corrhist Bonus
Passed Non-regression STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 187776 W: 48687 L: 48631 D: 90458
Ptnml(0-2): 570, 22327, 48090, 22279, 622
https://tests.stockfishchess.org/tests/view/68f58019637acd2a11e72233

Passed Non-regression LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 159378 W: 41027 L: 40946 D: 77405
Ptnml(0-2): 122, 17645, 44078, 17718, 126
https://tests.stockfishchess.org/tests/view/68fe7492637acd2a11e73090

closes https://github.com/official-stockfish/Stockfish/pull/6385

Bench: 2458738
2025-11-01 11:36:33 +01:00
Carlos EsparzaandJoost VandeVondele e9674a7888 simplify make_index()
passed non-regression STC: https://tests.stockfishchess.org/tests/view/68fdb409637acd2a11e72f11
LLR: 3.08 (-2.94,2.94) <-1.75,0.25>
Total: 95008 W: 24837 L: 24677 D: 45494
Ptnml(0-2): 268, 10024, 26775, 10154, 283

closes https://github.com/official-stockfish/Stockfish/pull/6384

no functional change
2025-11-01 11:34:54 +01:00
Shawn XuandJoost VandeVondele 0351652995 Simplify Pawn History Bonus
Passed Non-regression STC:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 122016 W: 31655 L: 31525 D: 58836
Ptnml(0-2): 388, 14317, 31474, 14435, 394
https://tests.stockfishchess.org/tests/view/68f57e66637acd2a11e7221d

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 81348 W: 21016 L: 20858 D: 39474
Ptnml(0-2): 45, 8793, 22841, 8949, 46
https://tests.stockfishchess.org/tests/view/68f9e2d9637acd2a11e72997

closes https://github.com/official-stockfish/Stockfish/pull/6382

Bench: 2370893
2025-11-01 11:32:53 +01:00
Daniel MonroeandJoost VandeVondele c9a2aff485 Simplify correction update condition
Passed non-regression STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 95136 W: 24722 L: 24564 D: 45850
Ptnml(0-2): 307, 11139, 24522, 11289, 311
https://tests.stockfishchess.org/tests/view/68e5034ea017f472e763dc5a

Passed non-regression LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 256440 W: 65854 L: 65873 D: 124713
Ptnml(0-2): 144, 28287, 71375, 28272, 142
https://tests.stockfishchess.org/tests/view/68e71ae4a017f472e763e291

closes https://github.com/official-stockfish/Stockfish/pull/6375

bench 2216361
2025-11-01 11:29:47 +01:00
Daniel MonroeandJoost VandeVondele 013d42914f Simplify static eval bonus
Passed non-regression STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 176896 W: 45998 L: 45933 D: 84965
Ptnml(0-2): 609, 20845, 45451, 20958, 585
https://tests.stockfishchess.org/tests/view/68e5e48ba017f472e763dd21

Passed non-regression LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 77682 W: 20046 L: 19884 D: 37752
Ptnml(0-2): 41, 8404, 21786, 8572, 38
https://tests.stockfishchess.org/tests/view/68ee71e328e6d77fcff9fd68

closes https://github.com/official-stockfish/Stockfish/pull/6374

bench 2101654
2025-11-01 11:27:27 +01:00
Daniel MonroeandJoost VandeVondele fd3c563f57 Simplify r50 condition in cutoff
Passed non-regression STC
LLR: 3.11 (-2.94,2.94) <-1.75,0.25>
Total: 114560 W: 29832 L: 29689 D: 55039
Ptnml(0-2): 332, 12302, 31869, 12445, 332
https://tests.stockfishchess.org/tests/view/68e38587fa806e2e8393d4a9

Passed non-regression LTC
LLR: 2.99 (-2.94,2.94) <-1.75,0.25>
Total: 256272 W: 65817 L: 65832 D: 124623
Ptnml(0-2): 137, 25528, 76817, 25521, 133
https://tests.stockfishchess.org/tests/view/68e69b47a017f472e763e065

closes https://github.com/official-stockfish/Stockfish/pull/6373

Bench: 2438327
2025-11-01 11:20:23 +01:00
Daniel MonroeandJoost VandeVondele 9e071f3561 Simplify best move effort
Passed non-regression STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 44224 W: 11614 L: 11403 D: 21207
Ptnml(0-2): 147, 4936, 11726, 5165, 138
https://tests.stockfishchess.org/tests/view/68e9410ed323fd15c04e3a87

Passed non-regression LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 109788 W: 28223 L: 28096 D: 53469
Ptnml(0-2): 60, 11493, 31657, 11628, 56
https://tests.stockfishchess.org/tests/view/68eb5ac0a23744016c14af1d

closes https://github.com/official-stockfish/Stockfish/pull/6371

No functional change
2025-11-01 11:18:00 +01:00
Carlos EsparzaandJoost VandeVondele 11ab4cde07 Avoid unnecessary allocations in AccumulatorStack
replacing the vector with an array resulted in a .45+-.10 % speedup.

closes https://github.com/official-stockfish/Stockfish/pull/6352

no functional change
2025-11-01 11:12:03 +01:00
pb00067andJoost VandeVondele fa3b4ef5af Improve retrograde analisys and matefinding capability
fixes https://github.com/official-stockfish/Stockfish/issues/6328

Before calling search(depth), the patch ensures that depth is at least 1
whenever we encounter a decisive score in the transposition table (TT). This
prevents search(depth) from being executed by qsearch, which would otherwise
ignore that information.  Typically, decisive TT hits occur when analyzing a
mating sequence backward. Due to the nature of Iterative Deepening (IID), such
scores are usually first found at depth 0. Without this patch, valuable
information can be lost because qsearch may overwrite the TT entry by replacing
the value with a static evaluation, even though the node was already processed
at a higher depth. This is also why the engine sometimes loses track of an
already discovered mate.

Using ..\sf\patch.exe on matetrack.epd with --nodes 1000000
Engine ID:     Stockfish dev-20251015-nogit
Total FENs:    6554
Found mates:   3437
Best mates:    2438

Using ..\sf\master.exe on matetrack.epd with --nodes 1000000
Engine ID:     Stockfish dev-20251015-nogit
Total FENs:    6554
Found mates:   3337
Best mates:    2407

Passed STC
https://tests.stockfishchess.org/tests/view/68fa3fa7637acd2a11e72a79
LLR: 3.55 (-2.94,2.94) <0.00,2.00>
Total: 134144 W: 34960 L: 34471 D: 64713
Ptnml(0-2): 376, 14199, 37459, 14636, 402

Failed LTC
https://tests.stockfishchess.org/tests/view/68ffc1b5637acd2a11e73377
LLR: -3.10 (-2.94,2.94) <0.50,2.50>
Total: 75360 W: 19423 L: 19519 D: 36418
Ptnml(0-2): 38, 7553, 22605, 7435, 49

closes https://github.com/official-stockfish/Stockfish/pull/6386

bench: 2530552
2025-11-01 11:02:00 +01:00
shaowyxandJoost VandeVondele a49b52cf69 Revert malus and associated coefficient parameters resulting from using only quiet moves
Following #6226 and #6256, this patch ultimately corresponds to the revert of #6200.
Parameters were tuned on 60k LTC games.

STC (10+0.1 th1) was accepted:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 167488 W: 43573 L: 43063 D: 80852
Ptnml(0-2): 506, 19644, 43004, 20014, 576
https://tests.stockfishchess.org/tests/view/68f526a4637acd2a11e721c2

LTC (60+0.6 th1) was accepted:
LLR: 2.99 (-2.94,2.94) <0.50,2.50>
Total: 61068 W: 15882 L: 15510 D: 29676
Ptnml(0-2): 31, 6578, 16949, 6940, 36
https://tests.stockfishchess.org/tests/view/68fa1968637acd2a11e72a0a

Non-regression VLTC (180+1.8 th1) was accepted:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 50380 W: 13087 L: 12905 D: 24388
Ptnml(0-2): 5, 5018, 14962, 5200, 5
https://tests.stockfishchess.org/tests/view/68fdc6e5637acd2a11e72f33

closes https://github.com/official-stockfish/Stockfish/pull/6378

Bench: 2530552
2025-11-01 10:48:40 +01:00
Timothy HerchenandJoost VandeVondele 6764561916 Improve index generation
The speedup seems to vary by machine.  The indexing function can be changed w/o needing to understand intrinsics.

Result of 100 runs
==================
base (...ish_baseline) =    1719637  +/- 3233
test (./stockfish    ) =    1734245  +/- 3534
diff                   =     +14608  +/- 4868

speedup        = +0.0085
P(speedup > 0) =  1.0000

closes https://github.com/official-stockfish/Stockfish/pull/6366

No functional change
2025-11-01 10:42:42 +01:00
Timothy HerchenandJoost VandeVondele 3bb01ce7a9 prefetch earlier if checKEP is false
Only a modest amount of work happens between the transposition table prefetch
and the probe, so the probe still often stalls waiting for DRAM. The vast
majority of the time (in particular, if !checkEP), the key is known much
earlier in the do_move function and the latency can be better hidden.

passed STC SMP
https://tests.stockfishchess.org/tests/view/68f337c528e6d77fcffa066a
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 65256 W: 16806 L: 16462 D: 31988
Ptnml(0-2): 76, 7386, 17362, 7726, 78

but failed to gain STC
https://tests.stockfishchess.org/tests/view/68f3378328e6d77fcffa0665
LLR: -2.94 (-2.94,2.94) <0.00,2.00>
Total: 109824 W: 28523 L: 28618 D: 52683
Ptnml(0-2): 311, 11799, 30788, 11702, 312

In local tests, the speedup grows with thread count

closes https://github.com/official-stockfish/Stockfish/pull/6372

No functional change
2025-11-01 10:40:23 +01:00
Shawn XuandJoost VandeVondele f434cc2918 Allow AccumulatorStack::size to point to one past the end
this is guaranteed to be correct since we access the last element with `size - 1`

closes https://github.com/official-stockfish/Stockfish/pull/6368

fixes https://github.com/official-stockfish/Stockfish/issues/6367

no functional change
2025-11-01 10:34:44 +01:00
Taras VukandJoost VandeVondele b9e3e7921b Increase NMP reduction when improving
Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 52896 W: 13904 L: 13565 D: 25427
Ptnml(0-2): 186, 6022, 13706, 6335, 199
https://tests.stockfishchess.org/tests/view/68e67d02a017f472e763dfaf

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 168354 W: 43750 L: 43163 D: 81441
Ptnml(0-2): 81, 18284, 46882, 18827, 103
https://tests.stockfishchess.org/tests/view/68e79d7ba017f472e763e352

closes https://github.com/official-stockfish/Stockfish/pull/6361

bench: 2537382
2025-11-01 10:28:02 +01:00
Kieren PearsonandJoost VandeVondele 75edbee01e Use huge pages for worker data
As the worker data is quite large (28MB after #6350) we can make use of huge pages as a speedup.

prior to #6350

STC passed elo gaining bounds:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 166272 W: 43479 L: 42993 D: 79800
Ptnml(0-2): 540, 17598, 46365, 18102, 531
https://tests.stockfishchess.org/tests/view/68e9f3c0d323fd15c04e3ba4

Tested the speedup on a large machine with speedtest:
==== master ====
Average (over 20):  288644510
==== largePageWorker ====
Average (over 20):  292082422

Test after #6350:
==== rustam-cpp-testPR ====
Average (over 20):  291035351
==== rustam-cpp-testPR-pages ====
Average (over 20):  291937367

https://github.com/official-stockfish/Stockfish/pull/6359

No functional change
2025-10-14 17:46:13 +02:00
Robert Nurnberg @ elitebookandJoost VandeVondele 315f8ba4bf let CI check for mate scores outside the valid range
closes https://github.com/official-stockfish/Stockfish/pull/6358

No functional change
2025-10-14 17:34:17 +02:00
Daniel MonroeandJoost VandeVondele e7a4708ad5 Remove condition in qsearch
Instead of skipping non-captures when pawn history is exceptionally high, skip all non-captures

Passed non-regression STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 38016 W: 10018 L: 9795 D: 18203
Ptnml(0-2): 155, 4346, 9755, 4625, 127
https://tests.stockfishchess.org/tests/view/68e43d4aa017f472e763db2e

Passed rebased non-regression LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 96048 W: 24854 L: 24710 D: 46484
Ptnml(0-2): 47, 10504, 26780, 10644, 49
https://tests.stockfishchess.org/tests/view/68e59352a017f472e763dcf9

closes https://github.com/official-stockfish/Stockfish/pull/6355

bench 2343840
2025-10-14 17:32:32 +02:00
rustam-cppandJoost VandeVondele 63d449d1d9 bigger PAWN_HISTORY_SIZE
STC (10+0.1 th1) was accepted:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 75712 W: 19701 L: 19326 D: 36685
Ptnml(0-2): 254, 8738, 19513, 9081, 270
https://tests.stockfishchess.org/tests/view/68e286d5fa806e2e8393d160

LTC (60+0.6 th1) was accepted:
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 108492 W: 28068 L: 27604 D: 52820
Ptnml(0-2): 60, 11639, 30390, 12091, 66
https://tests.stockfishchess.org/tests/view/68e3e564a017f472e763dac0

closes https://github.com/official-stockfish/Stockfish/pull/6350

bench 2128316
2025-10-14 17:27:42 +02:00
mstemberaandJoost VandeVondele c956df4cbb Split accumulator 3-way for avxvnni
This does the same thing for x86-64-avxvnni as #6336, #6339.

closes https://github.com/official-stockfish/Stockfish/pull/6347

No functional change
2025-10-14 17:25:48 +02:00
dav1312andJoost VandeVondele fc5d296f9d Update get_native_properties.sh for AVXVNNI
Update get_native_properties.sh to detect and report 'x86-64-avxvnni' when the CPU supports it.

closes https://github.com/official-stockfish/Stockfish/pull/6346

No functional change
2025-10-14 17:22:30 +02:00
Daniel SamekandJoost VandeVondele e18ed795f2 Introduce 4-ply continuation correction history
Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 113984 W: 29752 L: 29323 D: 54909
Ptnml(0-2): 376, 13191, 29435, 13608, 382
https://tests.stockfishchess.org/tests/view/68dc3576fa806e2e8393bd93

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 80154 W: 20823 L: 20417 D: 38914
Ptnml(0-2): 47, 8600, 22383, 8994, 53
https://tests.stockfishchess.org/tests/view/68df83e0fa806e2e8393cbe8

Passed non-regression VLTC (rebased):
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 38158 W: 9992 L: 9805 D: 18361
Ptnml(0-2): 3, 3406, 12075, 3591, 4
https://tests.stockfishchess.org/tests/view/68e22f2afa806e2e8393d0ed

closes https://github.com/official-stockfish/Stockfish/pull/6345

bench 2169281
2025-10-07 11:50:51 +02:00
Daniel MonroeandJoost VandeVondele feb17e5acf Make sure we don't move a nonexistent piece in SEE
added assert.

closes https://github.com/official-stockfish/Stockfish/pull/6342

No functional change
2025-10-07 11:49:00 +02:00
mstemberaandJoost VandeVondele ee243f0fdc Remove x86-64-vnni256 target
When vnni256 was first introduced #3038 it was very slightly faster than vnni512
on some machines. We have since sped up vnni512 significantly (#4796 over 10%
alone, #6139, and probably others I'm forgetting). Since any machine that can
run vnni256 can run vnni512 this arch is no longer useful.

Note, x86-64-avxvnni still covers targets that don't have AVX512 but do have
VNNI on 128- and 256-bit vectors.

closes https://github.com/official-stockfish/Stockfish/pull/6340

No functional change
2025-10-05 09:38:44 +02:00
Shawn XuandJoost VandeVondele e5c2dc5edd remove clang-format workaround
closes https://github.com/official-stockfish/Stockfish/pull/6332

No functional change
2025-10-05 09:33:42 +02:00
b09339a420 Split accumulator 3-Way
Squeeze a tiny bit more juice from the original idea in #6336 which this is on top of.

https://tests.stockfishchess.org/tests/view/68dddd85fa806e2e8393c0b9
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 156320 W: 40925 L: 40447 D: 74948
Ptnml(0-2): 427, 17330, 42172, 17800, 431

4-way doesn't look to be better than this.
https://tests.stockfishchess.org/tests/view/68dde19efa806e2e8393c0c1

closes https://github.com/official-stockfish/Stockfish/pull/6339

No functional change

Co-authored-by: M Stembera <m_stembera@yahoo.com>
2025-10-05 09:33:23 +02:00
FauziAkramandJoost VandeVondele 7a7c033a86 Tweak Correction History Bonus Asymmetrically
Refine the correction history update by applying an asymmetric bonus based on the type of evaluation error. It differentiates between negative corrections and positive corrections.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 165184 W: 43314 L: 42807 D: 79063
Ptnml(0-2): 551, 19391, 42261, 19778, 611
https://tests.stockfishchess.org/tests/view/68cae49902c43c969fe7f008

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 243234 W: 62765 L: 62029 D: 118440
Ptnml(0-2): 163, 25996, 68551, 26756, 151
https://tests.stockfishchess.org/tests/view/68d1c50dfa806e2e8393aa1f

closes https://github.com/official-stockfish/Stockfish/pull/6338

Bench: 2746404
2025-10-05 09:25:44 +02:00
nicolasduhamelandJoost VandeVondele 5c93616a3f Adjust aspiration window
Narrow the aspiration window after fail high.

Passed STC:
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 51296 W: 13550 L: 13207 D: 24539
Ptnml(0-2): 165, 5971, 13052, 6276, 184
https://tests.stockfishchess.org/tests/view/68d99afffa806e2e8393b7ae

Passed LTC;
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 87780 W: 22795 L: 22375 D: 42610
Ptnml(0-2): 52, 9340, 24694, 9744, 60
https://tests.stockfishchess.org/tests/view/68dae0a6fa806e2e8393baad

See the comments in #6293 discussing the mechanisms leading to issue #6296

closes https://github.com/official-stockfish/Stockfish/pull/6337

Bench: 2336606
2025-10-05 09:23:02 +02:00
Shawn XuandJoost VandeVondele 5895f47dab Further simplify low ply history in evasions
Passed Non-regression STC (vs #6308):
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 174208 W: 45414 L: 45343 D: 83451
Ptnml(0-2): 633, 20324, 45095, 20443, 609
https://tests.stockfishchess.org/tests/view/68c24be359efc3c96b611487

Passed Non-regression LTC (vs #6308):
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 110070 W: 28099 L: 27969 D: 54002
Ptnml(0-2): 56, 11919, 30962, 12035, 63
https://tests.stockfishchess.org/tests/view/68c4efa559efc3c96b611dfc

closes https://github.com/official-stockfish/Stockfish/pull/6321

Bench: 2151873
2025-10-05 09:18:16 +02:00
Shawn XuandJoost VandeVondele 3073d82ccf Simplify use of low-ply history in evasions
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 65024 W: 16991 L: 16804 D: 31229
Ptnml(0-2): 182, 7423, 17119, 7602, 186
https://tests.stockfishchess.org/tests/view/68c23f5459efc3c96b6113df

Passed Non-regression LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 207312 W: 53126 L: 53095 D: 101091
Ptnml(0-2): 126, 21986, 59389, 22041, 114
https://tests.stockfishchess.org/tests/view/68c241e359efc3c96b6113ef

closes https://github.com/official-stockfish/Stockfish/pull/6308

Bench: 2515619
2025-09-28 21:21:57 +02:00
Shawn XuandJoost VandeVondele c62e71e78f Simplify a separate term in low ply history bonus formula
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 211200 W: 54887 L: 54860 D: 101453
Ptnml(0-2): 719, 24894, 54296, 25023, 668
https://tests.stockfishchess.org/tests/view/68c21e7f59efc3c96b6112c8

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 217842 W: 55587 L: 55568 D: 106687
Ptnml(0-2): 130, 23651, 61313, 23724, 103
https://tests.stockfishchess.org/tests/view/68c230ec59efc3c96b61135a

closes https://github.com/official-stockfish/Stockfish/pull/6307

Bench: 2070860
2025-09-28 21:19:22 +02:00
FauziAkramandJoost VandeVondele 40aeb5a411 Simplify away conthist 0
While at it, I also added the scaler note to the Lmrdepth/history formula.

Passed STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 25376 W: 6660 L: 6423 D: 12293
Ptnml(0-2): 77, 2947, 6403, 3184, 77
https://tests.stockfishchess.org/tests/view/68c1ccf759efc3c96b610deb

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 208464 W: 53371 L: 53342 D: 101751
Ptnml(0-2): 110, 22776, 58426, 22815, 105
https://tests.stockfishchess.org/tests/view/68c1d04b59efc3c96b610e13

closes https://github.com/official-stockfish/Stockfish/pull/6304

Bench: 2029296
2025-09-28 21:14:28 +02:00
Shawn XuandJoost VandeVondele 415a1ad426 Simplify Probcut Clamp Further
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 157984 W: 41116 L: 41030 D: 75838
Ptnml(0-2): 568, 18570, 40601, 18714, 539
https://tests.stockfishchess.org/tests/view/68b750518f94a4e5a7fe76cd

Passed Non-regression LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 335232 W: 85443 L: 85543 D: 164246
Ptnml(0-2): 177, 36616, 94137, 36502, 184
https://tests.stockfishchess.org/tests/view/68bc767259efc3c96b61076b

closes https://github.com/official-stockfish/Stockfish/pull/6303

Bench: 2213844
2025-09-28 21:12:01 +02:00
7a36c0e95f Remove quiet move streak
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 67712 W: 17744 L: 17555 D: 32413
Ptnml(0-2): 204, 8030, 17274, 8069, 279
https://tests.stockfishchess.org/tests/view/68b784628f94a4e5a7fe7706

Passed Non-regression LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 196050 W: 50270 L: 50228 D: 95552
Ptnml(0-2): 122, 21465, 54813, 21499, 126
https://tests.stockfishchess.org/tests/view/68ba119d8f94a4e5a7fe7941

closes https://github.com/official-stockfish/Stockfish/pull/6299

Bench: 2238789

Co-authored-by: Daniel Monroe <github.com@danielmonroe.net>
2025-09-28 21:08:58 +02:00
Timothy HerchenandJoost VandeVondele 9b164d9520 Shave some instructions off a hot loop in affine transform
On x86, GCC generates highly suboptimal code for this loop in its old form,
about 2x as many instructions as necessary. This decreases throughput
especially in an SMT setting. Clang does a better job but this change still has
some improvement. Note that the std::ptrdiff_t type is not optional; using an
unsigned type brings back the bad assembly. (Not sure why, but it seems
reliable on all the GCC versions I tested.)

passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 44672 W: 11841 L: 11527 D: 21304
Ptnml(0-2): 165, 4625, 12415, 4993, 138
https://tests.stockfishchess.org/tests/view/68d8111efa806e2e8393b10e

closes https://github.com/official-stockfish/Stockfish/pull/6331

No functional change
2025-09-28 21:04:29 +02:00
Michael ChalyandJoost VandeVondele 4f4f78f86e Extend nodes pre qsearch only with deep enough tt entries
Modification of current pre qsearch extensions - allowing it only for deep enough tt entries.

Passed STC:
https://tests.stockfishchess.org/tests/view/68c954d302c43c969fe7eea5
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 35872 W: 9548 L: 9236 D: 17088
Ptnml(0-2): 101, 4075, 9295, 4341, 124

Passed LTC:
https://tests.stockfishchess.org/tests/view/68ca4e4f02c43c969fe7ef5f
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 107754 W: 27784 L: 27324 D: 52646
Ptnml(0-2): 47, 11528, 30300, 11922, 80

closes https://github.com/official-stockfish/Stockfish/pull/6324

bench: 2462792
2025-09-28 20:56:36 +02:00
Nonlinear2andJoost VandeVondele 6fa42d9724 Simplify RFP return value
Passed non-regression STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 220800 W: 57351 L: 57332 D: 106117
Ptnml(0-2): 726, 26200, 56548, 26181, 745
https://tests.stockfishchess.org/tests/view/68b1db6e6217b8721dca9c67

Passed gainer LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 257820 W: 66286 L: 65523 D: 126011
Ptnml(0-2): 118, 27586, 72779, 28269, 158
https://tests.stockfishchess.org/tests/view/68c1d40859efc3c96b610e3d

closes https://github.com/official-stockfish/Stockfish/pull/6310

bench: 2364596
2025-09-28 20:53:47 +02:00
FauziAkramandJoost VandeVondele bbad001a49 Double PawnHistory size and update formula
Doubling PAWN_HISTORY_SIZE to 1024. So with that, we can apply a stronger learning signal.
The bonus/malus multipliers in the update_quiet_histories function have been increased accordingly.

Passed STC:
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 111008 W: 29136 L: 28708 D: 53164
Ptnml(0-2): 367, 12870, 28609, 13284, 374
https://tests.stockfishchess.org/tests/view/68c201d659efc3c96b61117e

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 117210 W: 30142 L: 29664 D: 57404
Ptnml(0-2): 49, 12532, 32970, 13000, 54
https://tests.stockfishchess.org/tests/view/68c20a6259efc3c96b6111ef

closes https://github.com/official-stockfish/Stockfish/pull/6306

Bench: 2788334
2025-09-28 20:51:34 +02:00
Syine MinetaandJoost VandeVondele a47a1c1804 Penalty to TT move history on MultiCut
If a reduced search fails high with the TT move excluded, we know that
there are multiple moves that can produce cutoffs. Applying a  penalty
to the TT move history reduces extensions for TT moves so that it may
spend a bit more time exploring other moves.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 263680 W: 68965 L: 68313 D: 126402
Ptnml(0-2): 855, 31090, 67336, 31666, 893
https://tests.stockfishchess.org/tests/view/68c1f65a59efc3c96b6110e5

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 61008 W: 15713 L: 15350 D: 29945
Ptnml(0-2): 27, 6428, 17235, 6783, 31
https://tests.stockfishchess.org/tests/view/68c1fc5359efc3c96b611141

Passed non-regression VLTC (60+0.6, Threads=8):
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 50340 W: 13003 L: 12830 D: 24507
Ptnml(0-2): 3, 4570, 15849, 4747, 1
https://tests.stockfishchess.org/tests/view/68c2056559efc3c96b6111c3

closes https://github.com/official-stockfish/Stockfish/pull/6305

Bench: 2342975
2025-09-28 20:49:24 +02:00
Sebastian BuchwaldandJoost VandeVondele a82e2a4cb6 Replace deprecated macOS 13 runner image
The macOS 13 runner image will be retired by December 4th, 2025.

closes https://github.com/official-stockfish/Stockfish/pull/6330

No functional change
2025-09-28 20:46:42 +02:00
Robert Nurnberg @ elitebookandJoost VandeVondele f922fb5d55 add multithreaded matetrack runs to CI
Add runs with --threads 4 to our matetrack CI. These won't be deterministic, of
course. But they may catch early some multithreading bugs in our mate
reporting.

Motivated by #6293 and #6296.

https://github.com/official-stockfish/Stockfish/pull/6297

No functional change.
2025-09-28 20:45:30 +02:00
DisservinandJoost VandeVondele 1a3a9c9005 simplify upload_binaries ci
Previously the upload step used the os of the artifact to create the upload and
used an extra msys2 step. This is in fact not needed and we can do all required
changes on ubuntu instead.

closes https://github.com/official-stockfish/Stockfish/pull/6294

No functional change
2025-09-28 20:43:54 +02:00
Syine MinetaandJoost VandeVondele fc54d87301 Revert "Adjustment of the aspiration window after fail high/low."
This reverts commit bfc7000597.

Fixes https://github.com/official-stockfish/Stockfish/issues/6296

After this commit a bug has been reported when the position is close to
mate or being mated. Since no workarounds have been suggested yet, it
is best to revert this commit until a better solution is found.

closes https://github.com/official-stockfish/Stockfish/pull/6309

Bench: 2493363
2025-09-13 08:18:52 +02:00
Stefan GeschwentnerandJoost VandeVondele bfc7000597 Adjustment of the aspiration window after fail high/low.
For the new bound in the opposite direction of the fail, use a weighted average of alpha, beta and best value +- delta. In the case of a fail high, different average weights are used depending on whether or not there was a best move change during the last search.

The weights are determined from the following two consecutive LTC tunings.

First tuning: https://tests.stockfishchess.org/tests/view/68ab727975da51a345a5ac2e
Second tuning: https://tests.stockfishchess.org/tests/view/68aba3fe75da51a345a5ad52

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 39504 W: 10243 L: 9947 D: 19314
Ptnml(0-2): 25, 4182, 11041, 4480, 24
https://tests.stockfishchess.org/tests/view/68acbb6d6217b8721dca95f8

Passed VLTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 91196 W: 23574 L: 23167 D: 44455
Ptnml(0-2): 13, 8943, 27276, 9356, 10
https://tests.stockfishchess.org/tests/view/68af64786217b8721dca993d

closes https://github.com/official-stockfish/Stockfish/pull/6292

Bench: 2785713
2025-09-06 07:57:57 +02:00
Shawn XuandJoost VandeVondele d5f152b5df Reintroduce #6259
Reintroduces af181d9, which no longer regresses on matetrack after #6286

Using ./stockfish on matetrack.epd with --nodes 1000000
Engine ID:     Stockfish dev-20250902-adfddd2c
Total FENs:    6554
Found mates:   3490
Best mates:    2429

Passed Non-regression LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 37608 W: 9726 L: 9523 D: 18359
Ptnml(0-2): 16, 4001, 10578, 4182, 27
https://tests.stockfishchess.org/tests/view/68b886778f94a4e5a7fe77d9

closes https://github.com/official-stockfish/Stockfish/pull/6289

Bench: 2493363
2025-09-06 07:55:14 +02:00
Shawn XuandJoost VandeVondele adfddd2c98 Add scaling note to STC/LTC tunes
It has been repeatedly shown that such tunes are suspectible to become
anti-scaling. Below are some recent examples:

https://github.com/official-stockfish/Stockfish/commit/2e91a8635468e40c89a2303ce50384864d088611
https://github.com/official-stockfish/Stockfish/commit/d11f49b790429c236a1a4169f0d8052635fc03dc

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 32448 W: 8651 L: 8342 D: 15455
Ptnml(0-2): 81, 3695, 8408, 3914, 126
https://tests.stockfishchess.org/tests/view/6899489b0049e8ccef9d64ad

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 73854 W: 19042 L: 18649 D: 36163
Ptnml(0-2): 37, 7908, 20659, 8271, 52
https://tests.stockfishchess.org/tests/view/689abbe7fd8719b088c8d514

Revert VVLTC with STC bound:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 53802 W: 14030 L: 13740 D: 26032
Ptnml(0-2): 5, 4924, 16754, 5212, 6
https://tests.stockfishchess.org/tests/view/68a9a9f575da51a345a5a675

Revert VVLTC with LTC bound:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 53658 W: 14022 L: 13699 D: 25937
Ptnml(0-2): 3, 4894, 16712, 5217, 3
https://tests.stockfishchess.org/tests/view/68a8d2b2b6fb3300203bca77

https://tests.stockfishchess.org/tests/view/688cf38bf17748b4d23c8057
https://tests.stockfishchess.org/tests/view/6890bc7792fcad741b804a19

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 74928 W: 19466 L: 19071 D: 36391
Ptnml(0-2): 37, 8048, 20901, 8439, 39

Failed Non-regression VLTC:
LLR: -2.94 (-2.94,2.94) <-1.75,0.25>
Total: 57704 W: 14643 L: 14928 D: 28133
Ptnml(0-2): 5, 5925, 17280, 5634, 8
https://tests.stockfishchess.org/tests/view/6890bc7792fcad741b804a19

(Note that an STC-tuned version passed non-regression, but was shortly
simplified)

https://github.com/official-stockfish/Stockfish/pull/6040

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 27776 W: 7352 L: 7054 D: 13370
Ptnml(0-2): 68, 3126, 7221, 3386, 87
https://tests.stockfishchess.org/tests/view/680ec0f83629b02d74b1605b

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 161304 W: 41432 L: 40864 D: 79008
Ptnml(0-2): 61, 17305, 45357, 17863, 66
https://tests.stockfishchess.org/tests/view/680ec7f93629b02d74b16084

Failed Non-regression VVLTC:
LLR: -2.94 (-2.94,2.94) <-1.75,0.25>
Total: 313466 W: 80573 L: 81089 D: 151804
Ptnml(0-2): 38, 29689, 97782, 29199, 25
https://tests.stockfishchess.org/tests/view/6810d0533629b02d74b16756

https://github.com/official-stockfish/Stockfish/pull/5907
https://github.com/official-stockfish/Stockfish/pull/5887

Passed LTC with STC bounds:
https://tests.stockfishchess.org/tests/view/67b115dd6c6b9e172ad1592f
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 75756 W: 19393 L: 19044 D: 37319
Ptnml(0-2): 60, 8251, 20913, 8588, 66

Passed LTC with LTC bounds:
https://tests.stockfishchess.org/tests/view/67af5f5d6c6b9e172ad15765
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 108126 W: 27880 L: 27412 D: 52834
Ptnml(0-2): 85, 11786, 29866, 12228, 98

Revert VVLTC w/ STC bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 56342 W: 14536 L: 14246 D: 27560
Ptnml(0-2): 7, 5061, 17741, 5359, 3
https://tests.stockfishchess.org/tests/view/67be4f8ad8d5c2c657c52d10

Revert VVLTC w/ LTC bounds:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 66562 W: 17364 L: 17016 D: 32182
Ptnml(0-2): 3, 6145, 20637, 6493, 3
https://tests.stockfishchess.org/tests/view/67bcd25ff6b602bd7222ea40

closes https://github.com/official-stockfish/Stockfish/pull/6284

no functional change
2025-09-02 20:42:03 +02:00
Shawn XuandJoost VandeVondele f2da0ccf3f Simplify SMP Reduction
Passed Non-regression SMP STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 232784 W: 59845 L: 59841 D: 113098
Ptnml(0-2): 289, 26459, 62934, 26379, 331
https://tests.stockfishchess.org/tests/view/68ab96bf75da51a345a5acd6

Passed Non-regression SMP LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 374270 W: 95978 L: 96113 D: 182179
Ptnml(0-2): 118, 38575, 109888, 38432, 122
https://tests.stockfishchess.org/tests/view/68abcf1c75da51a345a5adb6

closes https://github.com/official-stockfish/Stockfish/pull/6285

Bench: 2667107
2025-09-02 20:41:08 +02:00
Shawn XuandJoost VandeVondele 75ac6c7a06 simplify stalemate further
Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 155200 W: 40650 L: 40562 D: 73988
Ptnml(0-2): 533, 17588, 41258, 17700, 521
https://tests.stockfishchess.org/tests/view/68abe11375da51a345a5adec

Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 82824 W: 21442 L: 21287 D: 40095
Ptnml(0-2): 51, 8738, 23675, 8901, 47
https://tests.stockfishchess.org/tests/view/68b205606217b8721dca9c8e

10k Stalemate:
Elo: 1.46 ± 1.2 (95%) LOS: 99.0%
Total: 10000 W: 4640 L: 4598 D: 762
Ptnml(0-2): 0, 140, 4678, 182, 0
https://tests.stockfishchess.org/tests/view/68b2059b6217b8721dca9c90

closes https://github.com/official-stockfish/Stockfish/pull/6283

Bench: 2431727
2025-09-02 20:39:02 +02:00
Daniel MonroeandJoost VandeVondele da63060ea3 Simplify sign term in quiet histories
Simplify sign term in quiet histories

Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 497824 W: 129130 L: 129418 D: 239276
Ptnml(0-2): 1546, 59040, 128008, 58792, 1526
https://tests.stockfishchess.org/tests/view/68a2a9c1b6fb3300203bc3ed

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 70830 W: 18185 L: 18016 D: 34629
Ptnml(0-2): 36, 7658, 19861, 7821, 39
https://tests.stockfishchess.org/tests/view/68af36c96217b8721dca98d9

closes https://github.com/official-stockfish/Stockfish/pull/6282

Bench: 2393762
2025-09-02 20:37:04 +02:00
Joost VandeVondeleandxu-shawn 3833583862 limit dynamic reduction.
fixes https://github.com/official-stockfish/Stockfish/issues/6280

prevents probcut from extending depth as analyzed here:
https://github.com/official-stockfish/Stockfish/pull/6254#issuecomment-3239144280

passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 86688 W: 22591 L: 22426 D: 41671
Ptnml(0-2): 305, 10125, 22311, 10306, 297
https://tests.stockfishchess.org/tests/view/68b418ab467ff96994ae4cd5

passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 82914 W: 21287 L: 21130 D: 40497
Ptnml(0-2): 39, 8959, 23305, 9114, 40
https://tests.stockfishchess.org/tests/view/68b47ffa78ed7a752a9e8f36

closes https://github.com/official-stockfish/Stockfish/pull/6286

Bench: 2787731

Co-Authored-By: xu-shawn <50402888+xu-shawn@users.noreply.github.com>
2025-09-02 20:34:47 +02:00
Arseniy SurkovandJoost VandeVondele 731ad9bbc3 Simplify adjust_key50 template
Remove the AfterMove template from the adjust_key50 function, which is only ever called with false.

closes https://github.com/official-stockfish/Stockfish/pull/6278

No functional change
2025-08-30 15:17:57 +02:00
Sebastian BuchwaldandJoost VandeVondele 7a3483fa9e Remove superfluous cast
closes https://github.com/official-stockfish/Stockfish/pull/6277

No functional change
2025-08-30 15:14:51 +02:00
Torsten HellwigandJoost VandeVondele 6a6dadb5a6 Remove buggy and unused function
The function does not fulfill its purpose and is not used anywhere.
See https://discord.com/channels/435943710472011776/1101022188313772083/1409801409855094874

closes https://github.com/official-stockfish/Stockfish/pull/6275

no functional change
2025-08-30 15:13:40 +02:00
Shawn XuandJoost VandeVondele 3f18352396 Further Simplify Stalemate Detection
Passed Non-regression STC:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 191520 W: 49633 L: 49582 D: 92305
Ptnml(0-2): 557, 20676, 53260, 20693, 574
https://tests.stockfishchess.org/tests/view/68ab570075da51a345a5abe1

Passed Non-regression LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 212934 W: 54643 L: 54618 D: 103673
Ptnml(0-2): 117, 22417, 61364, 22462, 107
https://tests.stockfishchess.org/tests/view/68ab84e975da51a345a5ac6b

10k Stalemate:
Elo: 0.35 ± 1.2 (95%) LOS: 71.6%
Total: 10000 W: 4602 L: 4592 D: 806
Ptnml(0-2): 0, 148, 4694, 158, 0
nElo: 1.99 ± 6.8 (95%) PairsRatio: 1.07
https://tests.stockfishchess.org/tests/view/68abeb8175da51a345a5af82

closes https://github.com/official-stockfish/Stockfish/pull/6268

Bench: 2420973
2025-08-30 15:12:06 +02:00
7d213afd37 Non-functional simplifications
closes https://github.com/official-stockfish/Stockfish/pull/6267

No functional change

Co-authored-by: Daniel Monroe <github.com@danielmonroe.net>
2025-08-30 15:09:47 +02:00
Shawn XuandJoost VandeVondele a289ee389a Simplify Capture Futility Pruning
Passed Non-regression STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 187904 W: 48639 L: 48583 D: 90682
Ptnml(0-2): 560, 22150, 48502, 22154, 586
https://tests.stockfishchess.org/tests/view/68aad56075da51a345a5a9e3

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 94302 W: 24246 L: 24101 D: 45955
Ptnml(0-2): 44, 10274, 26371, 10417, 45
https://tests.stockfishchess.org/tests/view/68ab541975da51a345a5aacf

closes https://github.com/official-stockfish/Stockfish/pull/6266

Bench: 2376717
2025-08-30 15:09:00 +02:00
Robert Nurnberg @ elitebookandJoost VandeVondele 222df615c1 revert #6259
The recent commit af181d9 was merged as a simplification, but unfortunately hurts mate finding efficiency.

https://github.com/vondele/matetrack/blob/69f5c5e8627c163a6a8480b869ee09bc44dc44d4/matetrack1000000.csv#L4075-L4076

So this PR proposes to revert it, while adding a comment in the code for future reference.

closes https://github.com/official-stockfish/Stockfish/pull/6269

Bench: 2566711
2025-08-30 15:06:57 +02:00
Nonlinear2andJoost VandeVondele 75f07da912 Avoid high rule50 count zeroing cutoffs
If the depth is low enough, don't TT cut if the rule50 count is high and the TT move is zeroing.

Passed STC:
https://tests.stockfishchess.org/tests/view/68a8fdd8b6fb3300203bcb92
LLR: 3.05 (-2.94,2.94) <0.00,2.00>
Total: 110304 W: 28805 L: 28402 D: 53097
Ptnml(0-2): 275, 11174, 31875, 11529, 299

Passed LTC:
https://tests.stockfishchess.org/tests/view/68aa200f75da51a345a5a809
LLR: 3.00 (-2.94,2.94) <0.50,2.50>
Total: 187956 W: 48489 L: 47928 D: 91539
Ptnml(0-2): 59, 16118, 61075, 16655, 71

closes https://github.com/official-stockfish/Stockfish/pull/6271

bench: 2641840
2025-08-30 15:05:11 +02:00
Akshat SinhaandJoost VandeVondele 69de394439 fix ppc altivec check & loongarch64-lsx typo
closes https://github.com/official-stockfish/Stockfish/pull/6276

No functional change
2025-08-30 15:03:09 +02:00
Sebastian BuchwaldandJoost VandeVondele cb7232a818 Use integer type for UCI spin values
In particular, parse spin values as integers to avoid rounding errors.

Fixes https://github.com/official-stockfish/Stockfish/issues/6263
closes https://github.com/official-stockfish/Stockfish/pull/6264

No functional change
2025-08-30 15:02:05 +02:00
Shawn XuandJoost VandeVondele 678d503d8f Simplify LMR Extensions
Passed Non-regression STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 236896 W: 61683 L: 61683 D: 113530
Ptnml(0-2): 730, 28057, 60901, 28003, 757
https://tests.stockfishchess.org/tests/view/68a9c20475da51a345a5a6a0

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 73644 W: 18936 L: 18770 D: 35938
Ptnml(0-2): 33, 7951, 20685, 8123, 30
https://tests.stockfishchess.org/tests/view/68aa95c575da51a345a5a88a

closes https://github.com/official-stockfish/Stockfish/pull/6265

Bench:  2693376
2025-08-24 20:10:43 +02:00
Stefan GeschwentnerandJoost VandeVondele 39c077f15a Less reduction for later threads.
Give "(thread id mod 8) * 64" less reductions (up to nearly a half ply).

Passed SMP STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 413176 W: 106496 L: 105666 D: 201014
Ptnml(0-2): 504, 46732, 111266, 47602, 484
https://tests.stockfishchess.org/tests/view/68a24aeeb6fb3300203bbdc4

Passed SMP LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 60420 W: 15622 L: 15268 D: 29530
Ptnml(0-2): 11, 6106, 17632, 6440, 21
https://tests.stockfishchess.org/tests/view/68a2c2ffb6fb3300203bc516

closes https://github.com/official-stockfish/Stockfish/pull/6249

No functional change
2025-08-24 20:07:50 +02:00
FauziAkramandJoost VandeVondele c99eb8ef32 Remove cap from a bonusScale formula
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 127264 W: 33159 L: 33042 D: 61063
Ptnml(0-2): 336, 14509, 33866, 14544, 377
https://tests.stockfishchess.org/tests/view/68a63fccb6fb3300203bc818

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 469512 W: 120094 L: 120331 D: 229087
Ptnml(0-2): 238, 51317, 131885, 51076, 240
https://tests.stockfishchess.org/tests/view/68a8d867b6fb3300203bcab5

closes https://github.com/official-stockfish/Stockfish/pull/6260

Bench: 2433974
2025-08-24 20:01:01 +02:00
Michael ChalyandJoost VandeVondele af181d9fe1 Small simplification in probCut movepicker
Remove no longer required condition for tt moves. The idea is that if tt the value is
greater than probCut beta try tt move that is a capture anyway - even if it
doesn't pass SEE check.  This idea in various implementations passed some STCs
and was looking decent at LTCs as a gainer.

Passed STC:
https://tests.stockfishchess.org/tests/view/68aa1ee075da51a345a5a805
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 136160 W: 35189 L: 35079 D: 65892
Ptnml(0-2): 436, 16083, 34891, 16275, 395

Passed LTC:
https://tests.stockfishchess.org/tests/view/68aa91d375da51a345a5a884
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 80022 W: 20652 L: 20492 D: 38878
Ptnml(0-2): 33, 8717, 22357, 8865, 39

closes https://github.com/official-stockfish/Stockfish/pull/6259

Bench: 2307940
2025-08-24 19:58:49 +02:00
Shawn XuandJoost VandeVondele e2fdf6f005 Simplify separate malus formulas
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 16352 W: 4336 L: 4090 D: 7926
Ptnml(0-2): 54, 1832, 4157, 2080, 53
https://tests.stockfishchess.org/tests/view/68a808f0b6fb3300203bca08

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 94014 W: 24129 L: 23986 D: 45899
Ptnml(0-2): 47, 9917, 26934, 10064, 45
https://tests.stockfishchess.org/tests/view/68a8f45cb6fb3300203bcb5e

closes https://github.com/official-stockfish/Stockfish/pull/6256

Bench: 2483704
2025-08-24 19:55:35 +02:00
Shawn XuandJoost VandeVondele 901ad7e7ee Simplify quiet move streak
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 46272 W: 12076 L: 11866 D: 22330
Ptnml(0-2): 140, 5407, 11860, 5561, 168
https://tests.stockfishchess.org/tests/view/68a9c26575da51a345a5a6a2

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 140442 W: 35984 L: 35886 D: 68572
Ptnml(0-2): 72, 15333, 39305, 15447, 64
https://tests.stockfishchess.org/tests/view/68aa245e75da51a345a5a80d

closes https://github.com/official-stockfish/Stockfish/pull/6258

Bench: 2931171
2025-08-24 19:52:42 +02:00
Shawn XuandJoost VandeVondele 85f87649bf Simplify stalemate detection
Passed Non-regression STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 107392 W: 27959 L: 27818 D: 51615
Ptnml(0-2): 317, 12094, 28735, 12231, 319
https://tests.stockfishchess.org/tests/view/68a65d8ab6fb3300203bc825

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 94014 W: 24129 L: 23986 D: 45899
Ptnml(0-2): 47, 9917, 26934, 10064, 45
https://tests.stockfishchess.org/tests/view/68a8f45cb6fb3300203bcb5e

Passed Stalemate 10k:
Elo: 2.22 ± 1.3 (95%) LOS: 100.0%
Total: 10000 W: 4626 L: 4562 D: 812
Ptnml(0-2): 1, 137, 4659, 203, 0
nElo: 12.00 ± 6.8 (95%) PairsRatio: 1.47
https://tests.stockfishchess.org/tests/view/68a65d8ab6fb3300203bc825

Supersedes #6114
Closes #6232

closes https://github.com/official-stockfish/Stockfish/pull/6255

Bench: 2473929
2025-08-24 19:50:51 +02:00
Daniel MonroeandJoost VandeVondele 7fa7a36dc6 Remove upper bound in see margin
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 99840 W: 25965 L: 25815 D: 48060
Ptnml(0-2): 326, 11708, 25688, 11886, 312
https://tests.stockfishchess.org/tests/view/68a24504b6fb3300203bbdae

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 248388 W: 63544 L: 63556 D: 121288
Ptnml(0-2): 125, 27132, 69709, 27086, 142
https://tests.stockfishchess.org/tests/view/68a24f3bb6fb3300203bbdee

closes https://github.com/official-stockfish/Stockfish/pull/6250

Bench: 2432765
2025-08-24 19:47:20 +02:00
Daniel SamekandJoost VandeVondele 2659351ce7 Remove depth reduction in the full-depth search
Passed simplification STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 217184 W: 56218 L: 56195 D: 104771
Ptnml(0-2): 627, 25673, 55985, 25664, 643
https://tests.stockfishchess.org/tests/view/68a21e0eb6fb3300203bbcf3

Passed simplification LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 30474 W: 7923 L: 7713 D: 14838
Ptnml(0-2): 20, 3205, 8569, 3431, 12
https://tests.stockfishchess.org/tests/view/68a2adacb6fb3300203bc3f9

closes https://github.com/official-stockfish/Stockfish/pull/6246

bench 2626263
2025-08-24 19:45:23 +02:00
Daniel MonroeandJoost VandeVondele 57d76bd459 Simplify depth condition in prior reduction
Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 172864 W: 45085 L: 45015 D: 82764
Ptnml(0-2): 516, 20456, 44405, 20552, 503
https://tests.stockfishchess.org/tests/view/68a24791b6fb3300203bbdb6

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 84738 W: 21733 L: 21578 D: 41427
Ptnml(0-2): 49, 9169, 23780, 9320, 51
https://tests.stockfishchess.org/tests/view/68a2665ab6fb3300203bc269

closes https://github.com/official-stockfish/Stockfish/pull/6244

Bench: 2131292
2025-08-24 19:43:48 +02:00
DisservinandJoost VandeVondele 47d60a50b6 CI: fix typo in flag
closes https://github.com/official-stockfish/Stockfish/pull/6247

No functional change
2025-08-24 19:40:40 +02:00
FauziAkramandJoost VandeVondele 176ef577e2 Set back static constexpr
closes https://github.com/official-stockfish/Stockfish/pull/6243

No functional change
2025-08-24 19:39:49 +02:00
MyselfandJoost VandeVondele 20bc19558e Speedup_threat_by_lesser
This patch could have been considered a non-functional speedup, but changing
the ranking of illegal moves.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 131040 W: 34247 L: 33792 D: 63001
Ptnml(0-2): 407, 15281, 33675, 15764, 393
https://tests.stockfishchess.org/tests/live_elo/68a9a75b75da51a345a5a66d

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 280146 W: 72055 L: 71242 D: 136849
Ptnml(0-2): 153, 30252, 78438, 31089, 141
https://tests.stockfishchess.org/tests/view/68a9f58475da51a345a5a6e0

closes https://github.com/official-stockfish/Stockfish/pull/6261

Bench: 2321931
2025-08-24 19:37:58 +02:00
Stefan GeschwentnerandJoost VandeVondele d11f49b790 Remove log depth reduction terms.
Motivated by the elo loss of last VVLTC regression test i remove the new log
depth reductions (tuned at STC) to regain strength at VVLTC. The constant terms
are adjusted based on the old values and the changes from the last added VVLTC
tuning.

Passed VVLTC with STC bound:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 53802 W: 14030 L: 13740 D: 26032
Ptnml(0-2): 5, 4924, 16754, 5212, 6
https://tests.stockfishchess.org/tests/view/68a9a9f575da51a345a5a675

Passed VVLTC with LTC bound:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 53658 W: 14022 L: 13699 D: 25937
Ptnml(0-2): 3, 4894, 16712, 5217, 3
https://tests.stockfishchess.org/tests/view/68a8d2b2b6fb3300203bca77

closes https://github.com/official-stockfish/Stockfish/pull/6257

Bench: 2566780
2025-08-24 19:30:31 +02:00
FauziAkramandJoost VandeVondele 7fe46b5a70 VVLTC tweak
Passed VVLTC with STC bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 60796 W: 15908 L: 15609 D: 29279
Ptnml(0-2): 6, 5598, 18889, 5901, 4
https://tests.stockfishchess.org/tests/view/68a1fbfeb6fb3300203bbc5c

Passed VVLTC with LTC bounds:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 24914 W: 6528 L: 6256 D: 12130
Ptnml(0-2): 4, 2263, 7648, 2541, 1
https://tests.stockfishchess.org/tests/view/68a211aeb6fb3300203bbca7

closes https://github.com/official-stockfish/Stockfish/pull/6241

Bench: 2130122
2025-08-17 22:46:04 +02:00
Stefan GeschwentnerandJoost VandeVondele 8ecfc3c89d Bigger thread dependent initial window.
Increase the initial delta of the aspiration window  by "thread id mod 8".

Passed SMP STC:
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 106176 W: 27470 L: 27069 D: 51637
Ptnml(0-2): 121, 12032, 28386, 12423, 126
https://tests.stockfishchess.org/tests/view/68a178fab6fb3300203bbaec

Passed SMP LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 133076 W: 34272 L: 33772 D: 65032
Ptnml(0-2): 38, 13709, 38537, 14223, 31
https://tests.stockfishchess.org/tests/view/68a1b5d2b6fb3300203bbb71

closes https://github.com/official-stockfish/Stockfish/pull/6239

No functional change
2025-08-17 22:39:49 +02:00
87andJoost VandeVondele 2176c9387e Add performance warning comment for vpcompressw
closes https://github.com/official-stockfish/Stockfish/pull/6237

No functional change
2025-08-17 22:38:09 +02:00
Stefan GeschwentnerandJoost VandeVondele c56bd10cb5 Fix undefined behavior in stalemate trap detection.
An important part of it is a function which detects if we can move a king or
pawn. If this function called before quiet move generation phase the end of the
checked move list is undefined (Thanks to AliceRoselia pointing out we have
some problem because of wrong bench in a test).

This problem exists also in master but is really rarely triggered (thanks to
vondele which found one) but it seen significant more often if the capture
order score is increased.

This fix now simply assumes in this case (and other similiar cases) that a king
or pawn can move without checking any moves.

Passed non-regression STC (with default book):
LLR: 3.03 (-2.94,2.94) <-1.75,0.25>
Total: 203008 W: 52465 L: 52424 D: 98119
Ptnml(0-2): 498, 20293, 59893, 20310, 510
https://tests.stockfishchess.org/tests/view/68a06ec5fd8719b088c8dc1a

Passed non-regression STC (with stalemates book):
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 31616 W: 14418 L: 14366 D: 2832
Ptnml(0-2): 1, 189, 15375, 243, 0
https://tests.stockfishchess.org/tests/view/68a07538fd8719b088c8dc1f

closes https://github.com/official-stockfish/Stockfish/pull/6235

Bench: 2996176
2025-08-17 22:35:03 +02:00
Joost VandeVondele 4b6b13ce5a Update sde action to 2.4, switch to 9.33.0
older version no longer downloadable.

closes https://github.com/official-stockfish/Stockfish/pull/6240

No functional change
2025-08-17 22:32:03 +02:00
Daniel MonroeandJoost VandeVondele 169737a9eb Simplify dual bonuses
Merge dual capture/quiet bonuses into one

Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 223200 W: 57868 L: 57854 D: 107478
Ptnml(0-2): 689, 26441, 57296, 26515, 659
https://tests.stockfishchess.org/tests/view/6898d28e0049e8ccef9d6384

Passed simplification LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 147270 W: 37750 L: 37659 D: 71861
Ptnml(0-2): 84, 15869, 41633, 15970, 79
https://tests.stockfishchess.org/tests/view/68990fe30049e8ccef9d643c

closes https://github.com/official-stockfish/Stockfish/pull/6226

Bench: 2996176
2025-08-16 12:12:07 +02:00
Daniel MonroeandJoost VandeVondele c9c002450a Simplify depth term in reductions
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 317344 W: 82352 L: 82442 D: 152550
Ptnml(0-2): 999, 37395, 81907, 37439, 932
https://tests.stockfishchess.org/tests/live_elo/68976798f8a258623dda6c46

Passed simplification LTC
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 291708 W: 74733 L: 74787 D: 142188
Ptnml(0-2): 159, 31813, 81956, 31775, 151
https://tests.stockfishchess.org/tests/live_elo/689791a5f8a258623dda6d03

closes https://github.com/official-stockfish/Stockfish/pull/6225

Bench: 2436991
2025-08-16 12:06:00 +02:00
Kevin LuandJoost VandeVondele 0db4a1bee9 Remove completedDepth condition for SE
Just removing the term

Passed STC:
LLR: 3.27 (-2.94,2.94) <-1.75,0.25>
Total: 179840 W: 47211 L: 47126 D: 85503
Ptnml(0-2): 567, 17929, 52879, 17942, 603
https://tests.stockfishchess.org/tests/view/688bdd88502b34dd5e7111ea

Passed LTC:
LLR: 3.26 (-2.94,2.94) <-1.75,0.25>
Total: 150708 W: 38738 L: 38637 D: 73333
Ptnml(0-2): 79, 12922, 49262, 13001, 90
https://tests.stockfishchess.org/tests/view/688ce857f17748b4d23c8026

Passed VLTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 288312 W: 74104 L: 74152 D: 140056
Ptnml(0-2): 44, 26732, 90657, 26674, 49
https://tests.stockfishchess.org/tests/view/688ec4f57d68fe4f7f130ee3

closes https://github.com/official-stockfish/Stockfish/pull/6221

functional at higher depths

Bench: 3002300
2025-08-16 12:01:19 +02:00
FauziAkramandJoost VandeVondele d864148595 Simplify beta formula
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 152384 W: 39907 L: 39814 D: 72663
Ptnml(0-2): 557, 17958, 39053, 18083, 541
https://tests.stockfishchess.org/tests/view/6890b66692fcad741b804a10

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 98688 W: 25411 L: 25270 D: 48007
Ptnml(0-2): 45, 10692, 27743, 10805, 59
https://tests.stockfishchess.org/tests/view/6896019c618946ab878347b0

closes: https://github.com/official-stockfish/Stockfish/pull/6220

bench: 3002300
2025-08-16 11:57:44 +02:00
Stefan GeschwentnerandJoost VandeVondele 2e91a86354 Add log depth as term to reduction factors.
Replace most reduction factors with a linear term of the form X + Y * msb(depth) (using msb(depth) as simple and fast approximation for log(depth))
and tune the weights with following SPSA test: https://tests.stockfishchess.org/tests/view/689903860049e8ccef9d6415.

Here the tuned values of X[8] and Y[8] were ignored because a simple test with this parameters alone failed badly (https://tests.stockfishchess.org/tests/view/68992b350049e8ccef9d6482).

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 32448 W: 8651 L: 8342 D: 15455
Ptnml(0-2): 81, 3695, 8408, 3914, 126
https://tests.stockfishchess.org/tests/view/6899489b0049e8ccef9d64ad

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 73854 W: 19042 L: 18649 D: 36163
Ptnml(0-2): 37, 7908, 20659, 8271, 52
https://tests.stockfishchess.org/tests/view/689abbe7fd8719b088c8d514

closes https://github.com/official-stockfish/Stockfish/pull/6229

Bench: 3151102
2025-08-16 11:51:39 +02:00
mstemberaandJoost VandeVondele 0383670cd5 Avoid using _mm512_storeu_epi16()
gcc 9 & 10 do not provide this function, instead use the generic _mm512_storeu_si512

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95483

https://tests.stockfishchess.org/actions?max_actions=1&action=&user=&text=&before=1755266300.455175&run_id=

closes https://github.com/official-stockfish/Stockfish/pull/6231

No functional change
2025-08-16 11:46:39 +02:00
Jost TrillerandJoost VandeVondele 7a07ac0e76 Adjust probcut on staticEval
Reducing probcut depth more when staticEval is very good and less if staticEval is not so good compared to beta

STC: https://tests.stockfishchess.org/tests/view/68926bf6690844f940fa1350
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 174400 W: 45698 L: 45174 D: 83528
Ptnml(0-2): 622, 20356, 44672, 20976, 574

LTC: https://tests.stockfishchess.org/tests/view/689651b4f8a258623dda6531
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 56010 W: 14547 L: 14191 D: 27272
Ptnml(0-2): 31, 5929, 15738, 6267, 40

This patch was generated using qwen3-235b-a22b-thinking-2507:

Prompt: https://rentry.co/bm6vriai
Thinking: https://rentry.co/34km4zf8
Final respone: https://rentry.co/rauotrvr

closes https://github.com/official-stockfish/Stockfish/pull/6219

Bench: 3629755
2025-08-09 09:08:38 +02:00
FauziAkramandJoost VandeVondele ade8917447 Remove outdated comment
Remove the Elo worth, as they are now completely outdated, making them irrelevant and potentially misleading
Continuation of #5810 as these comments in "history.h" were missed.

closes https://github.com/official-stockfish/Stockfish/pull/6216

No functional change
2025-08-09 09:04:33 +02:00
mstemberaandJoost VandeVondele 5464f7b114 Remove an instruction from find_nnz()
closes https://github.com/official-stockfish/Stockfish/pull/6212

No functional change
2025-08-09 08:54:47 +02:00
Niklas FiekasandJoost VandeVondele 125a0cfe7b Build x86-64-avx512icl in CI
closes https://github.com/official-stockfish/Stockfish/pull/6217

No functional change
2025-08-09 08:51:45 +02:00
Stockfisher69andJoost VandeVondele 303fe9a164 Simplification: Futility pruning for captures
tested in final form as simplication

passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 113682 W: 29199 L: 29074 D: 55409
Ptnml(0-2): 68, 12359, 31859, 12490, 65
https://tests.stockfishchess.org/tests/view/688e85a17d68fe4f7f130e24

earlier test, equivalent:
passed STC:
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 120224 W: 31621 L: 31176 D: 57427
Ptnml(0-2): 415, 14167, 30567, 14484, 479
https://tests.stockfishchess.org/tests/view/68888bdd7b562f5f7b732643

closes https://github.com/official-stockfish/Stockfish/pull/6209

Bench: 2661621
2025-08-09 08:49:12 +02:00
KazAppsandJoost VandeVondele b177b7394a Separate bonus/malus parameters for quiet and capture moves
Parameters were tuned on 60k STC games.

Passed STC
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 16928 W: 4570 L: 4277 D: 8081
Ptnml(0-2): 57, 1905, 4270, 2152, 80
https://tests.stockfishchess.org/tests/view/688b4486502b34dd5e711122

Passed LTC
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 43602 W: 11373 L: 11041 D: 21188
Ptnml(0-2): 23, 4657, 12116, 4975, 30
https://tests.stockfishchess.org/tests/view/688c0eb5502b34dd5e71124b

Passed non-regression VLTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 181016 W: 46335 L: 46281 D: 88400
Ptnml(0-2): 33, 18177, 54034, 18231, 33
https://tests.stockfishchess.org/tests/view/688e0a77f17748b4d23c822b

closes https://github.com/official-stockfish/Stockfish/pull/6200

bench: 3435529
2025-08-09 08:43:49 +02:00
rn5f107s2andJoost VandeVondele 94175524b1 Only set ep square if ep capture is possible
for positions such as:

position fen rr6/2q2p1k/2P1b1pp/bB2P1n1/R2B2PN/p4P1P/P1Q4K/1R6 b - - 2 38 moves f7f5
position fen 8/p2r1pK1/6p1/1kp1P1P1/2p5/2P5/8/4R3 b - - 0 43 moves f7f5
position fen 4k3/4p3/2b3b1/3P1P2/4K3/8/8/8 b - - moves e7e5

ep is not possible for various reasons. Do not set the ep square and the do not change the zobrist key, as if ep were possible.

This fixes 3-fold detection, which could in rare cases be observed as a warning generated by fastchess for PVs that continued beyond an actual 3-fold.

Also fixes wrong evals for certain moves e.g. a2a1 for
position fen 4k3/1b2p2b/8/3P1P2/4K3/8/8/r7 b - - 0 1 moves e7e5 e4e3 a1a2 e3e4 a2a1 e4f3 a1a2 f3e4

fixes https://github.com/official-stockfish/Stockfish/issues/6138

originally written and tested by rn5f107s2, with small buglet as
https://tests.stockfishchess.org/tests/view/685af90843ce022d15794400

failed STC
LLR: -2.93 (-2.94,2.94) <-1.75,0.25>
Total: 130688 W: 33986 L: 34362 D: 62340
Ptnml(0-2): 414, 13292, 38302, 12928, 408
https://tests.stockfishchess.org/tests/view/688bcb35502b34dd5e7111c9

passed LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 99018 W: 25402 L: 25273 D: 48343
Ptnml(0-2): 54, 9097, 31089, 9204, 65
https://tests.stockfishchess.org/tests/view/688c613c502b34dd5e7112d3

https://github.com/official-stockfish/Stockfish/pull/6211

Bench: 2946135

Co-authored-by: Joost VandeVondele <Joost.VandeVondele@gmail.com>
2025-08-04 15:16:21 +02:00
Jost TrillerandJoost VandeVondele 377a3a5269 Depth dependent reduction threshold when full-depth re-search
STC: https://tests.stockfishchess.org/tests/view/68894d577b562f5f7b73273b
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 155072 W: 40651 L: 40150 D: 74271
Ptnml(0-2): 609, 18271, 39292, 18738, 626

LTC: https://tests.stockfishchess.org/tests/view/688c2705502b34dd5e71127a
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 321012 W: 82891 L: 81995 D: 156126
Ptnml(0-2): 227, 34421, 90285, 35375, 198

This commit was generated using qwen3-235b-a22b-thinking-2507:

Prompt: https://rentry.co/iqtaoht7

Reasoning/thinking: https://rentry.co/wm6t9hye

closes https://github.com/official-stockfish/Stockfish/pull/6210

Bench: 2983938
2025-08-04 15:12:55 +02:00
Shawn XuandJoost VandeVondele 14a2e50a3d Simplify key after
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 534016 W: 139438 L: 139771 D: 254807
Ptnml(0-2): 2098, 63469, 136136, 63278, 2027
https://tests.stockfishchess.org/tests/view/688ac64d502b34dd5e7110a4

Passed Non-regression LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 43980 W: 11346 L: 11149 D: 21485
Ptnml(0-2): 31, 4689, 12353, 4886, 31
https://tests.stockfishchess.org/tests/view/688ea8a47d68fe4f7f130eb3

closes https://github.com/official-stockfish/Stockfish/pull/6207

Bench: 2884232
2025-08-04 15:07:37 +02:00
Michael ChalyandJoost VandeVondele 5d1505d8c7 Reintroduce reduction term in LMR for cutnodes
This term increases reduction for cutnodes without tt move, term was simplified away recently.

Passed STC:
https://tests.stockfishchess.org/tests/view/688882007b562f5f7b73262f
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 187616 W: 49367 L: 48824 D: 89425
Ptnml(0-2): 710, 21898, 48034, 22471, 695

Passed LTC:
https://tests.stockfishchess.org/tests/view/688c71f4502b34dd5e71139a
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 140280 W: 36280 L: 35750 D: 68250
Ptnml(0-2): 79, 15175, 39121, 15667, 98

closes https://github.com/official-stockfish/Stockfish/pull/6206

Bench: 2996732
2025-08-04 14:58:59 +02:00
FauziAkramandJoost VandeVondele 8e733d68fd Refactor: Simplify pawn structure indexing
Refactoring the mechanism for calculating pawn structure hash indices and make it consistent with the rest.

closes https://github.com/official-stockfish/Stockfish/pull/6205

No functional change
2025-08-04 14:56:03 +02:00
Joost VandeVondele a6e34f128f Fix icx profile-build
fixes the issue pointed out in https://github.com/official-stockfish/Stockfish/pull/6202

closes https://github.com/official-stockfish/Stockfish/pull/6203

No functional change
2025-08-04 14:53:49 +02:00
FauziAkramandJoost VandeVondele a37b38bdf0 Add "d < newDepth" condition for doDeeperSearch
Add "d < newDepth" condition for doDeeperSearch

Passed STC:
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 66144 W: 17512 L: 17148 D: 31484
Ptnml(0-2): 220, 7726, 16833, 8056, 237
https://tests.stockfishchess.org/tests/view/6887cce67b562f5f7b731a79

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 411240 W: 106103 L: 105023 D: 200114
Ptnml(0-2): 256, 44249, 115566, 45257, 292
https://tests.stockfishchess.org/tests/view/6887ec527b562f5f7b731c37

closes https://github.com/official-stockfish/Stockfish/pull/6204

Bench: 2917036
2025-08-02 16:25:31 +02:00
FauziAkramandJoost VandeVondele 62b958f517 Remove code that is not used anywhere
closes https://github.com/official-stockfish/Stockfish/pull/6201

No functional change
2025-08-02 16:21:41 +02:00
Daniel MonroeandJoost VandeVondele cd52859bd5 Simplify improving term
Set improving whenever ss->staticEval >= beta

Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 38016 W: 10122 L: 9900 D: 17994
Ptnml(0-2): 143, 4388, 9747, 4564, 166
https://tests.stockfishchess.org/tests/view/688803917b562f5f7b7322eb

Passed simplification LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 21648 W: 5594 L: 5375 D: 10679
Ptnml(0-2): 12, 2241, 6096, 2466, 9
https://tests.stockfishchess.org/tests/view/688820397b562f5f7b73235d

closes https://github.com/official-stockfish/Stockfish/pull/6198

Bench: 3576884
2025-08-02 16:18:46 +02:00
CSTENTORandJoost VandeVondele 57b32f3e60 Make Resetting the Aspiration Window after FailLow more aggressive
Sets beta=(3alpha+beta)/4 when failLow. Before it was beta=(alpha+beta)/2, even
though in theory we should be getting an upper bound from a failLow, we can’t
trust the score that caused the failLow. Therefore beta=alpha doesn’t work. I
made the buffer between the new beta to the bestValue that caused the failLow
smaller.

passed STC:
https://tests.stockfishchess.org/tests/view/688674947b562f5f7b7315b5
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 119616 W: 31253 L: 30818 D: 57545
Ptnml(0-2): 313, 14028, 30724, 14397, 346

passed LTC:
https://tests.stockfishchess.org/tests/view/6887e0ad7b562f5f7b731afc
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 96978 W: 24905 L: 24466 D: 47607
Ptnml(0-2): 59, 10376, 27183, 10809, 62

closes https://github.com/official-stockfish/Stockfish/pull/6197

bench: 3085519
2025-08-02 16:12:12 +02:00
aronpetkovskiandJoost VandeVondele cef551092c Only do IIR in minimally reduced nodes
Passed STC
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 164256 W: 42799 L: 42299 D: 79158
Ptnml(0-2): 451, 19305, 42087, 19863, 422
https://tests.stockfishchess.org/tests/view/68868e9b7b562f5f7b731615

Passed LTC
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 279396 W: 71871 L: 71062 D: 136463
Ptnml(0-2): 126, 30117, 78404, 30924, 127
https://tests.stockfishchess.org/tests/view/6886a4977b562f5f7b731663

closes https://github.com/official-stockfish/Stockfish/pull/6196

Bench: 3490609
2025-08-02 16:02:41 +02:00
J. OsterandJoost VandeVondele 9034730a5a Display upper/lowerbound before wdl
Bound info belongs directly to score info

closes https://github.com/official-stockfish/Stockfish/pull/6195

No functional change.
2025-08-02 15:54:10 +02:00
MinetaSandJoost VandeVondele 32cb78d782 Increase CI timeouts for perft
might fix CI failures on macOS instances

closes https://github.com/official-stockfish/Stockfish/pull/6194

No functional change
2025-08-02 15:52:24 +02:00
FauziAkramandJoost VandeVondele fdd9c34b75 Re-adding ttHit condition, but this time in the inner block
Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 150240 W: 39305 L: 38819 D: 72116
Ptnml(0-2): 536, 17541, 38494, 17999, 550
https://tests.stockfishchess.org/tests/view/6887ce577b562f5f7b731a85

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 159120 W: 41025 L: 40461 D: 77634
Ptnml(0-2): 96, 16974, 44866, 17518, 106
https://tests.stockfishchess.org/tests/view/6887f0ba7b562f5f7b731c5d

closes https://github.com/official-stockfish/Stockfish/pull/6193

bench: 3498926
2025-08-02 15:50:17 +02:00
mstemberaandJoost VandeVondele 83071917e9 Optimize find_nnz() using VBMI2
about a 0.7% speedup for the x86-64-avx512icl ARCH

closes https://github.com/official-stockfish/Stockfish/pull/6186

No functional change
2025-08-02 15:49:35 +02:00
aronpetkovskiandJoost VandeVondele 64e8e1b247 Simplify LMR extension limit formula
This patch simplifies the maximum depth formula that reductions in LMR can allow.

Passed STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 35616 W: 9358 L: 9139 D: 17119
Ptnml(0-2): 101, 4051, 9278, 4284, 94
https://tests.stockfishchess.org/tests/view/688608cf966ed85face24882

Passed LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 22284 W: 5833 L: 5613 D: 10838
Ptnml(0-2): 8, 2367, 6173, 2585, 9
https://tests.stockfishchess.org/tests/view/68860d7f966ed85face248d5

closes https://github.com/official-stockfish/Stockfish/pull/6191

Bench: 2902492
2025-07-28 20:18:55 +02:00
Daniel MonroeandJoost VandeVondele 525a5749bc Simplify extra continuation history updates
Passed simplification STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 254464 W: 65774 L: 65793 D: 122897
Ptnml(0-2): 699, 30087, 65638, 30150, 658
https://tests.stockfishchess.org/tests/view/68863283966ed85face24a59

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 144750 W: 37111 L: 37018 D: 70621
Ptnml(0-2): 79, 15676, 40764, 15785, 71
https://tests.stockfishchess.org/tests/view/6886655f7b562f5f7b731359

closes https://github.com/official-stockfish/Stockfish/pull/6189

Bench: 3071078
2025-07-28 20:13:15 +02:00
StyxandJoost VandeVondele ab83d320b8 Simplify NMP condition
As this is tried only for cutNode, and all children of cutNodes will be nonCutNodes, the guard condition is redundant.
Simplification just removes the unnecessary condition (always true).

closes https://github.com/official-stockfish/Stockfish/pull/6187

No functional change
2025-07-28 20:11:40 +02:00
87andJoost VandeVondele 1d8f118c3e Remove unnecessary deque allocation in perft
closes https://github.com/official-stockfish/Stockfish/pull/6182

No functional change.
2025-07-28 20:05:26 +02:00
FauziAkramandJoost VandeVondele a9b7638e96 Simplify newDepth modification formulas
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 21856 W: 5709 L: 5471 D: 10676
Ptnml(0-2): 77, 2466, 5589, 2734, 62
https://tests.stockfishchess.org/tests/view/68862ea4966ed85face24a27

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 108828 W: 27893 L: 27763 D: 53172
Ptnml(0-2): 59, 11860, 30435, 12012, 48
https://tests.stockfishchess.org/tests/view/68863046966ed85face24a3b

closes https://github.com/official-stockfish/Stockfish/pull/6181

Bench: 3069051
2025-07-28 20:03:05 +02:00
Nonlinear2andJoost VandeVondele 2e2d2778fc increase futility value when capturing last moved piece
Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 204320 W: 53059 L: 52500 D: 98761
Ptnml(0-2): 551, 23874, 52778, 24379, 578
https://tests.stockfishchess.org/tests/view/688618ae966ed85face24976

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 138582 W: 35748 L: 35225 D: 67609
Ptnml(0-2): 62, 14874, 38900, 15389, 66
https://tests.stockfishchess.org/tests/view/68862717966ed85face249f2

closes https://github.com/official-stockfish/Stockfish/pull/6190

Bench: 2894413
2025-07-28 19:52:40 +02:00
FauziAkramandJoost VandeVondele 9045fdb227 Add depth condition
Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 103360 W: 26941 L: 26530 D: 49889
Ptnml(0-2): 291, 11964, 26770, 12353, 302
https://tests.stockfishchess.org/tests/view/68863547966ed85face24a6f

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 115128 W: 29734 L: 29258 D: 56136
Ptnml(0-2): 48, 12406, 32182, 12878, 50
https://tests.stockfishchess.org/tests/view/68864f867b562f5f7b7312f2

closes https://github.com/official-stockfish/Stockfish/pull/6184

Bench: 2972498
2025-07-28 19:49:05 +02:00
FauziAkramandJoost VandeVondele 4fcfb0b4b7 Tweak the logic for setting the improving flag
Tweak the logic for setting the improving flag when the static evaluation is significantly higher than alpha.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 92320 W: 24057 L: 23664 D: 44599
Ptnml(0-2): 247, 10689, 23893, 11086, 245
https://tests.stockfishchess.org/tests/view/68860aba966ed85face248a1

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 281292 W: 72496 L: 71683 D: 137113
Ptnml(0-2): 135, 30289, 78995, 31082, 145
https://tests.stockfishchess.org/tests/view/6886168c966ed85face24962

closes https://github.com/official-stockfish/Stockfish/pull/6180

Bench: 3200439
2025-07-28 19:45:13 +02:00
pb00067andJoost VandeVondele 402602a70d Simplify static evaluation difference to move ordering logic
Passed STC simplification bounds
https://tests.stockfishchess.org/tests/view/686fcb091451bd6fb83082bc
LLR: 2.92 (-2.94,2.94) <-1.75,0.25>
Total: 762816 W: 196717 L: 197295 D: 368804
Ptnml(0-2): 2052, 90457, 196853, 90109, 1937

Passed LTC simplification bounds
https://tests.stockfishchess.org/tests/view/68808d316e17e7fa3939b35d
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 44736 W: 11499 L: 11303 D: 21934
Ptnml(0-2): 24, 4766, 12586, 4974, 18

closes https://github.com/official-stockfish/Stockfish/pull/6176

bench: 2684407
2025-07-28 19:40:15 +02:00
Guenther DemetzandJoost VandeVondele b6082ba750 Simplify search functions according DRY principle
Passed STC non-regression bounds
https://tests.stockfishchess.org/tests/view/6870db4bfa93cf16d3bb286a
LLR: 3.00 (-2.94,2.94) <-1.75,0.25>
Total: 182016 W: 46735 L: 46673 D: 88608
Ptnml(0-2): 368, 19895, 50465, 19867, 413

closes https://github.com/official-stockfish/Stockfish/pull/6161

no functional change
2025-07-28 19:35:28 +02:00
Muzhen GamingandJoost VandeVondele 90c83c381f VVLTC Search Tune
alues were tuned with 125k VVLTC games.

Passed VVLTC 1st sprt:
https://tests.stockfishchess.org/tests/view/68865feb7b562f5f7b731341
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 39640 W: 10380 L: 10109 D: 19151
Ptnml(0-2): 5, 3556, 12424, 3833, 2

Passed VVLTC 2nd sprt:
https://tests.stockfishchess.org/tests/view/68864dfa7b562f5f7b7312ee
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 25972 W: 6807 L: 6537 D: 12628
Ptnml(0-2): 0, 2294, 8132, 2556, 4

closes https://github.com/official-stockfish/Stockfish/pull/6188

Bench: 3143696
2025-07-28 19:33:00 +02:00
aronpetkovskiandJoost VandeVondele a516b517d3 Simplify eval >= beta condition from NMP
It seems that now only checking if static eval is above beta by some margin is all that's necessary.

Passed Non-Regression STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 227264 W: 58430 L: 58421 D: 110413
Ptnml(0-2): 532, 26559, 59454, 26542, 545
https://tests.stockfishchess.org/tests/view/68763a77432ca24f6388c766

Passed Non-Regression LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 72048 W: 18622 L: 18455 D: 34971
Ptnml(0-2): 26, 7775, 20272, 7908, 43
https://tests.stockfishchess.org/tests/view/687c9f2b6e17e7fa3939b0c5

closes https://github.com/official-stockfish/Stockfish/pull/6175

Bench: 2759840
2025-07-24 10:38:13 +02:00
Daniel MonroeandJoost VandeVondele 92514fd3a2 Simplify away a term in see pruning
This change only sets the margin to 0 when depth <= 2 and margin <= 42 which
never has a difference, thus non-functional

Passed non-regression STC
LLR: 3.51 (-2.94,2.94) <-1.75,0.25>
Total: 306976 W: 78935 L: 78961 D: 149080
Ptnml(0-2): 630, 34097, 84067, 34057, 637
https://tests.stockfishchess.org/tests/view/68708015fa93cf16d3bb2782

closes https://github.com/official-stockfish/Stockfish/pull/6170

No functional change
2025-07-24 10:32:41 +02:00
DisservinandJoost VandeVondele e88ccfd835 Prevent accidential misuse of TUNE()
Writing a TUNE expression like

int smallNetThreshold = 962;
TUNE(smallNetThreshold, 850, 1050);

is wrong and can lead to a weird binary. It should thus fail to even compile,
with the proper intellisense you'll also see the error directly in your editor.

A cheap way to prevent this is to disallow any fundamental type in the parameter list.

closes https://github.com/official-stockfish/Stockfish/pull/6164

No functional change
2025-07-24 10:28:17 +02:00
FauziAkramandJoost VandeVondele bdc393d3a2 Correct comment
closes https://github.com/official-stockfish/Stockfish/pull/6159

No functional change
2025-07-24 10:26:15 +02:00
Daniel SamekandJoost VandeVondele 2c9a1871ae Add offsets to history updates.
All parameters were tuned on 60k STC games (https://tests.stockfishchess.org/tests/view/6867ef49fe0f2fe354c0c735).

Passed STC:
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 60576 W: 15794 L: 15444 D: 29338
Ptnml(0-2): 141, 7003, 15642, 7369, 133
https://tests.stockfishchess.org/tests/view/686d5af11451bd6fb830772a

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 58722 W: 15159 L: 14799 D: 28764
Ptnml(0-2): 19, 6259, 16455, 6599, 29
https://tests.stockfishchess.org/tests/view/686f9fd51451bd6fb83081c9

closes https://github.com/official-stockfish/Stockfish/pull/6158

bench: 2706299
2025-07-24 10:24:08 +02:00
FauziAkramandJoost VandeVondele 793110ca19 Remove !ttData.move condition from cutNode reduction
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 118080 W: 30427 L: 30298 D: 57355
Ptnml(0-2): 290, 13880, 30561, 14029, 280
https://tests.stockfishchess.org/tests/view/6853f9bc038630d25f468670

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 232272 W: 59187 L: 59182 D: 113903
Ptnml(0-2): 111, 25430, 65014, 25505, 76
https://tests.stockfishchess.org/tests/view/68585c40a596a06817bb922e

closes https://github.com/official-stockfish/Stockfish/pull/6157

bench: 2227361
2025-07-24 10:19:10 +02:00
Shawn XuandJoost VandeVondele e2aa125570 Consistent syntax for class members
Currently, search.cpp uses three different syntax when accessing class member variables

* thisThread object references. This was a remnant from when pos.this_thread() was necessary for thread-local variable access
* this object references
* implicit member variable access

This PR aims to deprecate thisThread and standardize this syntax to implicit variable access, which is consistent with the rest of SF's codebase.

closes https://github.com/official-stockfish/Stockfish/pull/6154

no functional change
2025-07-24 10:13:24 +02:00
87andJoost VandeVondele 8c2d21f91a Speedup movegen with VBMI2
Passed STC
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 166720 W: 43191 L: 42701 D: 80828
Ptnml(0-2): 348, 18567, 45069, 18999, 377
https://tests.stockfishchess.org/tests/view/686ae98dfe0f2fe354c0c867

Refactor movegen to emit to a vector with 16-bit elements, which enables a
speedup with AVX512-VBMI2 when writing moves to the move list.

Very crude timing measurements of perft via timing ./stockfish "go perft 7"
demonstrates approximately 17% perft speedup:

Summary
  ./Stockfish-dev/src/stockfish 'go perft 7' ran
    1.17 ± 0.04 times faster than ./Stockfish-base/src/stockfish 'go perft 7'

Estimated overall nps increase of 0.4% via speedtest: 33605229 -> 33749825
(many thanks JonathanHallstrom).

The corresponding arch is avx512icl as it is a good baseline for consumer
avx-512 feature set support; Intel Ice Lake was the first consumer AVX-512 CPU
and it is a decent subset of what AMD Zen 4 supports.

closes https://github.com/official-stockfish/Stockfish/pull/6153

No functional change
2025-07-24 10:08:35 +02:00
Daniel MonroeandJoost VandeVondele c13f883dc4 simplify away TT history term
Passed simplification STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 163168 W: 42136 L: 42055 D: 78977
Ptnml(0-2): 410, 19224, 42212, 19351, 387
https://tests.stockfishchess.org/tests/view/684e16ee703522d4f129c9e9

Passed simplification LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 303144 W: 77580 L: 77647 D: 147917
Ptnml(0-2): 150, 33110, 85134, 33013, 165
https://tests.stockfishchess.org/tests/view/6852ff97703522d4f129d5f7

closes https://github.com/official-stockfish/Stockfish/pull/6152

bench: 2294774
2025-07-24 09:59:00 +02:00
Daniel MonroeandJoost VandeVondele a43f12ef08 Simplify away constants in statscore
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 317280 W: 81589 L: 81678 D: 154013
Ptnml(0-2): 799, 37651, 81847, 37526, 817
https://tests.stockfishchess.org/tests/view/684e197a703522d4f129c9f0

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 174816 W: 44656 L: 44593 D: 85567
Ptnml(0-2): 83, 19064, 49058, 19113, 90
https://tests.stockfishchess.org/tests/view/6858aa54a596a06817bb924f

closes https://github.com/official-stockfish/Stockfish/pull/6150

bench: 2508140
2025-07-24 09:56:36 +02:00
FauziAkramandJoost VandeVondele 6a09a24cd8 Remove depth condition from ttCapture reduction
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 25920 W: 6822 L: 6593 D: 12505
Ptnml(0-2): 54, 2932, 6783, 3113, 78
https://tests.stockfishchess.org/tests/view/6853f9c2038630d25f468672

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 177318 W: 45339 L: 45278 D: 86701
Ptnml(0-2): 85, 19333, 49765, 19388, 88
https://tests.stockfishchess.org/tests/view/6854468a038630d25f4686c0

closes https://github.com/official-stockfish/Stockfish/pull/6149

bench: 2437275
2025-07-24 09:52:35 +02:00
Daniel MonroeandJoost VandeVondele 62f08568cd Simplify PV term in lmr
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 204000 W: 52541 L: 52506 D: 98953
Ptnml(0-2): 561, 24133, 52589, 24144, 573
https://tests.stockfishchess.org/tests/view/684f24ce703522d4f129cab5

Passed simplification LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 156150 W: 39890 L: 39807 D: 76453
Ptnml(0-2): 82, 16882, 44043, 17007, 61
https://tests.stockfishchess.org/tests/view/6855d8bf1d0d9fc6587538f8

closes https://github.com/official-stockfish/Stockfish/pull/6148

bench 2766493
2025-07-24 09:49:43 +02:00
FauziAkramandDisservin ce73441f20 Simplify sudden death time optimization
Passed Sudden Death STC:
https://tests.stockfishchess.org/tests/view/68455fe5375c2b77d9855351
LLR: 2.91 (-2.94,2.94) <-1.75,0.25>
Total: 49248 W: 13008 L: 12798 D: 23442
Ptnml(0-2): 309, 5491, 12821, 5687, 316

Passed Sudden Death LTC:
https://tests.stockfishchess.org/tests/view/6845a392375c2b77d98553cf
LLR: 3.01 (-2.94,2.94) <-1.75,0.25>
Total: 551070 W: 141699 L: 142031 D: 267340
Ptnml(0-2): 1923, 60608, 150916, 60054, 2034

Passed Standard STC:
https://tests.stockfishchess.org/tests/view/683c5ebb6ec7634154f9d989
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 142624 W: 36808 L: 36709 D: 69107
Ptnml(0-2): 302, 15448, 39745, 15483, 334

Passed Standard LTC:
https://tests.stockfishchess.org/tests/view/683f1a4f6ec7634154f9dc5a
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 146922 W: 37381 L: 37296 D: 72245
Ptnml(0-2): 69, 13552, 46117, 13671, 52

closes https://github.com/official-stockfish/Stockfish/pull/6132

Bench: 2249459
2025-07-02 18:41:46 +02:00
FauziAkramandDisservin e695b9537e Remove eval & beta diff from NM reduction
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 43456 W: 11178 L: 10966 D: 21312
Ptnml(0-2): 114, 5078, 11114, 5326, 96
https://tests.stockfishchess.org/tests/view/6849ae13e84567164b5c9de9

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 63090 W: 16302 L: 16125 D: 30663
Ptnml(0-2): 37, 6837, 17603, 7048, 20
https://tests.stockfishchess.org/tests/view/684ab516e84567164b5ca02f

closes https://github.com/official-stockfish/Stockfish/pull/6134

Bench: 2249459
2025-07-02 18:41:46 +02:00
mstemberaandDisservin ce7254b5ea Optimize find_nnz() using AVX512
About a 1% speedup for ARCH x86-64-avx512 and x86-64-vnni512.

Note:  This could be optimized further if we wanted to add an ARCH
supporting VBMI2 which is even more modern than VNNI.

https://en.wikichip.org/wiki/x86/avx512_vbmi2

closes https://github.com/official-stockfish/Stockfish/pull/6139

No functional change
2025-07-02 18:41:45 +02:00
MinetaSandDisservin ea85a54fef Fix trivial errors in Makefile
1. Remove "default" rule as "default" has no special meaning as a rule
name. Make runs the very first rule whose name doesn't begin with a dot
(which is "help" currently).

2. Make "format" rule not update dependencies.

closes https://github.com/official-stockfish/Stockfish/pull/6140

No functional change
2025-07-02 18:32:12 +02:00
Robert Nurnberg @ elitebookandDisservin 84e2f3851d Introduce a constant for ValueList size in search()
Having the size of these lists in two separate places likely contributed
to the crashes seen during the recent tuning attempt
https://tests.stockfishchess.org/tests/view/685c413343ce022d15794536.

Thanks to @MinetaS for spotting this.

closes https://github.com/official-stockfish/Stockfish/pull/6142

No functional change
2025-07-02 18:32:02 +02:00
Daniel MonroeandDisservin 3a0fff96cf Simplify quiet move streak logic
Passed non-regression STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 148960 W: 38409 L: 38312 D: 72239
Ptnml(0-2): 372, 17664, 38318, 17747, 379
https://tests.stockfishchess.org/tests/view/684c5773703522d4f129c5f7

Passed non-regression LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 180720 W: 46188 L: 46130 D: 88402
Ptnml(0-2): 84, 19608, 50929, 19644, 95
https://tests.stockfishchess.org/tests/view/68505fa5703522d4f129cbab

closes https://github.com/official-stockfish/Stockfish/pull/6143

Bench: 2055894
2025-07-02 18:31:04 +02:00
Shawn XuandDisservin 318c948c4d Remove non-functional low-ply history fill
lowPlyHistory is always cleared at the start of `iterative_deepening`, so clearing it here is non-functional.

closes https://github.com/official-stockfish/Stockfish/pull/6144

No functional change
2025-07-02 18:31:04 +02:00
Daniel MonroeandDisservin a7a56c41f6 Simplify history term in futility pruning
Passed simplification STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 298816 W: 76814 L: 76881 D: 145121
Ptnml(0-2): 726, 35477, 77057, 35434, 714
https://tests.stockfishchess.org/tests/view/683f440f6ec7634154f9dc7f

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 237774 W: 60801 L: 60802 D: 116171
Ptnml(0-2): 91, 26088, 66532, 26083, 93
https://tests.stockfishchess.org/tests/view/68441189ffbc71bd236778de

closes https://github.com/official-stockfish/Stockfish/pull/6130

Bench: 2411502
2025-07-02 18:31:04 +02:00
pb00067andDisservin 34b75f1575 Restore integrity of MovePicker::can_move_king_or_pawn
PR6005 broken by PR6071

passed STC non regression
https://tests.stockfishchess.org/tests/view/6839791f6ec7634154f9d312
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 31776 W: 8353 L: 8130 D: 15293
Ptnml(0-2): 74, 3566, 8382, 3795, 71

passed LTC non-regression
https://tests.stockfishchess.org/tests/view/6839c87a6ec7634154f9d367
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 120756 W: 31015 L: 30899 D: 58842
Ptnml(0-2): 50, 12732, 34703, 12838, 55

closes https://github.com/official-stockfish/Stockfish/pull/6119

Bench: 1945300
2025-07-02 18:31:02 +02:00
disservinandGitHub 15555e8f4a Disable linux gcc riscv64 (#6145)
Temporarily disable it, until we figure out the toolchain issues which are causing the crashes.

closes https://github.com/official-stockfish/Stockfish/pull/6145

No functional change
2025-06-29 12:33:20 +02:00
Shawn XuandJoost VandeVondele 5337edfdb6 remove non-functional else
since we break out of the loop in the other branch

closes https://github.com/official-stockfish/Stockfish/pull/6116

no functional change
2025-06-02 22:12:37 +02:00
Daniel MonroeandJoost VandeVondele 9ac756695e reduce depth by 5 in probcut
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 63328 W: 16402 L: 16213 D: 30713
Ptnml(0-2): 174, 7378, 16340, 7629, 143
https://tests.stockfishchess.org/tests/view/6833530e6ec7634154f9be7f

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 69936 W: 17795 L: 17625 D: 34516
Ptnml(0-2): 29, 7631, 19474, 7809, 25
https://tests.stockfishchess.org/tests/view/68335e386ec7634154f9c266

closes https://github.com/official-stockfish/Stockfish/pull/6120

Bench: 2307268
2025-06-02 22:09:11 +02:00
Daniel MonroeandJoost VandeVondele 254b6d5e85 Simplify corrections in extension margins
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 96192 W: 25002 L: 24852 D: 46338
Ptnml(0-2): 242, 10868, 25716, 11038, 232
https://tests.stockfishchess.org/tests/view/683b44cb6ec7634154f9d6ac

Passed simplification LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 83334 W: 21473 L: 21317 D: 40544
Ptnml(0-2): 37, 8877, 23674, 9051, 28
https://tests.stockfishchess.org/tests/view/683b79786ec7634154f9d75a

closes https://github.com/official-stockfish/Stockfish/pull/6117

Bench: 2294814
2025-06-02 22:05:11 +02:00
Shawn XuandJoost VandeVondele 259bdaaa9f Remove an unnecessary bound check
When failing high, it is always true that `alpha < beta` and `beta <=
bestValue`. Therefore if alpha and bestValue is not in decisive range, it is
guaranteed that beta is not.

closes https://github.com/official-stockfish/Stockfish/pull/6115

no functional change
2025-06-02 21:57:19 +02:00
Shawn XuandJoost VandeVondele c9af7674bc Introduce Secondary TT Aging
When a high-depth TT entry fail to produce a cutoff, decrease the stored depth
by 1. This is intended to help cases such as #5023
(https://github.com/official-stockfish/Stockfish/issues/5023#issuecomment-2814209391),
where entries with extremely high depths prevent TT cutoffs, contributing to
search explosions.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 128800 W: 33502 L: 33053 D: 62245
Ptnml(0-2): 331, 15084, 33149, 15477, 359
https://tests.stockfishchess.org/tests/view/683958e56ec7634154f9d2a9

Passed LTC:
LLR: 2.97 (-2.94,2.94) <0.50,2.50>
Total: 63288 W: 16376 L: 16005 D: 30907
Ptnml(0-2): 26, 6712, 17798, 7081, 27
https://tests.stockfishchess.org/tests/view/683aa4026ec7634154f9d469

closes https://github.com/official-stockfish/Stockfish/pull/6113

Bench: 2144705
2025-06-02 21:55:55 +02:00
Daniel MonroeandJoost VandeVondele 3747a19937 Simplify away depth condition in IIR
Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 359520 W: 92714 L: 92849 D: 173957
Ptnml(0-2): 977, 42640, 92614, 42599, 930
https://tests.stockfishchess.org/tests/view/6833705d6ec7634154f9c302

Passed simplification LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 201756 W: 51544 L: 51507 D: 98705
Ptnml(0-2): 89, 21965, 56728, 22012, 84
https://tests.stockfishchess.org/tests/view/68338e386ec7634154f9c790

Passed simplification VLTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 48558 W: 12675 L: 12492 D: 23391
Ptnml(0-2): 9, 4779, 14516, 4970, 5
https://tests.stockfishchess.org/tests/view/6838e0b26ec7634154f9d25b

closes https://github.com/official-stockfish/Stockfish/pull/6112

Bench: 2302583
2025-06-02 21:52:38 +02:00
Daniel MonroeandJoost VandeVondele 70ff5e3163 Simplify away cutoff term in prior countermove bonus
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 61120 W: 16010 L: 15819 D: 29291
Ptnml(0-2): 150, 7105, 15869, 7276, 160
https://tests.stockfishchess.org/tests/view/683560226ec7634154f9ce0f

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 225090 W: 57555 L: 57543 D: 109992
Ptnml(0-2): 104, 24367, 63603, 24355, 116
https://tests.stockfishchess.org/tests/view/6836420c6ec7634154f9cf5c

closes https://github.com/official-stockfish/Stockfish/pull/6111

Bench: 2472910
2025-06-02 21:49:43 +02:00
Daniel MonroeandJoost VandeVondele ddefd6eb6b Simplify away check term in statscore
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 61696 W: 16031 L: 15841 D: 29824
Ptnml(0-2): 151, 7160, 16046, 7330, 161
https://tests.stockfishchess.org/tests/view/68353fcc6ec7634154f9cdd5

Passed simplification LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 237990 W: 60994 L: 60995 D: 116001
Ptnml(0-2): 95, 25964, 66903, 25913, 120
https://tests.stockfishchess.org/tests/view/683642256ec7634154f9cf5e

closes https://github.com/official-stockfish/Stockfish/pull/6110

Bench: 2521003
2025-06-02 21:47:26 +02:00
Nonlinear2andJoost VandeVondele 8da3c2155a Simplify NMP eval in qsearch
Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/6834e9436ec7634154f9cd6e
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 24864 W: 6626 L: 6394 D: 11844
Ptnml(0-2): 62, 2806, 6477, 3012, 75

Passed non-regression LTC:
https://tests.stockfishchess.org/tests/view/683598fd6ec7634154f9ce82
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 200148 W: 51461 L: 51424 D: 97263
Ptnml(0-2): 92, 21672, 56503, 21721, 86

closes https://github.com/official-stockfish/Stockfish/pull/6109

Bench: 2316591
2025-06-02 21:44:29 +02:00
Carlos EsparzaandJoost VandeVondele 5695486db9 Fix outdated comment
closes https://github.com/official-stockfish/Stockfish/pull/6108

No functional change
2025-06-02 21:40:13 +02:00
Robert Nurnberg @ elitebookandJoost VandeVondele 9debc540e5 Fix clang-format version in CONTRIBUTING.md
closes https://github.com/official-stockfish/Stockfish/pull/6107

No functional change
2025-06-02 21:38:53 +02:00
Shawn XuandJoost VandeVondele d0212906bd Simplify stat eval history adjustment further
closes https://github.com/official-stockfish/Stockfish/pull/6106

bench 2074807
2025-06-02 21:37:37 +02:00
mstemberaandJoost VandeVondele 29b0c07ac8 Simplify Position::pieces()
closes https://github.com/official-stockfish/Stockfish/pull/6104

No functional change
2025-06-02 21:28:18 +02:00
mstemberaandJoost VandeVondele d27298d7dc Remove unused threatenedPieces
threatenedPieces is no longer used since #6023

Also can_move_king_or_pawn() can be const.
Also remove a couple of redundant declarations.

closes https://github.com/official-stockfish/Stockfish/pull/6101

No functional change
2025-06-02 21:24:38 +02:00
Shawn XuandJoost VandeVondele dc85c5a4c9 Remove nnz lookup table load optimization
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 63296 W: 16491 L: 16311 D: 30494
Ptnml(0-2): 129, 6624, 17972, 6784, 139
https://tests.stockfishchess.org/tests/view/6833ce486ec7634154f9cb22

Passed 2nd Non-regression STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 369568 W: 95314 L: 95451 D: 178803
Ptnml(0-2): 897, 40231, 102601, 40222, 833
https://tests.stockfishchess.org/tests/view/68355c956ec7634154f9ce07

closes https://github.com/official-stockfish/Stockfish/pull/6100

no functional change
2025-06-02 21:21:54 +02:00
Daniel MonroeandJoost VandeVondele 9fd40b9ea8 Simplify tt depth in stat eval history adjustment
Passed simplification STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 102208 W: 26498 L: 26349 D: 49361
Ptnml(0-2): 284, 12095, 26166, 12306, 253
https://tests.stockfishchess.org/tests/view/683354c76ec7634154f9be88

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 133422 W: 34050 L: 33945 D: 65427
Ptnml(0-2): 56, 14473, 37559, 14556, 67
https://tests.stockfishchess.org/tests/view/683363626ec7634154f9c298

closes https://github.com/official-stockfish/Stockfish/pull/6099

Bench: 2652411
2025-06-02 21:19:17 +02:00
Daniel MonroeandJoost VandeVondele dfa176fc7e Small tt verify simplification
Also fix probcut comment

Passed non-regression STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 69728 W: 18080 L: 17909 D: 33739
Ptnml(0-2): 161, 7157, 20044, 7354, 148
https://tests.stockfishchess.org/tests/view/68324b116ec7634154f9b478

closes https://github.com/official-stockfish/Stockfish/pull/6094

No functional change
2025-06-02 21:15:56 +02:00
FauziAkramandJoost VandeVondele 9b79b75c9b Enforce minimum compiler versions
gcc 9.3
clang 10

using unsupported compiler versions will generate an error,
older version might miscompile SF

CI: improves output on failed bench output

closes https://github.com/official-stockfish/Stockfish/pull/6032

No functional change
2025-06-02 21:09:19 +02:00
FauziAkramandJoost VandeVondele 73c55e8949 Simplify Double Margin Formula
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 267296 W: 69214 L: 69248 D: 128834
Ptnml(0-2): 760, 31511, 69141, 31475, 761
https://tests.stockfishchess.org/tests/view/682f5d9a6ec7634154f9b01e

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 67872 W: 17460 L: 17289 D: 33123
Ptnml(0-2): 25, 7238, 19243, 7401, 29
https://tests.stockfishchess.org/tests/view/6833074b6ec7634154f9b5ae

Passed VLTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 118000 W: 30337 L: 30222 D: 57441
Ptnml(0-2): 15, 11783, 35289, 11898, 15
https://tests.stockfishchess.org/tests/view/683336c56ec7634154f9ba46

closes https://github.com/official-stockfish/Stockfish/pull/6097

Bench: 2312696
2025-05-25 21:31:12 +02:00
ppigazziniandJoost VandeVondele e3adfaf8fc build & ci: update to NDK r27c API level 29
Update to the latest LTS version NDK r27c (27.2.12479018),
the previous NDK are unsupported by Google, see:
https://developer.android.com/ndk/downloads

A build with NDK r27c and API level < 29 returns this error:
"executable's TLS segment is underaligned: alignment is 8 (skew 0), needs to be at least 64 for ARM64 Bionic"

Update the API level to 29 to use the native ELF LTS and avoid the error:
https://android.googlesource.com/platform/bionic/+/HEAD/docs/elf-tls.md
https://android.googlesource.com/platform/bionic/+/HEAD/android-changes-for-ndk-developers.md#elf-tls-available-for-api-level-29

A dynamic link build of Stockfish uses these libraries:

ldd stockfish-android-armv8-dynamic-api35
        libm.so => /system/lib64/libm.so
        libdl.so => /system/lib64/libdl.so
        libc.so => /system/lib64/libc.so
        ld-android.so => /system/lib64/ld-android.so

ld-android.so : the dynamic linker used by Android (on Linux is named ld-linux.so),
                responsible for loading and linking shared libraries into an executable at runtime.
libdl.so      : interface/library layer that provides function for dynamic loading,
                relies on the underlying functionality provided by the dynamic linker
libm.so       : math library for Android
libc.so       : standard C library for Android

References:
Doc for native (C/C++) API
https://developer.android.com/ndk/guides/stable_apis

C libraries (libc, libm, libdl):
https://developer.android.com/ndk/guides/stable_apis#c_library

Bionic changes with API levels:
https://android.googlesource.com/platform/bionic/+/HEAD/docs/status.md

NDK r27c build system:
https://android.googlesource.com/platform/ndk/+/ndk-r27-release/docs/BuildSystemMaintainers.md

CI: Update to NDK r27c (27.2.12479018), the default version in GitHub runner,
to switch to a recent clang 18.

A PGO build requires static linking, because the NDK doesn't ship
the Android loaders (linker/linker64), see:
https://groups.google.com/g/android-ndk/c/3Ep6zD3xxSY

The API level should not be an issue when distributing a static build,
use the API 29, the oldest one not affected by the LTS alignement issue.

closes https://github.com/official-stockfish/Stockfish/pull/6081

No functional change
2025-05-25 21:28:53 +02:00
Кирилл ЗариповandJoost VandeVondele bebffc5622 Adjust futility pruning thresholds using history
Passed STC:
https://tests.stockfishchess.org/tests/view/6833095a6ec7634154f9b5b3
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 56896 W: 14946 L: 14604 D: 27346
Ptnml(0-2): 117, 6674, 14561, 6942, 154

Passed LTC:
https://tests.stockfishchess.org/tests/view/6833179d6ec7634154f9b5da
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 200742 W: 51660 L: 51012 D: 98070
Ptnml(0-2): 96, 21520, 56473, 22204, 78

Passed Non-regression SMP STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 29080 W: 7591 L: 7373 D: 14116
Ptnml(0-2): 38, 3178, 7881, 3414, 29
https://tests.stockfishchess.org/tests/view/6833689d6ec7634154f9c2ba

closes https://github.com/official-stockfish/Stockfish/pull/6092

Bench: 2305697
2025-05-25 21:24:09 +02:00
Shawn XuandJoost VandeVondele 00b1540e01 Always Decrease Reduction on TTMove
Passed VVLTC w/ LTC Bounds:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 57792 W: 15005 L: 14676 D: 28111
Ptnml(0-2): 2, 5241, 18082, 5568, 3
https://tests.stockfishchess.org/tests/view/682a0e3c6ec7634154f9a07e

Passed VVLTC w/ STC Bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 372298 W: 96342 L: 95655 D: 180301
Ptnml(0-2): 37, 34598, 116181, 35307, 26
https://tests.stockfishchess.org/tests/view/682a45b16ec7634154f9a3b3

STC Elo Estimate:
Elo: 0.15 ± 1.4 (95%) LOS: 58.3%
Total: 59612 W: 15414 L: 15388 D: 28810
Ptnml(0-2): 166, 6959, 15527, 6991, 163
nElo: 0.30 ± 2.8 (95%) PairsRatio: 1.00
https://tests.stockfishchess.org/tests/view/68335d276ec7634154f9c25c

closes https://github.com/official-stockfish/Stockfish/pull/6095

bench 2634355
2025-05-25 21:19:59 +02:00
Daniel SamekandJoost VandeVondele 805a2c1672 Simplify FutilityMoveCount
Inlined condition, instead of a function.

closes https://github.com/official-stockfish/Stockfish/pull/6096

no functional change
2025-05-25 21:12:27 +02:00
Кирилл ЗариповandJoost VandeVondele eb27d9420f Make ProbCut search shallower in cutNode
Passed STC:
https://tests.stockfishchess.org/tests/view/6832d2436ec7634154f9b4fc
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 455072 W: 118162 L: 117237 D: 219673
Ptnml(0-2): 1233, 53409, 117362, 54264, 1268

Passed LTC:
https://tests.stockfishchess.org/tests/view/6833323e6ec7634154f9ba17
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 128436 W: 32916 L: 32415 D: 63105
Ptnml(0-2): 50, 13737, 36137, 14250, 44

closes https://github.com/official-stockfish/Stockfish/pull/6093

Bench: 2232447
2025-05-25 21:05:53 +02:00
FauziAkramandJoost VandeVondele fe7b9b14d2 Implement smoother reduction in time management
Implement smoother time reduction in time management by replacing a conditional
assignment with a continuous sigmoid-based function. The updated logic employs
a sigmoid-like function for a more gradual adjustment.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 64448 W: 16838 L: 16492 D: 31118
Ptnml(0-2): 145, 7214, 17207, 7466, 192
https://tests.stockfishchess.org/tests/view/6829dc046ec7634154f99fba

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 407340 W: 104458 L: 103408 D: 199474
Ptnml(0-2): 196, 42281, 117664, 43335, 194
https://tests.stockfishchess.org/tests/view/6829fe1b6ec7634154f9a036

closes https://github.com/official-stockfish/Stockfish/pull/6091

No functional change
2025-05-25 21:03:16 +02:00
Shawn XuandJoost VandeVondele e6ec4705a8 Remove deprecated arch from codeql
closes https://github.com/official-stockfish/Stockfish/pull/6090

no functional change
2025-05-25 21:00:38 +02:00
b1b5893a8e Minor code improvements
- Remove / add empty lines
- fix the `ttcapture` comment
- remove the `bonus` variable for `ttMoveHistory`
- remove unnecessary parentheses / brackets
- refactor the movepick good quiet stage
- rename `endMoves` to `endCur`, as the previous name suggests that it points to the end of all generated moves, which it does not.

closes https://github.com/official-stockfish/Stockfish/pull/6089

No functional change.

Co-Authored-By: xu-shawn <50402888+xu-shawn@users.noreply.github.com>
2025-05-25 20:59:27 +02:00
pb00067andJoost VandeVondele f58d923fe0 Simplify & improve stalemate detection
Change is functional because now we verify for stalemate also on captures and when not giving check.

Green STC test on stalemate-book
https://tests.stockfishchess.org/tests/view/682d878f6ec7634154f9ad2f
Elo: 2.29 ± 1.3 (95%) LOS: 100.0%
Total: 10000 W: 4637 L: 4571 D: 792
Ptnml(0-2): 2, 132, 4664, 202, 0
nElo: 12.42 ± 6.8 (95%) PairsRatio: 1.51

Green LTC test on stalemate-book
https://tests.stockfishchess.org/tests/view/682daa2d6ec7634154f9ad67
Elo: 0.80 ± 0.8 (95%) LOS: 96.9%
Total: 10000 W: 4727 L: 4704 D: 569
Ptnml(0-2): 0, 64, 4849, 87, 0
nElo: 6.51 ± 6.8 (95%) PairsRatio: 1.36

Passed non-regression test @ LTC
https://tests.stockfishchess.org/tests/view/682dd10d6ec7634154f9adb3
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 148512 W: 38135 L: 38046 D: 72331
Ptnml(0-2): 55, 15759, 42558, 15810, 74

N.B.: The unique concern I have, is that due changes in future a negative SEE
capture see might be returned in GOOD_CAPTURE stage. In this case the assert in
can_move_king_or_pawn() will trigger since we must guarantee that all moves
(also quiets) are generated in movepicker when calling can_move_king_or_pawn().

closes https://github.com/official-stockfish/Stockfish/pull/6088

bench: 2178135
2025-05-25 20:55:49 +02:00
Shawn XuandJoost VandeVondele 472cc764be Move SIMD code to a separate header
Passed Non-regression STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 115328 W: 29903 L: 29777 D: 55648
Ptnml(0-2): 265, 12293, 32444, 12375, 287
https://tests.stockfishchess.org/tests/view/68300e086ec7634154f9b1d1

closes https://github.com/official-stockfish/Stockfish/pull/6086

no functional change
2025-05-25 20:52:57 +02:00
DisservinandJoost VandeVondele 2662d6bf35 Update clang-format to v20
closes https://github.com/official-stockfish/Stockfish/pull/6085

No functional change
2025-05-23 08:54:06 +02:00
DisservinandJoost VandeVondele c13c1d2c30 Silence "may be used uninitialized" GCC 15 warning
closes https://github.com/official-stockfish/Stockfish/pull/6083

No functional change
2025-05-23 08:53:00 +02:00
Daniel MonroeandJoost VandeVondele 4f021cab3b Simplify allNode term in prior countermove
Passed simplification STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 53632 W: 14008 L: 13805 D: 25819
Ptnml(0-2): 136, 6253, 13869, 6388, 170
https://tests.stockfishchess.org/tests/view/6828f2b26ec7634154f99b5e

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 82482 W: 21202 L: 21045 D: 40235
Ptnml(0-2): 37, 8986, 23052, 9115, 51
https://tests.stockfishchess.org/tests/view/6829010a6ec7634154f99db3

closes https://github.com/official-stockfish/Stockfish/pull/6068

Bench: 2302782
2025-05-23 08:51:54 +02:00
ppigazziniandJoost VandeVondele e03898b57c ci: add tests and artifacts for windows-11-arm
integrate armv8 and armv8-dotprod builds on windows-11-arm in ci, creating the corresponding artifacts.
Correct Makefile to drop warnings when providing a CXX, add MINGW ARM64 to get_native_properties.sh

fixes https://github.com/official-stockfish/Stockfish/issues/5640
closes https://github.com/official-stockfish/Stockfish/pull/6078

No functional change
2025-05-21 07:29:57 +02:00
Nonlinear2andJoost VandeVondele 54fb42ddf8 clean up code
**Non functional changes:**
in search.cpp:
- an unnecessary pair of parenthesis in the IIR condition has been removed.
- refactored the stalemate trap detection code

in movepick.cpp:
- use the variables `from`, `to`, `piece`, `pieceType` and `capturedPiece`  instead of calling the same functions multiple times in `MovePicker::score()`.
- rename `MovePicker::other_piece_types_mobile()`.

**Functional changes:**
- make sure the processed move is always legal in `MovePicker::other_piece_types_mobile()`.

passed non regression STC:
https://tests.stockfishchess.org/tests/view/6829da686ec7634154f99faf
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 95680 W: 24962 L: 24820 D: 45898
Ptnml(0-2): 221, 9622, 28025, 9738, 234

Passed non regression LTC:
https://tests.stockfishchess.org/tests/view/682a102c6ec7634154f9a086
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 117666 W: 30065 L: 29957 D: 57644
Ptnml(0-2): 45, 10173, 38291, 10277, 47

Run of 10k games on the stalemate opening book:
https://tests.stockfishchess.org/tests/view/682b114e6ec7634154f9aa2d
Elo: 0.76 ± 0.9 (95%) LOS: 95.3%
Total: 10000 W: 4637 L: 4615 D: 748
Ptnml(0-2): 0, 75, 4828, 97, 0
nElo: 5.83 ± 6.8 (95%) PairsRatio: 1.29

closes https://github.com/official-stockfish/Stockfish/pull/6080

Bench: 2422771
2025-05-21 07:25:40 +02:00
Shawn XuandJoost VandeVondele 347e328fdb Simplify TT Replacement Strategy
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 50528 W: 13160 L: 12958 D: 24410
Ptnml(0-2): 132, 5681, 13439, 5877, 135
https://tests.stockfishchess.org/tests/view/682a8b296ec7634154f9a785

Passed Non-regression STC w/ High Hash Pressure:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 30048 W: 7849 L: 7621 D: 14578
Ptnml(0-2): 75, 3390, 7884, 3582, 93
https://tests.stockfishchess.org/tests/view/682a9caf6ec7634154f9a7ae

Passed Non-regression LTC w/ High Hash Pressure:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 17610 W: 4584 L: 4362 D: 8664
Ptnml(0-2): 7, 1799, 4974, 2015, 10
https://tests.stockfishchess.org/tests/view/682ab3966ec7634154f9a8c8

closes https://github.com/official-stockfish/Stockfish/pull/6079

Bench: 2422771
2025-05-19 20:49:28 +02:00
Michael ChalyandJoost VandeVondele 56ea1fadf1 Tweak low ply history
Increase low ply history maximum ply by 1 and also allow to use it for check evasions scoring.

Pased STC:
https://tests.stockfishchess.org/tests/view/682a2db36ec7634154f9a358
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 66464 W: 17440 L: 17081 D: 31943
Ptnml(0-2): 191, 7717, 17056, 8078, 190

Passed LTC:
https://tests.stockfishchess.org/tests/view/682a3d406ec7634154f9a39c
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 384030 W: 98476 L: 97452 D: 188102
Ptnml(0-2): 180, 41564, 107522, 42550, 199

closes https://github.com/official-stockfish/Stockfish/pull/6075

Bench: 2422771
2025-05-19 20:47:34 +02:00
Daniel SamekandJoost VandeVondele ccfa651968 Remove full depth search reduction when cutNode
Passed STC-simplification bounds:
https://tests.stockfishchess.org/tests/view/6829dd6d6ec7634154f99fd3
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 67872 W: 17629 L: 17443 D: 32800
Ptnml(0-2): 167, 7988, 17451, 8152, 178

Passed LTC-simplification bounds:
https://tests.stockfishchess.org/tests/view/6829f2176ec7634154f9a01c
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 94818 W: 24328 L: 24184 D: 46306
Ptnml(0-2): 52, 10246, 26667, 10394, 50

closes https://github.com/official-stockfish/Stockfish/pull/6074

bench: 2245168
2025-05-19 20:44:14 +02:00
Shawn XuandJoost VandeVondele 0f102f3692 Simplify Quiet Early Move Penalty
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 185344 W: 47995 L: 47939 D: 89410
Ptnml(0-2): 527, 21898, 47754, 21978, 515
https://tests.stockfishchess.org/tests/view/682a47536ec7634154f9a3bc

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 101706 W: 26050 L: 25912 D: 49744
Ptnml(0-2): 53, 11056, 28499, 11190, 55
https://tests.stockfishchess.org/tests/view/682a61736ec7634154f9a50e

closes https://github.com/official-stockfish/Stockfish/pull/6072

Bench: 2012032
2025-05-19 07:45:28 +02:00
mstemberaandJoost VandeVondele 009632c465 Simplify handling of good/bad quiets
Simplify the handling of good/bad quiets and make it more similar to the way we
handle good/bad captures. The good quiet limit was adjusted from -7998 to
-14000 to keep the ratio of good/bad quiets about the same as master.  This
also fixes a "bug" that previously returned some bad quiets during the
GOOD_QUIET stage when some qood quiets weren't sorted at low depths.

STC https://tests.stockfishchess.org/tests/view/6827a68c6ec7634154f9992b
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 75936 W: 19722 L: 19547 D: 36667
Ptnml(0-2): 186, 8937, 19589, 9028, 228

LTC https://tests.stockfishchess.org/tests/view/6828f8096ec7634154f99b82
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 104112 W: 26773 L: 26638 D: 50701
Ptnml(0-2): 51, 11363, 29098, 11488, 56

closes https://github.com/official-stockfish/Stockfish/pull/6071

Bench: 2007023
2025-05-19 07:42:56 +02:00
Shawn XuandJoost VandeVondele 39942db3ff Simplify In-Check Statscore
Passed Non-regression STC:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 129760 W: 33701 L: 33580 D: 62479
Ptnml(0-2): 359, 15248, 33575, 15309, 389
https://tests.stockfishchess.org/tests/view/681a88193629b02d74b17123

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 519612 W: 132224 L: 132512 D: 254876
Ptnml(0-2): 246, 56823, 145960, 56527, 250
https://tests.stockfishchess.org/tests/view/681f9ed43629b02d74b177c3

closes https://github.com/official-stockfish/Stockfish/pull/6070

bench: 2046462
2025-05-19 07:39:56 +02:00
Daniel MonroeandJoost VandeVondele 6e9b5af0f0 Check evaluation after ttMove before doing a tt cut
Passed STC
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 239136 W: 62222 L: 61608 D: 115306
Ptnml(0-2): 675, 28046, 61525, 28634, 688
https://tests.stockfishchess.org/tests/view/681053293629b02d74b1668f

Passed LTC
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 448770 W: 115237 L: 114088 D: 219445
Ptnml(0-2): 177, 48128, 126619, 49291, 170
https://tests.stockfishchess.org/tests/view/681902de3629b02d74b16f6d

closes https://github.com/official-stockfish/Stockfish/pull/6069

Bench: 2035432
2025-05-19 07:37:15 +02:00
FauziAkramandJoost VandeVondele 4f76768fcf Remove a moveCount condition
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 70816 W: 18315 L: 18134 D: 34367
Ptnml(0-2): 210, 8213, 18360, 8436, 189
https://tests.stockfishchess.org/tests/view/68248197a527315e07cccb2d

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 121770 W: 31248 L: 31130 D: 59392
Ptnml(0-2): 61, 13338, 33995, 13404, 87
https://tests.stockfishchess.org/tests/view/68272ff46ec7634154f998ad

closes https://github.com/official-stockfish/Stockfish/pull/6067

bench: 2319161
2025-05-19 07:31:26 +02:00
MapikaandJoost VandeVondele 1b6975ac41 Add quiet move streak tracking to search stack
Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 109344 W: 28473 L: 28053 D: 52818
Ptnml(0-2): 320, 12756, 28085, 13206, 305
https://tests.stockfishchess.org/tests/view/6828c43e6ec7634154f99a10

Passed LTC:
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 76308 W: 19721 L: 19323 D: 37264
Ptnml(0-2): 39, 8145, 21386, 8547, 37
https://tests.stockfishchess.org/tests/view/6828f65a6ec7634154f99b72

closes https://github.com/official-stockfish/Stockfish/pull/6066

Bench: 2161814
2025-05-19 07:27:26 +02:00
Shawn XuandJoost VandeVondele 6f445631ab Simplify Futility Margin
Passed STC Non-regression:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 159008 W: 41500 L: 41414 D: 76094
Ptnml(0-2): 501, 18821, 40759, 18937, 486
https://tests.stockfishchess.org/tests/view/680ff9e23629b02d74b1663a

Passed LTC Non-regression:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 163572 W: 41617 L: 41543 D: 80412
Ptnml(0-2): 90, 17755, 46024, 17825, 92
https://tests.stockfishchess.org/tests/view/6814dd973629b02d74b16bac

closes https://github.com/official-stockfish/Stockfish/pull/6065

Bench: 2018775
2025-05-19 07:22:20 +02:00
Shawn XuandJoost VandeVondele e4b0f37493 Shrink Enum Sizes
Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 110848 W: 28974 L: 28564 D: 53310
Ptnml(0-2): 302, 12118, 30132, 12612, 260
https://tests.stockfishchess.org/tests/view/68242770a527315e07ccca38

closes https://github.com/official-stockfish/Stockfish/pull/6063

no functional change
2025-05-19 07:17:39 +02:00
Shawn XuandJoost VandeVondele 6b7e05f0c5 Simplify PCM TTMove Bonus
Passed Non-regression STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 114048 W: 29597 L: 29459 D: 54992
Ptnml(0-2): 315, 13619, 29045, 13703, 342
https://tests.stockfishchess.org/tests/view/681e83533629b02d74b17701

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 61014 W: 15582 L: 15405 D: 30027
Ptnml(0-2): 25, 6485, 17307, 6668, 22
https://tests.stockfishchess.org/tests/view/68226b523629b02d74b17b89

closes https://github.com/official-stockfish/Stockfish/pull/6061

bench 2016566
2025-05-19 07:13:39 +02:00
FauziAkramandJoost VandeVondele 07f6edf934 Refactor Position::pseudo_legal Pawn Move Check
use intermediate variables to make the statement easier to read

closes https://github.com/official-stockfish/Stockfish/pull/6045

No functional change
2025-05-19 07:12:11 +02:00
FauziAkramandDisservin c4e2479a75 Introducing a depth component to the penalty.
Passed STC:
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 31648 W: 8358 L: 8050 D: 15240
Ptnml(0-2): 78, 3596, 8182, 3876, 92
https://tests.stockfishchess.org/tests/view/680fa73d3629b02d74b165a9

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 177720 W: 45524 L: 44920 D: 87276
Ptnml(0-2): 91, 19130, 49813, 19736, 90
https://tests.stockfishchess.org/tests/view/68109e2c3629b02d74b166ee

closes https://github.com/official-stockfish/Stockfish/pull/6060

Bench: 2251724
2025-05-13 20:47:50 +02:00
Shawn XuandDisservin b5f11085dd Do more extensions in nodes far from the root
Parameters found by @FauziAkram in the latest tune.

Passed VVLTC w/ LTC Bound:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 243264 W: 63452 L: 62774 D: 117038
Ptnml(0-2): 18, 22494, 75934, 23164, 22
https://tests.stockfishchess.org/tests/view/680e82b23629b02d74b15e27

Passed VVLTC w/ STC Bound:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 37838 W: 9935 L: 9667 D: 18236
Ptnml(0-2): 6, 3383, 11873, 3651, 6
https://tests.stockfishchess.org/tests/view/681e707a3629b02d74b176e8

STC Elo Estimate:
Elo: -0.49 ± 2.4 (95%) LOS: 34.8%
Total: 20000 W: 5118 L: 5146 D: 9736
Ptnml(0-2): 55, 2365, 5182, 2349, 49
nElo: -0.96 ± 4.8 (95%) PairsRatio: 0.99
https://tests.stockfishchess.org/tests/view/6822af4b3629b02d74b17be6

closes https://github.com/official-stockfish/Stockfish/pull/6059

Bench: 2135382
2025-05-13 20:46:27 +02:00
Daniel MonroeandDisservin 1f9af9966f Simplify futility pruning term
Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 49920 W: 12933 L: 12727 D: 24260
Ptnml(0-2): 141, 5865, 12752, 6051, 151
https://tests.stockfishchess.org/tests/view/681d01d33629b02d74b1756e

Passed simplification LTC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 68808 W: 17573 L: 17402 D: 33833
Ptnml(0-2): 23, 7343, 19511, 7494, 33
https://tests.stockfishchess.org/tests/view/681e33843629b02d74b176b1

closes https://github.com/official-stockfish/Stockfish/pull/6058

Bench: 2041086
2025-05-13 20:42:36 +02:00
FauziAkramandDisservin 40ef7b1212 Simplify probcut
Passed STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 80800 W: 20947 L: 20774 D: 39079
Ptnml(0-2): 217, 9446, 20906, 9609, 222
https://tests.stockfishchess.org/tests/view/680e83163629b02d74b15e2a

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 359004 W: 91362 L: 91486 D: 176156
Ptnml(0-2): 177, 39133, 101007, 39007, 178
https://tests.stockfishchess.org/tests/view/680e95db3629b02d74b15e7a

closes https://github.com/official-stockfish/Stockfish/pull/6054

Bench: 2060860
2025-05-13 20:41:01 +02:00
gab8192andDisservin 05e39527a8 Simplify low-ply history weight
Passed Non-regression STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 116064 W: 30095 L: 29959 D: 56010
Ptnml(0-2): 333, 13753, 29731, 13875, 340
https://tests.stockfishchess.org/tests/view/681229f53629b02d74b168d3

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 172824 W: 44096 L: 44031 D: 84697
Ptnml(0-2): 91, 18868, 48410, 18971, 72
https://tests.stockfishchess.org/tests/view/681474243629b02d74b16b44

closes https://github.com/official-stockfish/Stockfish/pull/6053

Bench: 2064179
2025-05-13 20:40:26 +02:00
FauziAkramandDisservin d4b405a5a6 Remove a cutNode condition
Allow some nodes to spawn even deeper lmr searches, also for cutNodes

Passed STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 59072 W: 15387 L: 15190 D: 28495
Ptnml(0-2): 145, 6956, 15166, 7095, 174
https://tests.stockfishchess.org/tests/view/6810b4c13629b02d74b16714

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 93294 W: 23737 L: 23591 D: 45966
Ptnml(0-2): 47, 10116, 26169, 10274, 41
https://tests.stockfishchess.org/tests/view/681170613629b02d74b167f3

closes https://github.com/official-stockfish/Stockfish/pull/6052

Bench: 1937565
2025-05-13 20:39:23 +02:00
mstemberaandDisservin 63a2ab1510 Simplify Captures scoring
STC https://tests.stockfishchess.org/tests/view/680ee3b43629b02d74b160f3
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 200896 W: 52089 L: 52048 D: 96759
Ptnml(0-2): 592, 23821, 51589, 23846, 600

LTC https://tests.stockfishchess.org/tests/view/68106adf3629b02d74b166b1
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 128856 W: 33026 L: 32917 D: 62913
Ptnml(0-2): 54, 13916, 36384, 14015, 59

Replaces discovered checks by direct checks which are simpler and more
common.  It's also what's used for scoring quiets.

closes https://github.com/official-stockfish/Stockfish/pull/6047

Bench: 1886966
2025-05-13 20:39:02 +02:00
Shawn XuandDisservin e9925b122f Simplify ttCapture LMR
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 51104 W: 13389 L: 13184 D: 24531
Ptnml(0-2): 182, 5940, 13068, 6215, 147
https://tests.stockfishchess.org/tests/view/680ef2503629b02d74b16498

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 73350 W: 18804 L: 18638 D: 35908
Ptnml(0-2): 30, 7906, 20639, 8068, 32
https://tests.stockfishchess.org/tests/view/6810510e3629b02d74b1668a

closes https://github.com/official-stockfish/Stockfish/pull/6044

Bench: 1805151
2025-05-13 20:36:42 +02:00
mstemberaandDisservin 7afd9e859d Simplify MovePicker::score() by using piece types for clarity
STC https://tests.stockfishchess.org/tests/view/680ed6cc3629b02d74b160bf
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 200128 W: 51838 L: 51802 D: 96488
Ptnml(0-2): 457, 19956, 59247, 19902, 502

closes https://github.com/official-stockfish/Stockfish/pull/6039

No functional change
2025-05-13 20:34:40 +02:00
Michael ChalyandDisservin b73c8982df Another scaling revert
Revert recent passer because it seems to not scale for longer time
controls. Adjust comments accordingly.

Passed VVLTC SPRT with STC bounds:
https://tests.stockfishchess.org/tests/view/680ddff73629b02d74b15b3f
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 198316 W: 51317 L: 50846 D: 96153
Ptnml(0-2): 17, 18459, 61737, 18926, 19

Passed VVLTC SPRT with LTC bounds:
https://tests.stockfishchess.org/tests/view/680d5b7e3629b02d74b15a21
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 123274 W: 31738 L: 31283 D: 60253
Ptnml(0-2): 7, 11444, 38282, 11895, 9

closes https://github.com/official-stockfish/Stockfish/pull/6038

Bench: 2173845
2025-05-13 20:32:54 +02:00
Shawn XuandDisservin 81cc004060 Remove risk tolerance
Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 379328 W: 97567 L: 97724 D: 184037
Ptnml(0-2): 909, 44861, 98314, 44638, 942
https://tests.stockfishchess.org/tests/view/680defc63629b02d74b15b62

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 160752 W: 40762 L: 40685 D: 79305
Ptnml(0-2): 60, 17548, 45091, 17609, 68
https://tests.stockfishchess.org/tests/view/680e8ff43629b02d74b15e65

closes https://github.com/official-stockfish/Stockfish/pull/6037

Bench: 1897340
2025-05-13 20:31:37 +02:00
Carlos EsparzaandDisservin 63c6f22627 Simplify DirtyPiece
Simplifies the DirtyPiece struct.

passed STC: https://tests.stockfishchess.org/tests/view/68054c9798cd372e3aea05d7
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 36608 W: 9641 L: 9437 D: 17530
Ptnml(0-2): 89, 3630, 10668, 3822, 95

passed LTC: https://tests.stockfishchess.org/tests/view/6805514598cd372e3aea0783
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 127620 W: 31993 L: 31894 D: 63733
Ptnml(0-2): 42, 10983, 41665, 11074, 46

closes https://github.com/official-stockfish/Stockfish/pull/6016

No functional change
2025-05-13 20:30:22 +02:00
ed6b8d179a Refactor futility_margin
a small term to the futility calculation that depends on eval - beta.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 203136 W: 52797 L: 52239 D: 98100
Ptnml(0-2): 549, 23827, 52255, 24391, 546
https://tests.stockfishchess.org/tests/view/680e84a43629b02d74b15e2e

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 100356 W: 25950 L: 25507 D: 48899
Ptnml(0-2): 35, 10683, 28302, 11120, 38
https://tests.stockfishchess.org/tests/view/680ebcb03629b02d74b16040

closes https://github.com/official-stockfish/Stockfish/pull/6009
closes https://github.com/official-stockfish/Stockfish/pull/6041

Bench: 1815939

Co-authored-by: Michael Chaly <Vizvezdenec@gmail.com>
Co-authored-by: xu-shawn <50402888+xu-shawn@users.noreply.github.com>
2025-05-13 20:26:28 +02:00
pb00067andDisservin 0f905b4e88 Preserve all moves in movepicker
Simplifies method otherPieceTypesMobile quite a bit and makes it more
precise. More precise because capturesSearched list not always contains
all processed captures:
although extremely rare, it can happen that a 'good' capture get pruned
at step 14 and doesn't make it to capturesSearched. (functional change
at higher depths)

passed STC-simplification bounds
https://tests.stockfishchess.org/tests/view/68025974cd501869c6698153
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 273664 W: 15658 L: 15681 D: 242325
Ptnml(0-2): 166, 10368, 115802, 10315, 181

passed LTC-simplification bounds
https://tests.stockfishchess.org/tests/view/6804b86acd501869c6698673
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 96780 W: 24547 L: 24419 D: 47814
Ptnml(0-2): 30, 8466, 31286, 8562, 46

Applied changes requested by disservin & retested to be on the safe side

STC:
https://tests.stockfishchess.org/tests/view/6806110698cd372e3aea5919
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 107392 W: 27739 L: 27606 D: 52047
Ptnml(0-2): 266, 10867, 31306, 10982, 275

LTC:
https://tests.stockfishchess.org/tests/view/6806346198cd372e3aea5965
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 233484 W: 59106 L: 59103 D: 115275
Ptnml(0-2): 116, 22787, 70939, 22778, 122

closes https://github.com/official-stockfish/Stockfish/pull/6005

Bench: 1857323
2025-05-13 20:23:43 +02:00
FauziAkramandDisservin 94e6c0498f VVLTC Tune
Passed VVLTC with LTC bounds:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 12800 W: 3432 L: 3184 D: 6184
Ptnml(0-2): 1, 1098, 3954, 1346, 1
https://tests.stockfishchess.org/tests/view/680e255e3629b02d74b15d5e

Passed VVLTC with STC bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 14402 W: 3865 L: 3625 D: 6912
Ptnml(0-2): 0, 1236, 4490, 1474, 1
https://tests.stockfishchess.org/tests/view/680e4dfb3629b02d74b15da6

Passed VVLTC third test (removing an unrelated change):
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 25584 W: 6670 L: 6398 D: 12516
Ptnml(0-2): 4, 2290, 7932, 2562, 4
https://tests.stockfishchess.org/tests/view/680e74223629b02d74b15def

closes https://github.com/official-stockfish/Stockfish/pull/6036

Bench: 1857323
2025-04-27 20:47:47 +02:00
Daniel MonroeandDisservin af3692b2d0 Simplify second probcut to linear function of depth
Passed non-regression STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 57472 W: 14962 L: 14765 D: 27745
Ptnml(0-2): 140, 6715, 14817, 6936, 128
https://tests.stockfishchess.org/tests/view/680df1063629b02d74b15b69

Passed non-regression LTC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 88416 W: 22499 L: 22348 D: 43569
Ptnml(0-2): 31, 9565, 24874, 9698, 40
https://tests.stockfishchess.org/tests/view/680df3a93629b02d74b15b7d

closes https://github.com/official-stockfish/Stockfish/pull/6035

Bench: 1792000
2025-04-27 20:31:40 +02:00
Michael ChalyandDisservin 3e26d3acc7 Do more pruning in moves loop
Effectively reverts one commit from some months ago.

Passed VVLTC SPRT with STC bounds
https://tests.stockfishchess.org/tests/view/680d39373629b02d74b156d7
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 405058 W: 104843 L: 104111 D: 196104
Ptnml(0-2): 35, 38029, 125672, 38755, 38

Passed VVLTC SPRT with LTC bounds
https://tests.stockfishchess.org/tests/view/680d1a3b3629b02d74b1563d
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 57032 W: 14917 L: 14588 D: 27527
Ptnml(0-2): 6, 5202, 17768, 5537, 3

closes https://github.com/official-stockfish/Stockfish/pull/6031

Bench: 1643819
2025-04-27 20:28:22 +02:00
FauziAkramandDisservin 267fd8a3d5 Tweak History Bonus
Inspired by @Ergodice , who came up first with the idea.

Passed STC:
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 18400 W: 4867 L: 4576 D: 8957
Ptnml(0-2): 52, 2052, 4714, 2317, 65
https://tests.stockfishchess.org/tests/view/68062a3c98cd372e3aea5959

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 454338 W: 116461 L: 115294 D: 222583
Ptnml(0-2): 198, 49139, 127346, 50270, 216
https://tests.stockfishchess.org/tests/view/6806347c98cd372e3aea5967

Passed VLTC non-reg:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 385970 W: 98401 L: 98546 D: 189023
Ptnml(0-2): 51, 38958, 115105, 38827, 44
https://tests.stockfishchess.org/tests/view/680cfe873629b02d74b155cf

closes https://github.com/official-stockfish/Stockfish/pull/6030

Bench: 1715817
2025-04-27 20:14:38 +02:00
FauziAkramandDisservin e5aa4b48c6 Simplify Evasion Move Scoring
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 160256 W: 41432 L: 41348 D: 77476
Ptnml(0-2): 485, 19034, 41028, 19074, 507
https://tests.stockfishchess.org/tests/view/680d242c3629b02d74b15662

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 103086 W: 26388 L: 26252 D: 50446
Ptnml(0-2): 41, 11174, 28982, 11300, 46
https://tests.stockfishchess.org/tests/view/680d47f83629b02d74b1571e

closes https://github.com/official-stockfish/Stockfish/pull/6029

Bench: 1937261
2025-04-27 19:47:08 +02:00
FauziAkramandDisservin f98c178960 Improve quiet moves bonus
Inspired by an old test by Peregrine.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 453024 W: 117244 L: 116316 D: 219464
Ptnml(0-2): 1336, 53355, 116258, 54171, 1392
https://tests.stockfishchess.org/tests/view/680ccacc3629b02d74b15532

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 140550 W: 35990 L: 35462 D: 69098
Ptnml(0-2): 65, 15152, 39319, 15668, 71
https://tests.stockfishchess.org/tests/view/680d2ed73629b02d74b15691

closes https://github.com/official-stockfish/Stockfish/pull/6028

Bench: 1708152
2025-04-27 19:45:37 +02:00
MyselfandDisservin 37cc2293ef Replace complex probcut function with a precomputed table.
Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/680de2683629b02d74b15b46
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 116000 W: 29864 L: 29742 D: 56394
Ptnml(0-2): 215, 11811, 33854, 11877, 243

closes https://github.com/official-stockfish/Stockfish/pull/6026

No functional change
2025-04-27 19:45:00 +02:00
Shawn XuandDisservin b0a7a34d3f Simplify malus calculation
closes https://github.com/official-stockfish/Stockfish/pull/6024

No functional change
2025-04-27 19:43:47 +02:00
Carlos EsparzaandDisservin f0de8dc034 Simplify move ordering bonuses for putting piece en prise and escaping capture
Now there is also a penalty for exposing knights and bishops to capture by a pawn.

Passed STC:
https://tests.stockfishchess.org/tests/view/68074379878abf56f9a0d5b1
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 96512 W: 24841 L: 24687 D: 46984
Ptnml(0-2): 294, 11336, 24835, 11504, 287

Passed LTC:
https://tests.stockfishchess.org/tests/view/6808954a878abf56f9a0d76d
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 221328 W: 56271 L: 56255 D: 108802
Ptnml(0-2): 131, 24149, 62071, 24199, 114

closes https://github.com/official-stockfish/Stockfish/pull/6023

Bench: 1778227
2025-04-27 19:42:06 +02:00
Carlos EsparzaandDisservin fda269a299 Introduce double incremental accumulator updates
when we need to update an accumulator by two moves and the second move
captures the piece moved in the first move, we can skip computing the
middle accumulator and cancel a feature add with a feature remove to
save work.

Passed STC
https://tests.stockfishchess.org/tests/view/67f70b1c31d7cf8afdc45f51
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 72800 W: 18878 L: 18529 D: 35393
Ptnml(0-2): 160, 7711, 20374, 7930, 225

closes https://github.com/official-stockfish/Stockfish/pull/5988

No functional change
2025-04-27 19:27:18 +02:00
Shawn XuandDisservin 4e49f8dff9 Clean up search
* Correct IIR scaling comments
* Replace `(PvNode || cutNode)` with `!allNode`
* Consistent formatting for scaler tags
* Add comments to some recently-introduced LMR terms
* Add comments on PCM bonus tweaks

Passed Non-regression STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 389472 W: 102457 L: 102622 D: 184393
Ptnml(0-2): 1676, 41887, 107798, 41676, 1699
https://tests.stockfishchess.org/tests/view/67a0ea670774dfd78deb23cd

closes https://github.com/official-stockfish/Stockfish/pull/5854

Bench: 1585741
2025-04-27 19:26:02 +02:00
Michael ChalyandJoost VandeVondele 27428a61c2 Allow some nodes to spawn even deeper lmr searches
This includes nodes that were PvNode on a previous ply and only for non cutNodes and movecounts < 8.

Passed STC:
https://tests.stockfishchess.org/tests/view/680cdf2e3629b02d74b15576
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 123552 W: 31979 L: 31539 D: 60034
Ptnml(0-2): 322, 14449, 31803, 14871, 331

Passed LTC:
https://tests.stockfishchess.org/tests/view/680cf09a3629b02d74b15599
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 114306 W: 29310 L: 28837 D: 56159
Ptnml(0-2): 51, 12247, 32090, 12708, 57

closes https://github.com/official-stockfish/Stockfish/pull/6022

bench: 1585741
2025-04-26 22:40:41 +02:00
Michael ChalyandJoost VandeVondele 4b58079485 Simplify and cleanup futility pruning for child nodes
This patch removes (eval - beta) / 8 addition and adjusts constants
accordingly, also moves every calculation into futility_margin function.

Passed STC:
https://tests.stockfishchess.org/tests/view/6806d00f878abf56f9a0d524
LLR: 2.99 (-2.94,2.94) <-1.75,0.25>
Total: 483456 W: 124592 L: 124860 D: 234004
Ptnml(0-2): 1419, 57640, 123927, 57274, 1468

Passed LTC:
https://tests.stockfishchess.org/tests/view/680cceb33629b02d74b1554c
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 263868 W: 67076 L: 67104 D: 129688
Ptnml(0-2): 155, 28893, 73846, 28905, 135

closes https://github.com/official-stockfish/Stockfish/pull/6021

bench: 1618439
2025-04-26 22:38:14 +02:00
MyselfandJoost VandeVondele 7e6a0c464b Check only if good see.
Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 106400 W: 27863 L: 27444 D: 51093
Ptnml(0-2): 320, 12431, 27275, 12858, 316
https://tests.stockfishchess.org/tests/view/6806239498cd372e3aea5949

Passed LTC:
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 420534 W: 107594 L: 106494 D: 206446
Ptnml(0-2): 197, 45541, 117722, 46579, 228
https://tests.stockfishchess.org/tests/view/6806b4b3878abf56f9a0d4fc

closes https://github.com/official-stockfish/Stockfish/pull/6020

bench: 1675758
2025-04-26 22:33:49 +02:00
FauziAkramandJoost VandeVondele 5f32b3ed4b Remove unneeded return statement
closes https://github.com/official-stockfish/Stockfish/pull/6013

No functional change
2025-04-26 22:26:39 +02:00
breatnandJoost VandeVondele f590767b91 Adaptive beta cut
passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 197088 W: 51084 L: 50533 D: 95471
Ptnml(0-2): 547, 23201, 50577, 23592, 627
https://tests.stockfishchess.org/tests/view/680604d798cd372e3aea58fe

passed LTC
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 127950 W: 32719 L: 32214 D: 63017
Ptnml(0-2): 69, 13825, 35673, 14348, 60
https://tests.stockfishchess.org/tests/view/6805eae498cd372e3aea588a

closes https://github.com/official-stockfish/Stockfish/pull/6012

bench 1579003
2025-04-26 22:23:26 +02:00
FauziAkramandJoost VandeVondele 8b85290313 Remove manual stack alignment workaround for GCC < 9.3
consequently using these old compilers will now error out.

closes https://github.com/official-stockfish/Stockfish/pull/6008

No functional change.
2025-04-26 22:15:08 +02:00
Nonlinear2andJoost VandeVondele 0dcfe096d6 Increase full depth search reduction when cutNode
In addition to the core patch, improve the use of `isTTMove`:

- this name was used to mean both `bestMove == ttData.move` and `move == ttData.move`, so i replaced the argument `isTTMove` of `update_all_stats` with `TTMove` directly.

- `ttData.move == move` was  still used in some places instead of `ss->isTTMove`. I replaced these to be more consistent.

Passed STC:
https://tests.stockfishchess.org/tests/view/68057b8f98cd372e3aea3472
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 38400 W: 10048 L: 9734 D: 18618
Ptnml(0-2): 102, 4360, 9956, 4686, 96

Passed LTC:
https://tests.stockfishchess.org/tests/view/68057f7c98cd372e3aea3842
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 312666 W: 79494 L: 78616 D: 154556
Ptnml(0-2): 144, 33809, 87563, 34659, 158

closes https://github.com/official-stockfish/Stockfish/pull/6007

Bench: 1623376
2025-04-26 22:11:40 +02:00
Daniel MonroeandJoost VandeVondele 88a524c552 Tweak futility formula
Passed STC
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 248448 W: 64344 L: 63718 D: 120386
Ptnml(0-2): 750, 29172, 63783, 29740, 779
https://tests.stockfishchess.org/tests/view/68056f5598cd372e3aea2901

Passed LTC
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 118824 W: 30358 L: 29874 D: 58592
Ptnml(0-2): 59, 12797, 33228, 13257, 71
https://tests.stockfishchess.org/tests/view/6805675698cd372e3aea20d0

closes https://github.com/official-stockfish/Stockfish/pull/6004

bench 1839796
2025-04-26 22:06:18 +02:00
Stefan GeschwentnerandJoost VandeVondele 7988de4aa3 Prefer discovered and double checks in capture ordering.
Increase weight for captured piece value if also a discovered or double check.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 142144 W: 36632 L: 36164 D: 69348
Ptnml(0-2): 429, 16470, 36788, 16974, 411
https://tests.stockfishchess.org/tests/view/68052d7498cd372e3ae9faaa

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 668328 W: 170837 L: 169238 D: 328253
Ptnml(0-2): 308, 72010, 187990, 73487, 369
https://tests.stockfishchess.org/tests/view/68053c9398cd372e3aea043b

closes https://github.com/official-stockfish/Stockfish/pull/6003

Bench: 1636625
2025-04-26 22:01:34 +02:00
FauziAkramandJoost VandeVondele f6b0d53a99 Re-adding the 5th continuation history
the two simplifications (#5992 and  #5978) are strongly correlated.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 98016 W: 25415 L: 25008 D: 47593
Ptnml(0-2): 291, 11509, 25005, 11908, 295
https://tests.stockfishchess.org/tests/view/6802774fcd501869c6698168

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 588588 W: 150073 L: 148633 D: 289882
Ptnml(0-2): 281, 63615, 165078, 65023, 297
https://tests.stockfishchess.org/tests/view/6804cbdacd501869c66987dc

closes https://github.com/official-stockfish/Stockfish/pull/6002

Bench: 1783315
2025-04-26 21:58:46 +02:00
Michael ChalyandJoost VandeVondele 449a8b017e Do second step of shallower search
Specifically if lmr depth is not lower than new depth and value is really bad.

Passed STC:
https://tests.stockfishchess.org/tests/view/6805444898cd372e3aea0494
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 77664 W: 20136 L: 19762 D: 37766
Ptnml(0-2): 214, 9006, 20039, 9338, 235

Passed LTC:
https://tests.stockfishchess.org/tests/view/680549b298cd372e3aea05ac
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 115458 W: 29447 L: 28971 D: 57040
Ptnml(0-2): 62, 12357, 32421, 12821, 68

closes https://github.com/official-stockfish/Stockfish/pull/6001

bench: 1515501
2025-04-26 21:52:36 +02:00
Shawn XuandJoost VandeVondele 4176ad7b0a simplify risk tolerance
Passed Non-regression STC:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 73408 W: 19028 L: 18844 D: 35536
Ptnml(0-2): 201, 8709, 18743, 8807, 244
https://tests.stockfishchess.org/tests/view/68051f3698cd372e3ae9f63a

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 91236 W: 23193 L: 23045 D: 44998
Ptnml(0-2): 34, 9908, 25599, 10030, 47
https://tests.stockfishchess.org/tests/view/6805239498cd372e3ae9fa41

closes https://github.com/official-stockfish/Stockfish/pull/6000

bench 1864632
2025-04-26 21:49:38 +02:00
Shawn XuandJoost VandeVondele 16cd38dba1 Tweak TT Move Reduction by TT Move History
Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 62496 W: 16197 L: 15844 D: 30455
Ptnml(0-2): 200, 7234, 16011, 7619, 184
https://tests.stockfishchess.org/tests/view/67f7fa9b31d7cf8afdc4609c

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 400470 W: 102068 L: 101012 D: 197390
Ptnml(0-2): 201, 43207, 112347, 44295, 185
https://tests.stockfishchess.org/tests/view/67f927b0cd501869c66975e0

Passed VVLTC Non-regression:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 125394 W: 32408 L: 32304 D: 60682
Ptnml(0-2): 3, 11702, 39188, 11796, 8
https://tests.stockfishchess.org/tests/view/6804c215cd501869c66986b9

closes https://github.com/official-stockfish/Stockfish/pull/5998

bench 1760988
2025-04-26 21:43:11 +02:00
Shawn XuandJoost VandeVondele b915ed702a remove StateInfo::next
unused.

closes https://github.com/official-stockfish/Stockfish/pull/5997

no functional change
2025-04-26 21:37:17 +02:00
Joost VandeVondele f2507d0562 Add x86-64-avxvnni in CI
will result in the corresponding binaries being available for download

closes https://github.com/official-stockfish/Stockfish/pull/5990

No functional change
2025-04-26 21:34:26 +02:00
Shawn XuandJoost VandeVondele f273eea71f Remove non-functional accumulator reset
Passed Non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 219360 W: 56600 L: 56583 D: 106177
Ptnml(0-2): 582, 23419, 61620, 23518, 541
https://tests.stockfishchess.org/tests/view/67fad20dcd501869c669780f

closes https://github.com/official-stockfish/Stockfish/pull/5986

no functional change
2025-04-26 21:30:01 +02:00
Joost VandeVondele f9459e4c8e Use matching llvm-profdata
when using multiple clang compilers in parallel, it is necessary to use
a matching llvm-profdata, as the profile data format may change between
versions. To use the proper llvm-profdata binary, the one in the same directory
as the compiler is used. This allows for code like:

```bash
echo "Compiling clang"
for comp in clang++-11 clang++-12 clang++-13 clang++-14  clang++-15  clang++-16  clang++-17 clang++-18 clang++-19 clang++-20
do
   make -j profile-build CXX=$comp COMP=clang >& out.compile.$comp
   mv stockfish stockfish.$comp
done
```

after installing the required versions with the automatic installation script (https://apt.llvm.org/)

closes https://github.com/official-stockfish/Stockfish/pull/5958

No functional change
2025-04-26 21:25:18 +02:00
pb00067andDisservin d2d046c2a4 Improve stalemate detection during search
Currently SF’s quiescence search like most alpha-beta based engines
doesn’t verify for stalemate because doing it each leaf position is to
expensive and costs elo. However in certain positions this creates a
blindspot for SF, not recognizing soon enough that the opponent can
reach a stalemate by sacrifycing his last mobile heavy piece(s). This
tactical motif & it’s measure are similar to zugzwang & verification
search: the measure itself does not gain elo, but prevents SF from
loosing/drawing games in an awkward way.

The fix consists of 3 measures:

1. Make qsearch verify for stalemate on transitions to pure KP-material
   for the side to move with our last Rook/Queen just been captured. In
    fact this is the scenario where stalemate happens with highest
    frequency. The stalemate-verification itself is optimized by merely
    checking for pawn pushes & king mobility (captures were already
    tried by qssearch)

2. Another culprit for the issue figured out to be SEE based pruning for
   checks in step 14. Here often the move forcing the stalemate (or
   forcing the opponent to not retake) get pruned away and it need to
   much time to reach enough depth. To encounter this we verify
   following conditions:

-   a) side to move is happy with a draw (alpha < 0)
-   b) we are about to sacrify our last heavy & unique mobile piece in
    this position.
-   c) this piece doesn’t move away from our kingring giving the king a
    new square to move.
     When all 3 conditions meet we don’t prune the move, because there
     is a good chance that capturing the piece means stalemate.

3. Store terminal nodes (mates & stalemates) in TT with higher depth
   than searched depth. This prevents SF from:
- reanalyzing the node  (=trying to generate legal moves) in vain at
  each iterative deepening step.
- overwriting an already correct draw-evaluation from a previous shallow
  normal search by a qsearch which doesn’t recognize stalemate and might
  store a verry erratic evaluation.
 This is due to the 4 constant in the TT-overwrite condition: d -
 DEPTH_ENTRY_OFFSET + 2 * pv > depth8 – 4 which allows qs to override
 entries made by normal searches with depth <=4.
This 3hrd measure however is not essential for fixing the issue, but
tests (one of vdv & one  of mine) seem to suggest that this measure
brings some small benefit.

Another other position where SF benefits from this fix is for instance
Position FEN 8/8/8/1B6/6p1/8/3K1Ppp/3N2kr w - - 0 1 bm f4 +M9

P.S.: Also this issue higly depends on the used net, how good the net is
at evaluate such mobility restricted positions. SF16 was pretty good in
solving 2rr4/5pBk/PqP3p1/1N3pPp/1PQ1bP1P/8/3R4/R4K2 b - - 0 40 bm Rxc6
(< 1 second) while SF16_1 with introduction of the dual net needs about
1,5 minutes and SF17.1 requires 3 minutes to find the drawing move Rxc6.

P.S.2: Using more threads produces indeterminism & using high hash
pressure makes SF reevaluate explored positions more often which makes
it more likely to solve the position. To have stable meaningful results
I tested therfore with one single thread and low hash pressure.

Preliminary LTC test at 30k games
https://tests.stockfishchess.org/tests/view/67ece7a931d7cf8afdc44e18 Elo: 0.04 ± 2.0 (95%) LOS: 51.7%
Total: 24416 W: 6226 L: 6223 D: 11967
Ptnml(0-2): 12, 2497, 7185, 2504, 10
nElo: 0.09 ± 4.4 (95%) PairsRatio: 1.00

Passed LTC no-regression sprt
https://tests.stockfishchess.org/tests/view/67ee8e4631d7cf8afdc452fb
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 401556 W: 101612 L: 101776 D: 198168
Ptnml(0-2): 152, 42241, 116170, 42049, 166

closes https://github.com/official-stockfish/Stockfish/pull/5983
fixes https://github.com/official-stockfish/Stockfish/issues/5899

Bench: 1721673
2025-04-18 14:32:26 +02:00
FauziAkramandDisservin 44efbaddea Simplify bonusScale calculation
Allowing this specific term to potentially become negative for low depth
values is ok, because the overall `bonusScale` is explicitly ensured to
be non-negative a few lines later: bonusScale = std::max(bonusScale, 0);

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 164928 W: 42446 L: 42368 D: 80114
Ptnml(0-2): 497, 19551, 42306, 19597, 513
https://tests.stockfishchess.org/tests/view/67debf0b8888403457d8736c

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 234942 W: 59539 L: 59537 D: 115866
Ptnml(0-2): 113, 25639, 65964, 25643, 112
https://tests.stockfishchess.org/tests/view/67e2e1c48888403457d87768

closes https://github.com/official-stockfish/Stockfish/pull/5979

Bench: 1933843
2025-04-18 14:32:26 +02:00
Daniel MonroeandDisservin 3d18ad719b Skip 5th continuation history
Passed simplification STC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 70208 W: 18098 L: 17914 D: 34196
Ptnml(0-2): 199, 8300, 17907, 8514, 184
https://tests.stockfishchess.org/tests/view/67ed889b31d7cf8afdc451cb

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 485004 W: 122703 L: 122956 D: 239345
Ptnml(0-2): 288, 53162, 135805, 53009, 238
https://tests.stockfishchess.org/tests/view/67ee810231d7cf8afdc452ea

closes https://github.com/official-stockfish/Stockfish/pull/5992

Bench: 1715901
2025-04-18 14:32:26 +02:00
Daniel MonroeandDisservin 904a016396 Don't use 5th continuation history in move ordering
Passed simplification STC
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 136960 W: 35374 L: 35262 D: 66324
Ptnml(0-2): 420, 16214, 35049, 16428, 369
https://tests.stockfishchess.org/tests/view/67e6d0ae6682f97da2178ee5

Passed simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 233016 W: 59063 L: 59059 D: 114894
Ptnml(0-2): 113, 25430, 65421, 25428, 116
https://tests.stockfishchess.org/tests/view/67e9f7fb31d7cf8afdc44ad4

closes https://github.com/official-stockfish/Stockfish/pull/5978

Bench: 1842721
2025-04-18 14:32:26 +02:00
Guenther DemetzandDisservin 698c069bba Move node increment inside do_move function
Move node increment inside do_move function so that we can centralize
the increment into a single point.

closes https://github.com/official-stockfish/Stockfish/pull/5989

No functional change
2025-04-18 14:32:26 +02:00
FauziAkramandDisservin 5f8e67a544 Remove combineLast3 optimization
Passed non-reg STC 1st:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 67328 W: 17296 L: 17118 D: 32914
Ptnml(0-2): 158, 7095, 19011, 7211, 189
https://tests.stockfishchess.org/tests/view/67e6c2796682f97da2178ebe

Passed non-reg STC 2nd:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 92288 W: 23885 L: 23734 D: 44669
Ptnml(0-2): 213, 10039, 25518, 10132, 242
https://tests.stockfishchess.org/tests/view/67ed6a2d31d7cf8afdc45190

closes https://github.com/official-stockfish/Stockfish/pull/5975

Bench: 1875196
2025-04-18 14:32:26 +02:00
Shawn XuandDisservin 2b4926e091 Simplify TT Move History
Part 1 passed Non-regression STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 195552 W: 50394 L: 50349 D: 94809
Ptnml(0-2): 581, 23222, 50122, 23273, 578
https://tests.stockfishchess.org/tests/view/67eb6ea831d7cf8afdc44c30

Part 2 passed Non-regression STC:
LLR: 2.92 (-2.94,2.94) <-1.75,0.25>
Total: 181664 W: 46786 L: 46727 D: 88151
Ptnml(0-2): 517, 21403, 46974, 21380, 558
https://tests.stockfishchess.org/tests/view/67eb6f3331d7cf8afdc44c33

Passed Non-regression LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 155454 W: 39496 L: 39412 D: 76546
Ptnml(0-2): 77, 16950, 43580, 17052, 68
https://tests.stockfishchess.org/tests/view/67eee76531d7cf8afdc45358

Passed Non-regression VLTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 118266 W: 30263 L: 30148 D: 57855
Ptnml(0-2): 11, 11844, 35309, 11957, 12
https://tests.stockfishchess.org/tests/view/67f2414a31d7cf8afdc45760

closes https://github.com/official-stockfish/Stockfish/pull/5987

Bench: 1792850
2025-04-18 14:32:26 +02:00
mstemberaandDisservin 8d2eef2b1e Fix fused() all controll paths should return a value.
closes https://github.com/official-stockfish/Stockfish/pull/5973

No functional change
2025-04-18 14:32:26 +02:00
AliceRoseliaandDisservin 2af64d581b Update AUTHORS
closes https://github.com/official-stockfish/Stockfish/pull/5972

No functional change
2025-04-18 14:32:19 +02:00
FauziAkramandDisservin cf8b3637a0 Improve futility pruning
Adding a small term to the futility calculation that depends on eval - beta.
Refactored to a simpler form.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 74176 W: 19323 L: 18954 D: 35899
Ptnml(0-2): 226, 8576, 19117, 8941, 228
https://tests.stockfishchess.org/tests/view/67e6b0946682f97da2178eaf

Passed LTC:
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 135090 W: 34499 L: 33983 D: 66608
Ptnml(0-2): 79, 14403, 38040, 14969, 54
https://tests.stockfishchess.org/tests/view/67e757cc6682f97da2178f62

closes https://github.com/official-stockfish/Stockfish/pull/5970

Bench: 1875196
2025-04-18 14:32:19 +02:00
Disservin bb3eaf8def Add cstddef header
Fix missing header for std::size_t reported on discord.

closes https://github.com/official-stockfish/Stockfish/pull/5968

No functional change
2025-04-18 14:32:19 +02:00
mstemberaandDisservin 1577fa0470 Simplify Forward and Backward
Forward and Backward are not independent so simplify to a bool.

Cleanup some MSVC warnings like "warning C4267: 'argument': conversion
from 'size_t' to 'int', possible loss of data" Other minor formatting
stuff.

This is a rebase of https://github.com/official-stockfish/Stockfish/pull/5912

closes https://github.com/official-stockfish/Stockfish/pull/5967

No functional change
2025-04-18 14:32:19 +02:00
Daniel MonroeandDisservin fb6a3e04ec Simply use non_pawn_material rather than summing tuned terms
Passed simplification STC
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 136576 W: 35285 L: 35175 D: 66116
Ptnml(0-2): 410, 16179, 34997, 16295, 407
https://tests.stockfishchess.org/tests/view/67e5dc736682f97da2178da6

Passed rebased simplification LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 85482 W: 21812 L: 21658 D: 42012
Ptnml(0-2): 34, 9260, 24022, 9368, 57
https://tests.stockfishchess.org/tests/view/67e852cb31d7cf8afdc44966

closes https://github.com/official-stockfish/Stockfish/pull/5965

Bench: 2006483
2025-04-18 14:32:19 +02:00
Daniel SamekandDisservin 15f34560f2 Update AUTHORS
closes https://github.com/official-stockfish/Stockfish/pull/5964

No functional change
2025-04-18 14:32:13 +02:00
Disservin 7beff18ef0 Retire Acc Pointer
Since @xu-shawn's refactor the acc pointer has become a bit unnecessary,
we can achieve the same behavior by using the dimension size to get the
right accumulator from the `AccumulatorState`. I think the acc pointer
has become a bit of a burden required to be passed through multiple
different functions together with the necessary templating required.

Passed Non-Regression STC:
https://tests.stockfishchess.org/tests/view/67ed600f31d7cf8afdc45183
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 279744 W: 72037 L: 72082 D: 135625
Ptnml(0-2): 673, 29918, 78767, 29809, 705

closes https://github.com/official-stockfish/Stockfish/pull/5942

No functional change
2025-04-18 13:54:09 +02:00
Shawn XuandDisservin d7c04a9429 Introduce TT Move History Double Extensions
Passed VVLTC w/ STC bounds:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 74918 W: 19436 L: 19120 D: 36362
Ptnml(0-2): 6, 6890, 23354, 7200, 9
https://tests.stockfishchess.org/tests/view/67e4a1088888403457d878bb

Passed VVLTC w/ LTC bounds:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 111706 W: 29050 L: 28619 D: 54037
Ptnml(0-2): 13, 10218, 34959, 10651, 12
https://tests.stockfishchess.org/tests/view/67d6877c517865b4a2dfd36b

STC Elo Estimate:
Elo: 1.26 ± 2.0 (95%) LOS: 88.8%
Total: 30000 W: 7855 L: 7746 D: 14399
Ptnml(0-2): 104, 3531, 7630, 3622, 113
nElo: 2.44 ± 3.9 (95%) PairsRatio: 1.03
https://tests.stockfishchess.org/tests/view/67eacb8c31d7cf8afdc44b99

closes https://github.com/official-stockfish/Stockfish/pull/5961

Bench: 1887897
2025-04-02 17:53:05 +02:00
Daniel SamekandDisservin d942e13398 Less fail high cnt in the condition
Passed STC:
https://tests.stockfishchess.org/tests/view/67e027538888403457d87535
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 164000 W: 42535 L: 42034 D: 79431
Ptnml(0-2): 478, 19228, 42113, 19677, 504

Passed LTC:
https://tests.stockfishchess.org/tests/view/67e3c21b8888403457d87808
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 139176 W: 35500 L: 34975 D: 68701
Ptnml(0-2): 54, 15038, 38898, 15525, 73

closes https://github.com/official-stockfish/Stockfish/pull/5960

Bench: 1921404
2025-04-02 17:52:38 +02:00
Daniel MonroeandDisservin 3d61f932cb Squash out post-lmr bonus variable
Squash out bonus variable for post-lmr now that the bonus is constant.

closes https://github.com/official-stockfish/Stockfish/pull/5953

No functional change
2025-04-02 17:43:40 +02:00
mstemberaandDisservin ed89817f62 Various cleanups
Various simplifications, cleanups, consistancy improvements, and warning
mitigations.

Passed Non-Regression STC:
https://tests.stockfishchess.org/tests/view/67e7dd2d6682f97da2178fd8
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 386848 W: 99593 L: 99751 D: 187504
Ptnml(0-2): 1024, 41822, 107973, 41498, 1107

closes https://github.com/official-stockfish/Stockfish/pull/5948

No functional change
2025-04-02 17:43:14 +02:00
mstemberaandDisservin 1a395f1b56 Remove pawn_attacks_bb()
Generalize attacks_bb() to handle pawns and remove pawn_attacks_bb()

https://tests.stockfishchess.org/tests/view/67e9496231d7cf8afdc44a2e
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 134688 W: 34660 L: 34553 D: 65475
Ptnml(0-2): 298, 14619, 37462, 14608, 357

closes https://github.com/official-stockfish/Stockfish/pull/5947

No functional change
2025-04-02 17:39:40 +02:00
Shawn XuandDisservin d2cb927a04 Simplify TT cutoff conthist updates
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 86304 W: 22251 L: 22084 D: 41969
Ptnml(0-2): 250, 10214, 22123, 10249, 316
https://tests.stockfishchess.org/tests/view/67db60cd8c7f315cc372aae7

Passed Non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 199158 W: 50655 L: 50617 D: 97886
Ptnml(0-2): 103, 21579, 56182, 21607, 108
https://tests.stockfishchess.org/tests/view/67dcdc5b8c7f315cc372ac12

closes https://github.com/official-stockfish/Stockfish/pull/5945

Bench: 2069191
2025-04-02 17:37:02 +02:00
FauziAkramandDisservin dfef7e7520 Remove redundant assignment
closes https://github.com/official-stockfish/Stockfish/pull/5944

No functional change
2025-04-02 17:36:46 +02:00
Shawn XuandDisservin ee35a51c40 Remove extra division
closes https://github.com/official-stockfish/Stockfish/pull/5943

No functional change
2025-04-02 17:34:37 +02:00
Shawn XuandDisservin c2ff7a95c3 Cleanup fused updates
Passed Non-regression STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 70656 W: 18257 L: 18077 D: 34322
Ptnml(0-2): 217, 7912, 18879, 8114, 206
https://tests.stockfishchess.org/tests/view/67e23ae78888403457d876d4

closes https://github.com/official-stockfish/Stockfish/pull/5941

No functional change
2025-04-02 17:34:31 +02:00
Disservin 0475c8653f Restore development
closes https://github.com/official-stockfish/Stockfish/pull/5956

No functional change
2025-04-02 17:23:32 +02:00
Joost VandeVondeleandGitHub b4ac3d6b96 Merge pull request #5161 from Disservin/cluster
Merge SF master in the cluster branch
2024-04-10 22:18:16 +02:00
Disservin 6c19bec86e Merge branch 'master' into cluster 2024-04-10 18:46:26 +02:00
Joost VandeVondeleandGitHub 8c4ac26c8e Merge pull request #4661 from vondele/clusterMergeMaster17
Merge SF 16 in the cluster branch
2023-07-05 12:11:22 +02:00
Joost VandeVondele a2b24e0030 Merge branch 'sf16branch' into clusterMergeMaster17 2023-07-03 20:35:36 +02:00
Joost VandeVondeleandGitHub 66b4e7f080 Merge pull request #4349 from vondele/clusterMergeMaster16
Cluster: merge master up to SF15.1
2023-01-21 16:57:10 +01:00
Joost VandeVondele 04a0be956d Merge branch 'master' into clusterMergeMaster16 2022-12-04 16:43:04 +01:00
Joost VandeVondele f327096cfb Merge branch 'master' into clusterMergeMaster15
fix small merge conflicts, lightly tested:
Score of cluster vs master: 1 - 0 - 59  [0.508] 60
Elo difference: 5.8 +/- 11.2, LOS: 84.1 %, DrawRatio: 98.3 %
2022-10-30 16:12:47 +01:00
Joost VandeVondele e592dcb8e3 Merge branch 'master' into clusterMergeMaster14
closes https://github.com/official-stockfish/Stockfish/pull/3980
2022-04-16 08:31:47 +02:00
Joost VandeVondeleandGitHub 80eae02603 Merge pull request #3789 from vondele/fixNodeCount
[cluster] fix node count in bench command
2021-11-13 17:17:17 +01:00
Joost VandeVondele 0ad7de3182 [cluster] fix node count in bench command
bugfix use the cluster wide node count in bench for reporting

No function change
2021-11-13 17:14:27 +01:00
Joost VandeVondeleandGitHub e0b4dc24c8 Merge pull request #3771 from vondele/clusterMergeMaster13
Cluster: merge SF 14.1
2021-10-31 21:38:23 +01:00
Joost VandeVondele b79ec2e0b2 Merge branch 'master' into clusterMergeMaster13
brings the cluster branch to SF 14.1

Fixes minor conflicts

local testing cluster 4x4T vs master 4T, 10+0.1s, noob_3moves:

Score of cluster vs master: 6 - 0 - 94  [0.530] 100
Elo difference: 20.9 +/- 16.2, LOS: 99.3 %, DrawRatio: 94.0 %

No functional change
2021-10-31 16:17:24 +01:00
Stéphane Nicolet 43c887d367 Merge branch 'vondele-clusterMergeMaster12' into cluster 2021-03-17 11:22:02 +01:00
Joost VandeVondele 3a187b863b Merge branch 'master' into clusterMergeMaster12
fixes minor merge conflicts

looks good:

Score of cluster vs master: 15 - 1 - 134  [0.547] 150
Elo difference: 32.5 +/- 17.5, LOS: 100.0 %, DrawRatio: 89.3 %

for 4 threads against 4x4 threads at 10+0.1s
2021-03-16 20:49:19 +01:00
Joost VandeVondele 5fcd0e6f2a Merge branch 'master' into clusterMergeMaster11
fixes minor conflicts.
2020-09-17 19:17:37 +02:00
Joost VandeVondele 19129473f2 Merge NNUE (master) in the cluster branch.
fixes minor merge conflicts, and a first quick testing looks OK:

4mpix4th vs 4th at 30+0.3s:

Score of cluster vs master: 3 - 0 - 37  [0.537] 40
Elo difference: 26.1 +/- 28.5, LOS: 95.8 %, DrawRatio: 92.5 %

No functional change.
2020-08-19 23:30:37 +02:00
Joost VandeVondele b706b91bb1 Update cluster branch to latest master
Fixes a few merge conflicts.

Verified equal bench for 1 rank, and expected performance master vs cluster with 2 ranks.

Score of cluster vs master: 196 - 54 - 400  [0.609] 650
Elo difference: 77.1 +/- 16.3, LOS: 100.0 %, DrawRatio: 61.5 %

No functional change.
2020-06-28 13:04:21 +02:00
Joost VandeVondele 8ec5faa46e Merge branch 'master' into clusterMergeMaster8 2020-01-18 08:26:43 +01:00
Joost VandeVondele 8a9d269855 Merge remote-tracking branch 'upstream/master' into clusterMergeMaster7 2019-10-20 09:20:34 +02:00
Joost VandeVondele 0b3c13107a Merge branch 'master' into clusterMergeMaster6 2019-07-11 15:26:46 +02:00
Joost VandeVondele 669074672c Some doc changes. No functional change. 2019-07-05 14:47:33 +02:00
Joost VandeVondele 0fd0e4e849 Merge branch 'master' into clusterMergeMaster6 2019-07-01 16:36:58 +02:00
Joost VandeVondele 85327828c9 Merge branch 'master' into clusterMergeMaster5 2019-05-01 08:23:22 +02:00
Joost VandeVondele 4cdb6386d8 Merge branch 'master' into clusterMergeMaster5 2019-03-21 07:13:00 +01:00
Joost VandeVondele 982880bd70 Merge remote-tracking branch 'upstream/master' into clusterMergeMaster4 2019-02-17 13:18:08 +01:00
Joost VandeVondeleandStéphane Nicolet bf17a410ec [Cluster] Use a sendrecv ring instead of allgather
Using point to point instead of a collective improves performance, and might be more flexible for future improvements.
Also corrects the condition for the number elements required to fill the send buffer.

The actual Elo gains depends a bit on the setup used for testing.

8mpi x 32t yields 141 - 102 - 957 ~ 11 Elo
8mpi x 1t yields 70 +- 9 Elo.
2019-01-24 10:39:24 +01:00
Joost VandeVondeleandStéphane Nicolet 5e7777e9d0 [Cluster] adds missing line
one-liner fixes a merge error, resulting in a garbage output line. No influence on play.
2019-01-17 08:06:25 +01:00
Joost VandeVondeleandStéphane Nicolet 10a920d7d7 [cluster] Improve user documentation
- add cluster info line
- provides basic info on positions received/stored in a cluster run,
  useful to judge performance.
- document most cluster functionality in the readme.md

No functional change
2019-01-14 09:11:33 +01:00
Joost VandeVondele 21819b7bf8 Merge branch 'master' into clusterMergeMaster3 2019-01-09 21:52:30 +01:00
Joost VandeVondeleandStéphane Nicolet 8c4338ae49 [Cluster] Param tweak.
Small tweak of parameters, yielding some Elo.

The cluster branch can now be considered to be in good shape. In local testing, it runs stable for >30k games. Performance benefits from an MPI implementation that is able to make asynchronous progress. The code should be run with 1 MPI rank per node, and threaded on the node.

Performance against master has now been measured. Master has been given 1 node with 32 cores/threads in standard SMP, the cluster branch has been given N=2..20 of those nodes, running the corresponding number of MPI processes, each with 32 threads. Time control has been 10s+0.1s, Hash 8MB/core, the book 8moves_v3.pgn, the number of games 400.

```
Score of cluster-2mpix32t vs master-32t: 96 - 27 - 277  [0.586] 400
Elo difference: 60.54 +/- 18.49

Score of cluster-3mpix32t vs master-32t: 101 - 18 - 281  [0.604] 400
Elo difference: 73.16 +/- 17.94

Score of cluster-4mpix32t vs master-32t: 126 - 18 - 256  [0.635] 400
Elo difference: 96.19 +/- 19.68

Score of cluster-5mpix32t vs master-32t: 110 - 5 - 285  [0.631] 400
Elo difference: 93.39 +/- 17.09

Score of cluster-6mpix32t vs master-32t: 117 - 9 - 274  [0.635] 400
Elo difference: 96.19 +/- 18.06

Score of cluster-7mpix32t vs master-32t: 142 - 10 - 248  [0.665] 400
Elo difference: 119.11 +/- 19.89

Score of cluster-8mpix32t vs master-32t: 125 - 14 - 261  [0.639] 400
Elo difference: 99.01 +/- 19.18

Score of cluster-9mpix32t vs master-32t: 137 - 7 - 256  [0.662] 400
Elo difference: 117.16 +/- 19.20

Score of cluster-10mpix32t vs master-32t: 145 - 8 - 247  [0.671] 400
Elo difference: 124.01 +/- 19.86

Score of cluster-16mpix32t vs master-32t: 153 - 6 - 241  [0.684] 400
Elo difference: 133.95 +/- 20.17

Score of cluster-20mpix32t vs master-32t: 134 - 8 - 258  [0.657] 400
Elo difference: 113.29 +/- 19.11
```

As the cluster parallelism is essentially lazyMPI, the nodes per second has been verified to scale perfectly to large node counts. Unfortunately, that is not necessarily indicative of playing strength. In the following 2min search from startPos, we reach about 4.8Gnps (128 nodes).

```
info depth 38 seldepth 51 multipv 1 score cp 53 nodes 576165794092 nps 4801341606 hashfull 1000 tbhits 0 time 120001 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 d8d7 c2c4 b8c6 b1c3 g8f6 d2d4 d7g4 d4d5 c6d4 f3d4 g4d1 e1d1 c5d4 c3b5 a8c8 b2b3 a7a6 b5d4 f6e4 d1e2 g7g6 c1e3 f8g7 a1c1 e4c5 f2f3 f7f5 h1d1 e8g8 d4c2 c5d7 a2a4 a6a5 e3d4 f5f4 d4f2 f8f7 h2h3 d7c5
```
2019-01-06 15:38:31 +01:00
Joost VandeVondeleandStéphane Nicolet 8a3f8e21ae [Cluster] Move IO to the root.
Fixes one TODO, by moving the IO related to bestmove to the root, even if this move is found by a different rank.

This is needed to make sure IO from different ranks is ordered properly. If this is not done it is possible that e.g. a bestmove arrives before all info lines have been received, leading to output that confuses tools and humans alike (see e.g. https://github.com/cutechess/cutechess/issues/472)
2019-01-04 14:56:04 +01:00
Joost VandeVondeleandStéphane Nicolet 267ca781cd Always wait before posting the next call in _sync. 2019-01-02 11:16:24 +01:00
Joost VandeVondeleandStéphane Nicolet ac43bef5c5 [Cluster] Improve message passing part.
This rewrites in part the message passing part, using in place gather, and collecting, rather than merging, the data of all threads.

neutral with a single thread per rank:
Score of new-2mpi-1t vs old-2mpi-1t: 789 - 787 - 2615  [0.500] 4191
Elo difference: 0.17 +/- 6.44

likely progress with multiple threads per rank:
Score of new-2mpi-36t vs old-2mpi-36t: 76 - 53 - 471  [0.519] 600
Elo difference: 13.32 +/- 12.85
2019-01-02 11:16:24 +01:00
Joost VandeVondeleandStéphane Nicolet 7a32d26d5f [cluster] keep track of TB hits cluster-wide. 2018-12-29 15:34:57 +01:00
Joost VandeVondeleandStéphane Nicolet fb5c1f5bf5 Fix comment 2018-12-29 15:34:57 +01:00
Joost VandeVondeleandStéphane Nicolet 87f0fa55a0 [cluster] keep track of node counts cluster-wide.
This generalizes exchange of signals between the ranks using a non-blocking all-reduce. It is now used for the stop signal and the node count, but should be easily generalizable (TB hits, and ponder still missing). It avoids having long-lived outstanding non-blocking collectives (removes an early posted Ibarrier). A bit too short a test, but not worse than before:

Score of new-r4-1t vs old-r4-1t: 459 - 401 - 1505  [0.512] 2365
Elo difference: 8.52 +/- 8.43
2018-12-29 15:34:57 +01:00
Joost VandeVondeleandStéphane Nicolet 2f882309d5 fixup 2018-12-29 15:34:57 +01:00
Joost VandeVondeleandStéphane Nicolet 86953b9392 [cluster] Fix non-mpi compile
fix compile of the cluster branch in the non-mpi case.

Add a TODO as a reminder for the new voting scheme.

No functional changes
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet ba1c639836 [cluster] fill sendbuffer better
use a counter to track available elements.

Some elo gain, on 4 ranks:

Score of old-r4-1t vs new-r4-1t: 422 - 508 - 1694  [0.484] 2624
Elo difference: -11.39 +/- 7.90
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet e526c5aa52 [cluster] Make bench compatible
Fix one TODO.

Takes care of output from bench.
Sum nodes over ranks.
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 9cd2c817db Add one more TODO 2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 54a0a228f6 [cluster] Some formatting cleanup
standarize whitespace a bit.
Also adds two TODOs for follow up work.

No functional change.
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 1cd2c7861a [cluster] avoid creating MPI data type.
there is no need to make an MPI data type for the sendbuffer, simpler and faster.

No functional change
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 7af3f4da7a [cluster] Avoid TT saving our own TT entries.
avoid saving to TT the part of the receive buffer that actually originates from the same rank.

Now, on 1 mpi rank, we have the same bench as the non-mpi code on 1 thread.
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 271181bb31 [cluster] Add depth condition to cluster TT saves.
since the logic for saving moves in the sendbuffer and the associated rehashing is expensive, only do it for TT stores of sufficient depth.

quite some gain in local testing with 4 ranks against the previous version.
Elo difference: 288.84 +/- 21.98

This starts to make the branch useful, but for on-node runs, difference remains to the standard threading.
2018-12-29 15:34:56 +01:00
noobpwnftwandStéphane Nicolet 66b2c6b9f1 Implement best move voting system for cluster
This implements the cluster version of https://github.com/official-stockfish/Stockfish/commit/d96c1c32a2fa109e7cc6cd07f6029cd13977121e
2018-12-29 15:34:56 +01:00
Joost VandeVondeleandStéphane Nicolet 2559c20c6e [cluster] Fix oversight in TT key reuse
In the original code, the position key stored in the TT is used to probe&store TT entries after message passing. Since we only store part of the bits in the TT, this leads to incorrect rehashing. This is fixed in this patch storing also the full key in the send buffers, and using that for hashing after message arrival.

Short testing with 4 ranks (old vs new) shows this is effective:
Score of mpiold vs mpinew: 84 - 275 - 265  [0.347] 624
Elo difference: -109.87 +/- 20.88
2018-12-29 15:34:55 +01:00
Joost VandeVondeleandStéphane Nicolet 2659c407c4 Fix segfault.
the wrong data type was passed to an MPI call, leading to occasional segfaults. This patch fixes this.

No functional change.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 3730ae1efb Small simplifications and code cleanup
Non-functional simplifications.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 0d6cdc0c6d Implement yielding loop while waiting for input
Some MPI implementations use busy-wait pooling, which will turn MPI_Bcast into busy-wait loop, workaround with our own yielding loop.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 80afeb0d3b Fix consistency between PV and bestmove output
In case that a non-root mainThread on a node is the new best thread in the cluster, it should always output its PV.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 2405b38165 Fix search result aggregation
This reverts my earlier change that only the root node gets to output best move after fixing problem with MPI_Allreduce by our custom operator(BestMoveOp). This function is not commutable and we must ensure that its output is consistent among all nodes.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 8a95d269eb Implement proper stop signalling from root node
Previous behavior was to wait on all nodes to finish their search on their own TM and aggregate to root node via a blocking MPI_Allreduce call. This seems to be problematic.

In this commit a proper non-blocking signalling barrier was implemented to use TM from root node to control the cluster search, and disable TM on all non-root nodes.

Also includes some cosmetic fix to the nodes/NPS display.
2018-12-29 15:34:55 +01:00
noobpwnftwandStéphane Nicolet 3b7b632aa5 Fix a bug of outputting multiple lines of bestmove 2018-12-29 15:34:55 +01:00
Omri MorandStéphane Nicolet 29c166a072 MPI/Cluster implementation for Stockfish
Based on Peter Österlund's "Lazy Cluster" algorithm,
but with some simplifications.
To compile, point COMPCXX to the MPI C++ compiler wrapper (mpicxx).
2018-12-29 15:34:55 +01:00
85 changed files with 7310 additions and 2788 deletions
+2 -2
View File
@@ -9,14 +9,14 @@ AllowAllParametersOfDeclarationOnNextLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes
BreakTemplateDeclarations: Yes
BasedOnStyle: WebKit
BitFieldColonSpacing: After
BinPackParameters: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: false
AfterFunction: false
AfterClass: false
AfterControlStatement: true
BeforeElse: true
+6 -6
View File
@@ -4,7 +4,7 @@
"name": "Android NDK aarch64",
"os": "ubuntu-22.04",
"simple_name": "android",
"compiler": "aarch64-linux-android21-clang++",
"compiler": "aarch64-linux-android29-clang++",
"emu": "qemu-aarch64",
"comp": "ndk",
"shell": "bash",
@@ -14,7 +14,7 @@
"name": "Android NDK arm",
"os": "ubuntu-22.04",
"simple_name": "android",
"compiler": "armv7a-linux-androideabi21-clang++",
"compiler": "armv7a-linux-androideabi29-clang++",
"emu": "qemu-arm",
"comp": "ndk",
"shell": "bash",
@@ -26,25 +26,25 @@
{
"binaries": "armv8-dotprod",
"config": {
"compiler": "armv7a-linux-androideabi21-clang++"
"compiler": "armv7a-linux-androideabi29-clang++"
}
},
{
"binaries": "armv8",
"config": {
"compiler": "armv7a-linux-androideabi21-clang++"
"compiler": "armv7a-linux-androideabi29-clang++"
}
},
{
"binaries": "armv7",
"config": {
"compiler": "aarch64-linux-android21-clang++"
"compiler": "aarch64-linux-android29-clang++"
}
},
{
"binaries": "armv7-neon",
"config": {
"compiler": "aarch64-linux-android21-clang++"
"compiler": "aarch64-linux-android29-clang++"
}
}
]
+165 -43
View File
@@ -8,11 +8,11 @@
"comp": "gcc",
"shell": "bash",
"archive_ext": "tar",
"sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future --"
"sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.33.0-2024-01-07-lin/sde -future --"
},
{
"name": "MacOS 13 Apple Clang",
"os": "macos-13",
"name": "macOS 15 Apple Clang",
"os": "macos-15-intel",
"simple_name": "macos",
"compiler": "clang++",
"comp": "clang",
@@ -20,8 +20,8 @@
"archive_ext": "tar"
},
{
"name": "MacOS 14 Apple Clang M1",
"os": "macos-14",
"name": "macOS 15 Apple Clang M1",
"os": "macos-15",
"simple_name": "macos-m1",
"compiler": "clang++",
"comp": "clang",
@@ -38,7 +38,19 @@
"msys_env": "x86_64-gcc",
"shell": "msys2 {0}",
"ext": ".exe",
"sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --",
"sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.33.0-2024-01-07-win/sde.exe -future --",
"archive_ext": "zip"
},
{
"name": "Windows 11 Mingw-w64 Clang arm64",
"os": "windows-11-arm",
"simple_name": "windows",
"compiler": "clang++",
"comp": "clang",
"msys_sys": "clangarm64",
"msys_env": "clang-aarch64-clang",
"shell": "msys2 {0}",
"ext": ".exe",
"archive_ext": "zip"
}
],
@@ -49,93 +61,131 @@
"x86-64-bmi2",
"x86-64-avxvnni",
"x86-64-avx512",
"x86-64-vnni256",
"x86-64-vnni512",
"apple-silicon"
"x86-64-avx512icl",
"apple-silicon",
"armv8",
"armv8-dotprod"
],
"exclude": [
{
"binaries": "x86-64",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-sse41-popcnt",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-avx2",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-bmi2",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-avx512",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-vnni256",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-vnni512",
"config": {
"os": "macos-14"
"os": "macos-15"
}
},
{
"binaries": "x86-64-avx512icl",
"config": {
"os": "macos-15"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"ubuntu-22.04": null
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-13"
"os": "macos-15-intel"
}
},
{
"binaries": "x86-64-avx512",
"config": {
"os": "macos-13"
}
},
{
"binaries": "x86-64-vnni256",
"config": {
"os": "macos-13"
"os": "macos-15-intel"
}
},
{
"binaries": "x86-64-vnni512",
"config": {
"os": "macos-13"
"os": "macos-15-intel"
}
},
{
"binaries": "x86-64-avx512icl",
"config": {
"os": "macos-15-intel"
}
},
{
"binaries": "x86-64",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-sse41-popcnt",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-avx2",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-bmi2",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-avx512",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-vnni512",
"config": {
"os": "windows-11-arm"
}
},
{
"binaries": "x86-64-avx512icl",
"config": {
"os": "windows-11-arm"
}
},
{
@@ -147,7 +197,13 @@
{
"binaries": "apple-silicon",
"config": {
"os": "macos-13"
"os": "windows-11-arm"
}
},
{
"binaries": "apple-silicon",
"config": {
"os": "ubuntu-20.04"
}
},
{
@@ -155,6 +211,72 @@
"config": {
"os": "ubuntu-22.04"
}
},
{
"binaries": "apple-silicon",
"config": {
"os": "macos-15-intel"
}
},
{
"binaries": "armv8",
"config": {
"os": "windows-2022"
}
},
{
"binaries": "armv8",
"config": {
"os": "ubuntu-20.04"
}
},
{
"binaries": "armv8",
"config": {
"os": "ubuntu-22.04"
}
},
{
"binaries": "armv8",
"config": {
"os": "macos-15-intel"
}
},
{
"binaries": "armv8",
"config": {
"os": "macos-15"
}
},
{
"binaries": "armv8-dotprod",
"config": {
"os": "windows-2022"
}
},
{
"binaries": "armv8-dotprod",
"config": {
"os": "ubuntu-20.04"
}
},
{
"binaries": "armv8-dotprod",
"config": {
"os": "ubuntu-22.04"
}
},
{
"binaries": "armv8-dotprod",
"config": {
"os": "macos-15-intel"
}
},
{
"binaries": "armv8-dotprod",
"config": {
"os": "macos-15"
}
}
]
}
+3 -3
View File
@@ -38,7 +38,7 @@ jobs:
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
NDKV="27.2.12479018"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
@@ -80,9 +80,9 @@ jobs:
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU
make -j4 profile-build ARCH=$BINARY COMP=$COMP RUN_PREFIX=$EMU
make strip ARCH=$BINARY COMP=$COMP
WINE_PATH=$EMU ../tests/signature.sh $benchref
RUN_PREFIX=$EMU ../tests/signature.sh $benchref
mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT
- name: Remove non src files
+4 -4
View File
@@ -25,11 +25,11 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Run clang-format style check
uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # @v4.15.0
id: clang-format
continue-on-error: true
with:
clang-format-version: "18"
clang-format-version: "20"
exclude-regex: "incbin"
- name: Comment on PR
@@ -37,9 +37,9 @@ jobs:
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0
with:
message: |
clang-format 18 needs to be run on this PR.
clang-format 20 needs to be run on this PR.
If you do not have clang-format installed, the maintainer will run it when merging.
For the exact version please see https://packages.ubuntu.com/noble/clang-format-18.
For the exact version please see https://packages.ubuntu.com/plucky/clang-format-20.
_(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_
comment_tag: execution
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
- name: Build
working-directory: src
run: make -j build ARCH=x86-64-modern
run: make -j build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+11 -11
View File
@@ -43,10 +43,10 @@ jobs:
- name: Download SDE package
if: runner.os == 'Linux' || runner.os == 'Windows'
uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3
uses: petarpetrovt/setup-sde@f0fa5971dc275704531e94264dd23250c442aa41 # @v2.4
with:
environmentVariableName: SDE_DIR
sdeVersion: 9.27.0
sdeVersion: 9.33.0
- name: Download the used network from the fishtest framework
run: make net
@@ -63,22 +63,22 @@ jobs:
- name: Check compiler
run: $COMPCXX -v
- name: Show g++ cpu info
if: runner.os != 'macOS'
run: g++ -Q -march=native --help=target
- name: Show clang++ cpu info
if: runner.os == 'macOS'
run: clang++ -E - -march=native -###
- name: Show compiler cpu info
run: |
if [[ "$COMPCXX" == clang* ]]; then
$COMPCXX -E - -march=native -###
else
$COMPCXX -Q -march=native --help=target
fi
# x86-64 with newer extensions tests
- name: Compile ${{ matrix.config.binaries }} build
run: |
make clean
make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE"
make -j4 profile-build ARCH=$BINARY COMP=$COMP RUN_PREFIX="$SDE"
make strip ARCH=$BINARY COMP=$COMP
WINE_PATH="$SDE" ../tests/signature.sh $benchref
RUN_PREFIX="$SDE" ../tests/signature.sh $benchref
mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT
- name: Remove non src files
+30 -11
View File
@@ -24,7 +24,7 @@ jobs:
with:
repository: vondele/matetrack
path: matetrack
ref: 4f8a80860ed8f3607f05a9195df8b40203bdc360
ref: 2d96fa3373f90edb032b7ea7468473fb9e6f0343
persist-credentials: false
- name: matetrack install deps
@@ -47,25 +47,44 @@ jobs:
wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-wdl/
wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-dtz/
- name: Run matetrack
- name: Run matetrack th1
working-directory: matetrack
run: |
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out
! grep "issues were detected" matecheckout.out > /dev/null
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout1.out
! grep "issues were detected" matecheckout1.out > /dev/null
- name: Run matetrack with --syzygy50MoveRule false
- name: Run matetrack th4
working-directory: matetrack
run: |
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 --threads 4 | tee matecheckout4.out
! grep "issues were detected" matecheckout4.out > /dev/null
- name: Run matetrack th1 with --syzygy50MoveRule false
working-directory: matetrack
run: |
grep 5men cursed.epd > cursed5.epd
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile cursed5.epd --nodes 100000 --syzygy50MoveRule false | tee matecheckcursed.out
! grep "issues were detected" matecheckcursed.out > /dev/null
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile cursed5.epd --nodes 100000 --syzygy50MoveRule false | tee matecheckcursed1.out
! grep "issues were detected" matecheckcursed1.out > /dev/null
- name: Verify mate and TB win count for matecheckcursed.out
- name: Run matetrack th4 with --syzygy50MoveRule false
working-directory: matetrack
run: |
mates=$(grep "Found mates:" matecheckcursed.out | awk '{print $3}')
tbwins=$(grep "Found TB wins:" matecheckcursed.out | awk '{print $4}')
grep 5men cursed.epd > cursed5.epd
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile cursed5.epd --nodes 100000 --threads 4 --syzygy50MoveRule false | tee matecheckcursed4.out
! grep "issues were detected" matecheckcursed4.out > /dev/null
- name: Verify mate and TB win count for matecheckcursed[14].out
working-directory: matetrack
run: |
mates=$(grep "Found mates:" matecheckcursed1.out | awk '{print $3}')
tbwins=$(grep "Found TB wins:" matecheckcursed1.out | awk '{print $4}')
if [ $(($mates + $tbwins)) -ne 32 ]; then
echo "Sum of mates and TB wins is not 32 in matecheckcursed.out" >&2
echo "Sum of mates and TB wins is not 32 in matecheckcursed1.out" >&2
exit 1
fi
mates=$(grep "Found mates:" matecheckcursed4.out | awk '{print $3}')
tbwins=$(grep "Found TB wins:" matecheckcursed4.out | awk '{print $4}')
if [ $(($mates + $tbwins)) -ne 32 ]; then
echo "Sum of mates and TB wins is not 32 in matecheckcursed4.out" >&2
exit 1
fi
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
- name: Run with glibcxx assertions
make_option: ""
cxx_extra_flags: -D_GLIBCXX_ASSERTIONS
instrumented_option: non
instrumented_option: none
defaults:
run:
working-directory: src
+34 -21
View File
@@ -29,24 +29,25 @@ jobs:
shell: bash
- name: Android NDK aarch64
os: ubuntu-22.04
compiler: aarch64-linux-android21-clang++
compiler: aarch64-linux-android29-clang++
comp: ndk
run_armv8_tests: true
shell: bash
- name: Android NDK arm
os: ubuntu-22.04
compiler: armv7a-linux-androideabi21-clang++
compiler: armv7a-linux-androideabi29-clang++
comp: ndk
run_armv7_tests: true
shell: bash
- name: Linux GCC riscv64
os: ubuntu-22.04
compiler: g++
comp: gcc
run_riscv64_tests: true
base_image: "riscv64/alpine:edge"
platform: linux/riscv64
shell: bash
# Currently segfaults in the CI unrelated to a Stockfish change.
# - name: Linux GCC riscv64
# os: ubuntu-22.04
# compiler: g++
# comp: gcc
# run_riscv64_tests: true
# base_image: "riscv64/alpine:edge"
# platform: linux/riscv64
# shell: bash
- name: Linux GCC ppc64
os: ubuntu-22.04
compiler: g++
@@ -55,21 +56,21 @@ jobs:
base_image: "ppc64le/alpine:latest"
platform: linux/ppc64le
shell: bash
- name: MacOS 13 Apple Clang
os: macos-13
- name: macOS 15 Apple Clang
os: macos-15-intel
compiler: clang++
comp: clang
run_64bit_tests: true
shell: bash
- name: MacOS 14 Apple Clang M1
os: macos-14
- name: macOS 15 Apple Clang M1
os: macos-15
compiler: clang++
comp: clang
run_64bit_tests: false
run_m1_tests: true
shell: bash
- name: MacOS 13 GCC 11
os: macos-13
- name: macOS 15 GCC 11
os: macos-15-intel
compiler: g++-11
comp: gcc
run_64bit_tests: true
@@ -98,6 +99,14 @@ jobs:
msys_sys: clang64
msys_env: clang-x86_64-clang
shell: msys2 {0}
- name: Windows 11 Mingw-w64 Clang arm64
os: windows-11-arm
compiler: clang++
comp: clang
run_armv8_tests: true
msys_sys: clangarm64
msys_env: clang-aarch64-clang
shell: msys2 {0}
defaults:
run:
working-directory: src
@@ -118,7 +127,7 @@ jobs:
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
NDKV="27.2.12479018"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
@@ -302,8 +311,10 @@ jobs:
- name: Test armv8 build
if: matrix.config.run_armv8_tests
run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j4 ARCH=armv8 build
../tests/signature.sh $benchref
@@ -311,8 +322,10 @@ jobs:
- name: Test armv8-dotprod build
if: matrix.config.run_armv8_tests
run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j4 ARCH=armv8-dotprod build
../tests/signature.sh $benchref
+4 -14
View File
@@ -12,20 +12,17 @@ on:
jobs:
Artifacts:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
runs-on: ubuntu-latest
env:
COMPCXX: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }}
NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }}
SDE: ${{ matrix.config.sde }}
strategy:
fail-fast: false
matrix: ${{ fromJson(inputs.matrix) }}
defaults:
run:
shell: ${{ matrix.config.shell }}
shell: bash
steps:
- uses: actions/checkout@v4
with:
@@ -37,13 +34,6 @@ jobs:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Create Package
run: |
mkdir stockfish
@@ -69,13 +59,13 @@ jobs:
cp CONTRIBUTING.md ../stockfish/
- name: Create tar
if: runner.os != 'Windows'
if: ${{ !startsWith(matrix.config.os, 'windows') }}
run: |
chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT
tar -cvf stockfish-$NAME-$BINARY.tar stockfish
- name: Create zip
if: runner.os == 'Windows'
if: ${{ startsWith(matrix.config.os, 'windows') }}
run: |
zip -r stockfish-$NAME-$BINARY.zip stockfish
+20 -1
View File
@@ -10,6 +10,7 @@ Motohiro Isozaki (yaneurao)
Hisayori Noda (nodchip)
# All other authors of Stockfish code (in alphabetical order)
87flowers
Aditya (absimaldata)
Adrian Petrescu (apetresc)
Ahmed Kerimov (wcdbmv)
@@ -20,6 +21,7 @@ Alexander Kure
Alexander Pagel (Lolligerhans)
Alfredo Menezes (lonfom169)
Ali AlZhrani (Cooffe)
AliceRoselia
Andreas Jan van der Meulen (Andyson007)
Andreas Matthies (Matthies)
Andrei Vetrov (proukornew)
@@ -29,10 +31,13 @@ Andy Duplain
Antoine Champion (antoinechampion)
Aram Tumanian (atumanian)
Arjun Temurnikar
Aron Petkovski (fury)
Arseniy Surkov (codedeliveryservice)
Artem Solopiy (EntityFX)
Auguste Pop
Balazs Szilagyi
Balint Pfliegel
Baptiste Rech (breatn)
Ben Chaney (Chaneybenjamini)
Ben Koshy (BKSpurgeon)
Bill Henry (VoyagerOne)
@@ -53,10 +58,12 @@ Ciekce
clefrks
Clemens L. (rn5f107s2)
Cody Ho (aesrentai)
CSTENTOR
Dale Weiler (graphitemaster)
Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic)
Daniel Monroe (Ergodice)
Daniel Monroe (daniel-monroe)
Daniel Samek (DanSamek)
Dan Schmidt (dfannius)
Dariusz Orzechowski (dorzechowski)
David (dav1312)
@@ -119,16 +126,21 @@ Jonathan McDermid (jonathanmcdermid)
Joost VandeVondele (vondele)
Joseph Ellis (jhellis3)
Joseph R. Prostko
Jost Triller (tsoj)
Jörg Oster (joergoster)
Julian Willemer (NightlyKing)
jundery
Justin Blanchard (UncombedCoconut)
Kazuki Yamashita (KazApps)
Kelly Wilson
Ken Takusagawa
Kenneth Lee (kennethlee33)
kevlu8
Kian E (KJE-98)
Kieren Pearson (KierenP)
kinderchocolate
Kiran Panditrao (Krgp)
Kirill Zaripov (kokodio)
Kojirion
Krisztián Peőcz
Krystian Kuzniarek (kuzkry)
@@ -145,6 +157,7 @@ Lucas Braesch (lucasart)
Lyudmil Antonov (lantonov)
Maciej Żenczykowski (zenczykowski)
Malcolm Campbell (xoto10)
Mark Marosi (Mapika)
Mark Tenzer (31m059)
marotear
Mathias Parnaudeau (mparnaudeau)
@@ -172,6 +185,7 @@ Nathan Rugg (nmrugg)
Nguyen Pham (nguyenpham)
Nicklas Persson (NicklasPersson)
Nick Pelling (nickpelling)
Nicolas Duhamel (nikloskoda)
Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT)
Norman Schmidt (FireFather)
@@ -187,6 +201,7 @@ Panthee
Pascal Romaret
Pasquale Pigazzini (ppigazzini)
Patrick Jansen (mibere)
Patrick Leonhardt (Yoshie2000)
Peter Schneider (pschneider1968)
Peter Zsifkovits (CoffeeOne)
PikaCat
@@ -205,6 +220,7 @@ Ronald de Man (syzygy1, syzygy)
Ron Britvich (Britvich)
rqs
Rui Coelho (ruicoelhopedro)
rustam-cpp
Ryan Schmitt
Ryan Takker
Sami Kiminki (skiminki)
@@ -224,12 +240,15 @@ Stefano Di Martino (StefanoD)
Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet)
Stephen Touset (stouset)
Stockfisher69
Styx (styxdoto)
Syine Mineta (MinetaS)
Taras Vuk (TarasVuk)
Thanar2
thaspel
theo77186
TierynnB
Timothy Herchen (anematode)
Ting-Hsuan Huang (fffelix-huang)
Tobias Steinmann
Tomasz Sobczyk (Sopel97)
+1 -1
View File
@@ -59,7 +59,7 @@ discussion._
Changes to Stockfish C++ code should respect our coding style defined by
[.clang-format](.clang-format). You can format your changes by running
`make format`. This requires clang-format version 18 to be installed on your system.
`make format`. This requires clang-format version 20 to be installed on your system.
## Navigate
+27
View File
@@ -59,6 +59,33 @@ This distribution of Stockfish consists of the following files:
* a file with the .nnue extension, storing the neural network for the NNUE
evaluation. Binary distributions will have this file embedded.
## Stockfish on distributed memory systems
The cluster branch allows for running Stockfish on a cluster of servers (nodes)
that are connected with a high-speed and low-latency network, using the message
passing interface (MPI). In this case, one MPI process should be run per node,
and UCI options can be used to set the number of threads/hash per node as usual.
Typically, the engine will be invoked as
```
mpirun -np N /path/to/stockfish
```
where ```N``` stands for the number of MPI processes used (alternatives to ```mpirun```,
include ```mpiexec```, ```srun```). Use 1 mpi rank per node, and employ threading
according to the cores per node. To build the cluster
branch, it is sufficient to specify ```COMPCXX=mpicxx``` (or e.g. CC depending on the name
of the compiler providing MPI support) on the make command line, and do a clean build:
```
make -j ARCH=x86-64-modern clean build COMPCXX=mpicxx mpi=yes
```
Make sure that the MPI installation is configured to support ```MPI_THREAD_MULTIPLE```,
this might require adding system specific compiler options to the Makefile. Stockfish employs
non-blocking (asynchronous) communication, and benefits from an MPI
implementation that efficiently supports this. Some MPI implentations might benefit
from leaving 1 core/thread free for these asynchronous communications, and might require
setting additional environment variables. ```mpirun``` should forward stdin/stdout
to ```rank 0``` only (e.g. ```srun --input=0 --output=0```).
Refer to your MPI documentation for more info.
## Contributing
__See [Contributing Guide](CONTRIBUTING.md).__
+132 -95
View File
@@ -1,89 +1,101 @@
Contributors to Fishtest with >10,000 CPU hours, as of 2025-03-22.
Contributors to Fishtest with >10,000 CPU hours, as of 2025-12-24.
Thank you!
Username CPU Hours Games played
------------------------------------------------------------------
noobpwnftw 41712226 3294628533
vdv 28993864 954145232
technologov 24984442 1115931964
linrock 11463033 741692823
noobpwnftw 42692720 3385202467
vdv 39922218 1277282126
technologov 26354561 1163905856
linrock 12002255 785641643
olafm 3030005 197722318
mlang 3026000 200065824
okrout 2726068 248285678
olafm 2420096 161297116
pemo 1838361 62294199
TueRens 1804847 80170868
okrout 3020471 268364402
pemo 2009761 66178221
TueRens 1956328 83294326
sebastronomy 1806628 73868874
dew 1689162 100033738
sebastronomy 1655637 67294942
grandphish2 1474752 92156319
JojoM 1130625 73666098
rpngn 973590 59996557
oz 921203 60370346
grandphish2 1479778 92306101
JojoM 1130646 73666860
rpngn 1081976 65292619
oz 1029329 69522328
gvreuls 844572 59249068
tvijlbrief 796125 51897690
gvreuls 792215 55184194
mibere 703840 46867607
leszek 599745 44681421
leszek 609538 45301765
cw 519602 34988289
fastgm 503862 30260818
CSU_Dynasty 474794 31654170
maximmasiutin 441753 28129452
robal 437950 28869118
ctoks 435150 28542141
robal 503208 32703510
maximmasiutin 500174 30818270
CSU_Dynasty 481663 31916842
ctoks 435431 28551199
crunchy 427414 27371625
bcross 415724 29061187
mgrabiak 380202 27586936
tolkki963 358623 26373242
velislav 342588 22140902
ncfish1 329039 20624527
Fisherman 327231 21829379
Sylvain27 317021 11494912
Fifis 323909 16200123
Sylvain27 320732 11671388
marrco 310446 19587107
Calis007 310201 18969692
Viren6 297938 5847458
Dantist 296386 18031762
Fifis 289595 14969251
tolkki963 286043 23596996
Calis007 272677 17281620
naclosagc 296040 13865010
anematode 293146 3918134
maposora 278093 20454200
javran 271465 20506096
cody 258835 13301710
nordlandia 249322 16420192
javran 212141 16507618
Goatminola 218812 21411814
Torom 211061 7238522
glinscott 208125 13277240
drabel 204167 13930674
Wencey 203584 9943614
mhoram 202894 12601997
sschnee 201756 12874780
bking_US 198894 11876016
Wencey 198537 9606420
Mineta 195312 10337614
Thanar 179852 12365359
sschnee 170521 10891112
armo9494 168141 11177514
armo9494 169747 11254404
amicic 161636 11290899
DesolatedDodo 160605 10392474
markkulix 158320 13538874
spams 157128 10319326
maposora 155839 13963260
sqrt2 147963 9724586
vdbergh 140514 9242985
vdbergh 141201 9308647
jcAEie 140086 10603658
CoffeeOne 137100 5024116
malala 136182 8002293
Goatminola 134893 11640524
xoto 133759 9159372
markkulix 132104 11000548
naclosagc 131472 4660806
Dubslow 129685 8527664
Dubslow 130795 8609646
zeryl 129154 7911565
davar 129023 8376525
DMBK 122960 8980062
cuistot 122470 8393996
megaman7de 122254 8066174
dsmith 122059 7570238
Wolfgang 120919 8619168
CypressChess 120902 8683904
amicic 119661 7938029
cuistot 116864 7828864
sterni1971 113754 6054022
Spprtr 113356 8129809
Data 113305 8220352
BrunoBanani 112960 7436849
megaman7de 109139 7360928
skiminki 107583 7218170
zeryl 104523 6618969
MediumBerry5575 103884 7830022
MaZePallas 102823 6633619
YvesKn 102213 5098076
sunu 100167 7040199
thirdlife 99178 2246544
thirdlife 99182 2246960
ElbertoOne 99028 7023771
TechiePirate 98957 1249064
DeepnessFulled 97313 5083358
TataneSan 97257 4239502
romangol 95662 7784954
bigpen0r 94825 6529241
jojo2357 94358 7635486
malfoy 92712 3392874
voidedstarlight 92582 2342038
brabos 92118 6186135
Maxim 90818 3283364
psk 89957 5984901
@@ -92,26 +104,26 @@ jromang 87260 5988073
racerschmacer 85805 6122790
Vizvezdenec 83761 5344740
0x3C33 82614 5271253
Spprtr 82103 5663635
MarcusTullius 82359 5335665
BRAVONE 81239 5054681
MarcusTullius 78930 5189659
Mineta 78731 4947996
Torom 77978 2651656
rn 78566 6000852
nssy 76497 5259388
woutboat 76379 6031688
teddybaer 75125 5407666
Pking_cda 73776 5293873
Viren6 73664 1356502
yurikvelo 73611 5046822
Zirie 71260 4602355
Bobo1239 70579 4794999
solarlight 70517 5028306
dv8silencer 70287 3883992
0x539 67147 2918044
manap 66273 4121774
tinker 64333 4268790
CounterFlow 63914 3775062
mecevdimitar 62493 3508750
DanielMiao1 62188 1335664
qurashee 61208 3429862
DanielMiao1 60181 1317252
AGI 58316 4336328
jojo2357 57435 4944212
robnjr 57262 4053117
Freja 56938 3733019
MaxKlaxxMiner 56879 3423958
@@ -120,44 +132,52 @@ rkl 55132 4164467
jmdana 54988 4041917
notchris 53936 4184018
renouve 53811 3501516
CounterFlow 52536 3203740
jibarbosa 53504 5110028
somethingintheshadows 52333 4344808
finfish 51360 3370515
eva42 51272 3599691
eastorwest 51117 3454811
sylvek 50391 3765170
rap 49985 3219146
pb00067 49733 3298934
GPUex 48686 3684998
OuaisBla 48626 3445134
lemtea 48563 1672454
ronaldjerum 47654 3240695
abdicj 46740 2709482
biffhero 46564 3111352
oryx 46141 3583236
jibarbosa 45890 4541218
DeepnessFulled 45734 3944282
abdicj 45577 2631772
oryx 46422 3607582
VoyagerOne 45476 3452465
mecevdimitar 44240 2584396
rdp65536 43948 2881890
speedycpu 43842 3003273
jbwiebe 43305 2805433
gopeto 43046 2821514
YvesKn 42628 2177630
Antihistamine 41788 2761312
mhunt 41735 2691355
somethingintheshadows 41502 3330418
WoodMan777 40858 3491196
Epic29 40771 4067404
drauh 40419 1634770
homyur 39893 2850481
gri 39871 2515779
vidar808 39774 1656372
Gaster319 38994 3477702
Garf 37741 2999686
SC 37299 2731694
Gaster319 37229 3289674
ZacHFX 36533 2553282
csnodgrass 36207 2688994
ZacHFX 35528 2486328
icewulf 34782 2415146
icewulf 34935 2421834
strelock 34716 2074055
Jopo12321 33921 2531448
xuhdev 33798 3295210
csnodgra 33780 1446866
EthanOConnor 33370 2090311
slakovv 32915 2021889
shawnxu 32144 2814668
IslandLambda 32667 1659344
Kataiser 32477 2688862
shawnxu 32330 2830036
srowen 32248 1791136
qgluca 31941 2491622
Gelma 31771 1551204
srowen 31181 1732120
kdave 31157 2198362
manapbk 30987 1810399
votoanthuan 30691 2460856
@@ -168,15 +188,26 @@ spcc 29925 1901692
hyperbolic.tom 29840 2017394
chuckstablers 29659 2093438
Pyafue 29650 1902349
WoodMan777 29300 2579864
Flopzee 29388 1899905
hoching 29054 2067144
belzedar94 28846 1811530
wizardassassin 28007 2318204
purpletree 27892 2061966
Kyrega 27674 963872
joendter 27193 1781570
Danielv123 27132 1043614
chriswk 26902 1868317
xwziegtm 26897 2124586
Jopo12321 26818 1816482
spotscene 26877 2139674
achambord 26582 1767323
shreven 26448 1703328
Patrick_G 26276 1801617
yorkman 26193 1992080
Ulysses 25517 1711634
ols 26173 1443517
wer 26136 793146
Skiff84 26083 1135002
RudyMars 25980 2211364
Ulysses 25544 1714542
SFTUser 25182 1675689
nabildanial 25068 1531665
Sharaf_DG 24765 1786697
@@ -184,30 +215,28 @@ rodneyc 24376 1416402
jsys14 24297 1721230
AndreasKrug 24235 1934711
agg177 23890 1395014
Disservin 23768 1934576
Ente 23752 1678188
JanErik 23408 1703875
Isidor 23388 1680691
Norabor 23371 1603244
Nullvalue 23155 2022752
fishtester 23115 1581502
wizardassassin 23073 1789536
Skiff84 22984 1053680
cisco2015 22920 1763301
ols 22914 1322047
Hjax 22561 1566151
Zirie 22542 1472937
gerbil 22435 1679842
Serpensin 22396 1861156
team-oh 22272 1636708
mkstockfishtester 22253 2029566
Roady 22220 1465606
tsim67 22077 1353048
MazeOfGalious 21978 1629593
sg4032 21950 1643373
tsim67 21939 1343944
sev 21791 1983016
ianh2105 21725 1632562
Serpensin 21704 1809188
xor12 21628 1680365
dex 21612 1467203
nesoneg 21494 1463031
IslandLambda 21468 1239756
user213718 21454 1404128
sphinx 21211 1384728
qoo_charly_cai 21136 1514927
@@ -215,22 +244,20 @@ jjoshua2 21001 1423089
Zake9298 20938 1565848
horst.prack 20878 1465656
0xB00B1ES 20590 1208666
t3hf1sht3ster 20544 673134
Dinde 20459 1292774
t3hf1sht3ster 20456 670646
j3corre 20405 941444
0x539 20332 1039516
Adrian.Schmidt123 20316 1281436
malfoy 20313 1350694
purpletree 20019 1461026
wei 19973 1745989
teenychess 19819 1762006
RickGroszkiewicz 19749 1913986
rstoesser 19569 1293588
eudhan 19274 1283717
nalanzeyu 19211 396674
vulcan 18871 1729392
Karpovbot 18766 1053178
Farseer 18536 1078326
jundery 18445 1115855
Farseer 18281 1074642
sebv15 18267 1262588
whelanh 17887 347974
ville 17883 1384026
@@ -239,84 +266,94 @@ purplefishies 17595 1092533
dju 17414 981289
iisiraider 17275 1049015
Karby 17177 1030688
fogleman 17134 815562
zhujianzhao 17111 1666972
DragonLord 17014 1162790
pirt 16991 1274215
pirt 16993 1274363
redstone59 16842 1461780
Alb11747 16787 1213990
Naven94 16414 951718
scuzzi 16155 995347
IgorLeMasson 16064 1147232
micpilar 15866 1399266
ako027ako 15671 1173203
xuhdev 15516 1528278
infinigon 15285 965966
fishtrawler 15205 1436165
Nikolay.IT 15154 1068349
Andrew Grant 15114 895539
OssumOpossum 14857 1007129
LunaticBFF57 14525 1190310
YELNAMRON 14480 1141420
enedene 14476 905279
YELNAMRON 14475 1141330
RickGroszkiewicz 14272 1385984
joendter 14269 982014
MooTheCow 14459 1023868
BestBoyBerlin 14353 1365584
bpfliegel 14233 882523
mpx86 14019 759568
jpulman 13982 870599
getraideBFF 13871 1172846
crocogoat 13817 1119086
Nesa92 13806 1116101
joster 13710 946160
joster 13717 946960
mbeier 13650 1044928
Pablohn26 13552 1088532
wxt9861 13550 1312306
biniek 13469 930029
Dark_wizzie 13422 1007152
Jackfish 13422 914984
Hongildong 13297 699288
Rudolphous 13244 883140
Jackfish 13177 894206
MooTheCow 13091 892304
Phoenix17 13032 1124066
Machariel 13010 863104
mabichito 12903 749391
FormazChar 12899 980413
thijsk 12886 722107
AdrianSA 12860 804972
Flopzee 12698 894821
szczur90 12684 977536
Kyrega 12661 456438
szczur90 12720 979324
mschmidt 12644 863193
korposzczur 12606 838168
fatmurphy 12547 853210
Oakwen 12532 855759
Oakwen 12537 856257
SapphireBrand 12416 969604
Snuuka 12392 509082
deflectooor 12386 579392
modolief 12386 896470
ckaz 12273 754644
Hongildong 12201 648712
pgontarz 12151 848794
dbernier 12103 860824
FormazChar 12051 913497
shreven 12044 884734
rensonthemove 11999 971993
stocky 11954 699440
ali-al-zhrani 11887 836126
3cho 11842 1036786
Craftyawesome 11736 832254
dragon123118 11578 1044142
ImperiumAeternum 11482 979142
lvdv 11475 594400
infinity 11470 727027
kusihe 11468 468450
vaskoul 11446 976902
aga 11412 695127
Def9Infinity 11408 700682
torbjo 11395 729145
Thomas A. Anderson 11372 732094
savage84 11358 670860
d64 11263 789184
ali-al-zhrani 11245 779246
vaskoul 11144 953906
Poly 11172 455568
enizor 11140 630194
snicolet 11106 869170
dapper 11032 771402
Ethnikoi 10993 945906
Snuuka 10938 435504
Karmatron 10871 678306
gerbil 10871 1005842
zarthus 10773 1034536
OliverClarke 10696 942654
Omed 10680 669816
cyberthink 10647 936538
basepi 10637 744851
michaelrpg 10624 748179
Cubox 10621 826448
dragon123118 10421 936506
GBx3TV 10499 343266
Styx 10450 867836
OIVAS7572 10420 995586
GBx3TV 10388 339952
Garruk 10365 706465
dzjp 10343 732529
Lorenz 10311 886308
borinot 10026 902130
+16 -6
View File
@@ -31,7 +31,7 @@ set_arch_loongarch64() {
if check_flags 'lasx'; then
true_arch='loongarch64-lasx'
elif check_flags 'lsx'; then
true_arch='lonngarch64-lsx'
true_arch='loongarch64-lsx'
else
true_arch='loongarch64'
fi
@@ -39,10 +39,14 @@ set_arch_loongarch64() {
# Set the file CPU x86_64 architecture
set_arch_x86_64() {
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
true_arch='x86-64-vnni256'
if check_flags 'avx512f' 'avx512cd' 'avx512vl' 'avx512dq' 'avx512bw' 'avx512ifma' 'avx512vbmi' 'avx512vbmi2' 'avx512vpopcntdq' 'avx512bitalg' 'avx512vnni' 'vpclmulqdq' 'gfni' 'vaes'; then
true_arch='x86-64-avx512icl'
elif check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
true_arch='x86-64-vnni512'
elif check_flags 'avx512f' 'avx512bw'; then
true_arch='x86-64-avx512'
elif check_flags 'avxvnni'; then
true_arch='x86-64-avxvnni'
elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then
true_arch='x86-64-bmi2'
elif check_flags 'avx2'; then
@@ -55,7 +59,7 @@ set_arch_x86_64() {
}
set_arch_ppc_64() {
if $(grep -q -w "altivec" /proc/cpuinfo); then
if grep -q -w "altivec" /proc/cpuinfo; then
power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo)
if [ "0$power" -gt 7 ]; then
# VSX started with POWER8
@@ -81,7 +85,7 @@ case $uname_s in
'x86_64')
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.')
set_arch_x86_64
if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then
if [ "$true_arch" = 'x86-64-avx512' ]; then
file_arch='x86-64-bmi2'
fi
;;
@@ -130,7 +134,13 @@ case $uname_s in
esac
file_ext='tar'
;;
'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer
'MINGW'*'ARM64'*) # Windows ARM64 system with POSIX compatibility layer
# TODO: older chips might be armv8, but we have no good way to detect, /proc/cpuinfo shows x86 info
file_os='windows'
true_arch='armv8-dotprod'
file_ext='zip'
;;
'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows x86_64system with POSIX compatibility layer
get_flags
check_znver_1_2
set_arch_x86_64
+116 -62
View File
@@ -1,5 +1,5 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
# Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,6 +25,21 @@ ifeq ($(KERNEL),Linux)
OS := $(shell uname -o)
endif
### Command prefix to run the built executable (e.g. wine, sde, qemu)
### Backward compatible alias: WINE_PATH (deprecated)
ifneq ($(strip $(WINE_PATH)),)
ifeq ($(strip $(RUN_PREFIX)),)
RUN_PREFIX := $(WINE_PATH)
endif
ifeq ($(MAKELEVEL),0)
ifneq ($(strip $(RUN_PREFIX)),$(strip $(WINE_PATH)))
$(warning *** Both RUN_PREFIX and WINE_PATH are set; ignoring WINE_PATH. ***)
else
$(warning *** WINE_PATH is deprecated; use RUN_PREFIX instead. ***)
endif
endif
endif
### Target Windows OS
ifeq ($(OS),Windows_NT)
ifneq ($(COMP),ndk)
@@ -32,8 +47,8 @@ ifeq ($(OS),Windows_NT)
endif
else ifeq ($(COMP),mingw)
target_windows = yes
ifeq ($(WINE_PATH),)
WINE_PATH := $(shell which wine)
ifeq ($(RUN_PREFIX),)
RUN_PREFIX := $(shell which wine)
endif
endif
@@ -49,22 +64,24 @@ PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
PGOBENCH = $(WINE_PATH) ./$(EXE) bench
PGOBENCH = $(RUN_PREFIX) ./$(EXE) bench
### Source and object files
SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
misc.cpp movegen.cpp movepick.cpp position.cpp \
misc.cpp movegen.cpp movepick.cpp position.cpp cluster.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp \
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \
nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp \
engine.cpp score.cpp memory.cpp
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \
nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \
nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \
nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \
search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \
nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \
nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \
nnue/nnue_architecture.h nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h \
position.h search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h \
cluster.h
OBJS = $(notdir $(SRCS:.cpp=.o))
@@ -97,14 +114,15 @@ VPATH = syzygy:nnue:nnue/features
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4
# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension
# vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
# lsx = yes/no --- -mlsx --- Use Loongson SIMD eXtension
# lasx = yes/no --- -mlasx --- use Loongson Advanced SIMD eXtension
# mpi = yes/no --- -DUSE_MPI --- Use Message Passing Interface
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
@@ -125,10 +143,10 @@ ifeq ($(ARCH), native)
endif
# explicitly check for the list of supported architectures (as listed with make help),
# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true`
# the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true`
ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64-avx512icl x86-64-vnni512 x86-64-avx512 x86-64-avxvnni \
x86-64-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
loongarch64 loongarch64-lsx loongarch64-lasx))
@@ -152,19 +170,20 @@ sse41 = no
avx2 = no
avxvnni = no
avx512 = no
vnni256 = no
vnni512 = no
avx512icl = no
altivec = no
vsx = no
neon = no
mpi = no
dotprod = no
arm_version = 0
lsx = no
lasx = no
STRIP = strip
ifneq ($(shell which clang-format-18 2> /dev/null),)
CLANG-FORMAT = clang-format-18
ifneq ($(shell which clang-format-20 2> /dev/null),)
CLANG-FORMAT = clang-format-20
else
CLANG-FORMAT = clang-format
endif
@@ -267,17 +286,6 @@ ifeq ($(findstring -avx512,$(ARCH)),-avx512)
avx512 = yes
endif
ifeq ($(findstring -vnni256,$(ARCH)),-vnni256)
popcnt = yes
sse = yes
sse2 = yes
ssse3 = yes
sse41 = yes
avx2 = yes
pext = yes
vnni256 = yes
endif
ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
popcnt = yes
sse = yes
@@ -290,6 +298,19 @@ ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
vnni512 = yes
endif
ifeq ($(findstring -avx512icl,$(ARCH)),-avx512icl)
popcnt = yes
sse = yes
sse2 = yes
ssse3 = yes
sse41 = yes
avx2 = yes
pext = yes
avx512 = yes
vnni512 = yes
avx512icl = yes
endif
ifeq ($(sse),yes)
prefetch = yes
endif
@@ -533,14 +554,12 @@ ifeq ($(KERNEL),Darwin)
XCRUN = xcrun
endif
# To cross-compile for Android, NDK version r21 or later is recommended.
# In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils.
# Currently we don't know how to make PGO builds with the NDK yet.
# To cross-compile for Android, use NDK version r27c or later.
ifeq ($(COMP),ndk)
CXXFLAGS += -stdlib=libc++ -fPIE
CXXFLAGS += -stdlib=libc++
comp=clang
ifeq ($(arch),armv7)
CXX=armv7a-linux-androideabi16-clang++
CXX=armv7a-linux-androideabi29-clang++
CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon
ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),)
STRIP=arm-linux-androideabi-strip
@@ -549,7 +568,7 @@ ifeq ($(COMP),ndk)
endif
endif
ifeq ($(arch),armv8)
CXX=aarch64-linux-android21-clang++
CXX=aarch64-linux-android29-clang++
ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),)
STRIP=aarch64-linux-android-strip
else
@@ -557,14 +576,32 @@ ifeq ($(COMP),ndk)
endif
endif
ifeq ($(arch),x86_64)
CXX=x86_64-linux-android21-clang++
CXX=x86_64-linux-android29-clang++
ifneq ($(shell which x86_64-linux-android-strip 2>/dev/null),)
STRIP=x86_64-linux-android-strip
else
STRIP=llvm-strip
endif
endif
LDFLAGS += -static-libstdc++ -pie -lm -latomic
LDFLAGS += -static-libstdc++
endif
### Allow overwriting CXX from command line
ifdef COMPCXX
CXX=$(COMPCXX)
endif
# llvm-profdata must be version compatible with the specified CXX (be it clang, or the gcc alias)
# make -j profile-build CXX=clang++-20 COMP=clang
# Locate the version in the same directory as the compiler used,
# with fallback to a generic one if it can't be located
LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))llvm-profdata
# for icx
ifeq ($(wildcard $(LLVM_PROFDATA)),)
LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))/compiler/llvm-profdata
endif
ifeq ($(wildcard $(LLVM_PROFDATA)),)
LLVM_PROFDATA := llvm-profdata
endif
ifeq ($(comp),icx)
@@ -581,11 +618,6 @@ else
endif
endif
### Allow overwriting CXX from command line
ifdef COMPCXX
CXX=$(COMPCXX)
endif
### Sometimes gcc is really clang
ifeq ($(COMP),gcc)
gccversion := $(shell $(CXX) --version 2>/dev/null)
@@ -593,6 +625,8 @@ ifeq ($(COMP),gcc)
ifneq ($(gccisclang),)
profile_make = clang-profile-make
profile_use = clang-profile-use
else
CXXFLAGS += -Wstack-usage=128000
endif
endif
@@ -605,6 +639,19 @@ ifneq ($(comp),mingw)
ifneq ($(KERNEL),Haiku)
ifneq ($(COMP),ndk)
LDFLAGS += -lpthread
add_lrt = yes
ifeq ($(target_windows),yes)
add_lrt = no
endif
ifeq ($(KERNEL),Darwin)
add_lrt = no
endif
ifeq ($(add_lrt),yes)
LDFLAGS += -lrt
endif
endif
endif
endif
@@ -615,6 +662,7 @@ ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
else
CXXFLAGS += -g
CXXFLAGS += -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG
endif
### 3.2.2 Debugging with undefined behavior sanitizers
@@ -694,21 +742,21 @@ endif
ifeq ($(avx512),yes)
CXXFLAGS += -DUSE_AVX512
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw
endif
endif
ifeq ($(vnni256),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256
CXXFLAGS += -mavx512f -mavx512bw -mavx512dq -mavx512vl
endif
endif
ifeq ($(vnni512),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=512
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl
endif
endif
ifeq ($(avx512icl),yes)
CXXFLAGS += -DUSE_AVX512 -DUSE_VNNI -DUSE_AVX512ICL
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mavx512ifma -mavx512vbmi -mavx512vbmi2 -mavx512vpopcntdq -mavx512bitalg -mavx512vnni -mvpclmulqdq -mgfni -mvaes
endif
endif
@@ -847,6 +895,15 @@ ifeq ($(OS), Android)
LDFLAGS += -fPIE -pie
endif
### 3.10 MPI
ifneq (,$(findstring mpi, $(CXX)))
mpi = yes
endif
ifeq ($(mpi),yes)
CXXFLAGS += -DUSE_MPI -Wno-cast-qual -fexceptions
DEPENDFLAGS += -DUSE_MPI
endif
### ==========================================================================
### Section 4. Public Targets
### ==========================================================================
@@ -870,8 +927,8 @@ help:
echo "Supported archs:" && \
echo "" && \
echo "native > select the best architecture for the host processor (default)" && \
echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lake or AMD Zen 4" && \
echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \
echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \
echo "x86-64-avx512 > x86 64-bit with avx512 support" && \
echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support" && \
echo "x86-64-bmi2 > x86 64-bit with bmi2 support" && \
@@ -989,10 +1046,6 @@ net:
format:
$(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
# default target
default:
help
### ==========================================================================
### Section 5. Private Targets
### ==========================================================================
@@ -1020,11 +1073,12 @@ config-sanity: net
echo "avx2: '$(avx2)'" && \
echo "avxvnni: '$(avxvnni)'" && \
echo "avx512: '$(avx512)'" && \
echo "vnni256: '$(vnni256)'" && \
echo "vnni512: '$(vnni512)'" && \
echo "avx512icl: '$(avx512icl)'" && \
echo "altivec: '$(altivec)'" && \
echo "vsx: '$(vsx)'" && \
echo "neon: '$(neon)'" && \
echo "mpi: '$(mpi)'" && \
echo "dotprod: '$(dotprod)'" && \
echo "arm_version: '$(arm_version)'" && \
echo "lsx: '$(lsx)'" && \
@@ -1056,8 +1110,8 @@ config-sanity: net
(test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \
(test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \
(test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
(test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \
(test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
(test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \
(test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
(test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \
(test "$(neon)" = "yes" || test "$(neon)" = "no") && \
@@ -1081,7 +1135,7 @@ clang-profile-make:
all
clang-profile-use:
$(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw
$(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use=stockfish.profdata' \
EXTRALDFLAGS='-fprofile-use ' \
@@ -1109,7 +1163,7 @@ icx-profile-make:
all
icx-profile-use:
$(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw
$(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
EXTRALDFLAGS='-fprofile-use ' \
@@ -1118,6 +1172,6 @@ icx-profile-use:
.depend: $(SRCS)
-@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null
ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean config-sanity))
ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean format config-sanity))
-include .depend
endif
+8 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -65,6 +65,10 @@ const std::vector<std::string> Defaults = {
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
// Positions with high numbers of changed threats
"k7/2n1n3/1nbNbn2/2NbRBn1/1nbRQR2/2NBRBN1/3N1N2/7K w - - 0 1",
"K7/8/8/BNQNQNB1/N5N1/R1Q1q2r/n5n1/bnqnqnbk w - - 0 1",
// 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
@@ -450,7 +454,7 @@ BenchmarkSetup setup_benchmark(std::istream& is) {
int desiredTimeS;
if (!(is >> setup.threads))
setup.threads = get_hardware_concurrency();
setup.threads = int(get_hardware_concurrency());
else
setup.originalInvocation += std::to_string(setup.threads);
@@ -479,11 +483,10 @@ BenchmarkSetup setup_benchmark(std::istream& is) {
float totalTime = 0;
for (const auto& game : BenchmarkPositions)
{
setup.commands.emplace_back("ucinewgame");
int ply = 1;
for (int i = 0; i < static_cast<int>(game.size()); ++i)
{
const float correctedTime = getCorrectedTime(ply);
const float correctedTime = float(getCorrectedTime(ply));
totalTime += correctedTime;
ply += 1;
}
@@ -509,4 +512,4 @@ BenchmarkSetup setup_benchmark(std::istream& is) {
return setup;
}
} // namespace Stockfish
} // namespace Stockfish
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+6 -48
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,8 +31,7 @@ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
alignas(64) Magic Magics[SQUARE_NB][2];
@@ -42,13 +41,6 @@ Bitboard RookTable[0x19000]; // To store rook attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]);
// Returns the bitboard of target square for the given step
// from the given square. If the step is off the board, returns empty bitboard.
Bitboard safe_destination(Square s, int step) {
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
}
}
// Returns an ASCII representation of a bitboard suitable
@@ -86,18 +78,6 @@ void Bitboards::init() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
PawnAttacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
PawnAttacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9})
PseudoAttacks[KING][s1] |= safe_destination(s1, step);
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17})
PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step);
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb<ROOK>(s1, 0);
for (PieceType pt : {BISHOP, ROOK})
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
@@ -106,6 +86,8 @@ void Bitboards::init() {
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] =
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
RayPassBB[s1][s2] =
attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
}
BetweenBB[s1][s2] |= s2;
}
@@ -113,30 +95,6 @@ void Bitboards::init() {
}
namespace {
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
Bitboard attacks = 0;
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{
Square s = sq;
while (safe_destination(s, d))
{
attacks |= (s += d);
if (occupied & s)
{
break;
}
}
}
return attacks;
}
// Computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
@@ -165,7 +123,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
Magic& m = magics[s][pt - BISHOP];
m.mask = sliding_attack(pt, s, 0) & ~edges;
m.mask = Bitboards::sliding_attack(pt, s, 0) & ~edges;
#ifndef USE_PEXT
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
#endif
@@ -182,7 +140,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
#ifndef USE_PEXT
occupancy[size] = b;
#endif
reference[size] = sliding_attack(pt, s, b);
reference[size] = Bitboards::sliding_attack(pt, s, b);
if (HasPext)
m.attacks[pext(b, m.mask)] = reference[size];
+165 -71
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@
#include <cstdint>
#include <cstdlib>
#include <string>
#include <initializer_list>
#include <array>
#include "types.h"
@@ -61,9 +63,7 @@ extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
// Magic holds all magic bitboards relevant data for a single square
struct Magic {
@@ -103,17 +103,17 @@ constexpr Bitboard square_bb(Square s) {
// Overloads of bitwise operators between a Bitboard and a Square for testing
// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); }
inline Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); }
inline Bitboard operator^(Bitboard b, Square s) { return b ^ square_bb(s); }
inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
constexpr Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); }
constexpr Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); }
constexpr Bitboard operator^(Bitboard b, Square s) { return b ^ square_bb(s); }
constexpr Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
constexpr Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
constexpr Bitboard operator&(Square s, Bitboard b) { return b & s; }
constexpr Bitboard operator|(Square s, Bitboard b) { return b | s; }
constexpr Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
constexpr Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
constexpr bool more_than_one(Bitboard b) { return b & (b - 1); }
@@ -155,11 +155,6 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
: shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(b);
}
inline Bitboard pawn_attacks_bb(Color c, Square s) {
assert(is_ok(s));
return PawnAttacks[c][s];
}
// Returns a bitboard representing an entire line (from board edge
// to board edge) that intersects the two given squares. If the given squares
@@ -185,11 +180,6 @@ inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2];
}
// Returns true if the squares s1, s2 and s3 are aligned either on a
// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; }
// distance() functions return the distance between x and y, defined as the
// number of steps for a king in x to reach y.
@@ -213,57 +203,14 @@ inline int distance<Square>(Square x, Square y) {
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
// Returns the pseudo attacks of the given piece type
// assuming an empty board.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s) {
assert((Pt != PAWN) && (is_ok(s)));
return PseudoAttacks[Pt][s];
constexpr int constexpr_popcount(Bitboard b) {
b = b - ((b >> 1) & 0x5555555555555555ULL);
b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL);
b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
return static_cast<int>((b * 0x0101010101010101ULL) >> 56);
}
// Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue passed an occupied square.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
assert((Pt != PAWN) && (is_ok(s)));
switch (Pt)
{
case BISHOP :
case ROOK :
return Magics[s][Pt - BISHOP].attacks_bb(occupied);
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[Pt][s];
}
}
// Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue passed an occupied square.
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
assert((pt != PAWN) && (is_ok(s)));
switch (pt)
{
case BISHOP :
return attacks_bb<BISHOP>(s, occupied);
case ROOK :
return attacks_bb<ROOK>(s, occupied);
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[pt][s];
}
}
// Counts the number of non-zero bits in a bitboard.
inline int popcount(Bitboard b) {
@@ -369,6 +316,153 @@ inline Square pop_lsb(Bitboard& b) {
return s;
}
namespace Bitboards {
// Returns the bitboard of target square for the given step
// from the given square. If the step is off the board, returns empty bitboard.
constexpr Bitboard safe_destination(Square s, int step) {
constexpr auto abs = [](int v) { return v < 0 ? -v : v; };
Square to = Square(s + step);
return is_ok(to) && abs(file_of(s) - file_of(to)) <= 2 ? square_bb(to) : Bitboard(0);
}
constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
Bitboard attacks = 0;
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{
Square s = sq;
while (safe_destination(s, d))
{
attacks |= (s += d);
if (occupied & s)
{
break;
}
}
}
return attacks;
}
constexpr Bitboard knight_attack(Square sq) {
Bitboard b = {};
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17})
b |= safe_destination(sq, step);
return b;
}
constexpr Bitboard king_attack(Square sq) {
Bitboard b = {};
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9})
b |= safe_destination(sq, step);
return b;
}
constexpr Bitboard pseudo_attacks(PieceType pt, Square sq) {
switch (pt)
{
case PieceType::ROOK :
case PieceType::BISHOP :
return sliding_attack(pt, sq, 0);
case PieceType::QUEEN :
return sliding_attack(PieceType::ROOK, sq, 0) | sliding_attack(PieceType::BISHOP, sq, 0);
case PieceType::KNIGHT :
return knight_attack(sq);
case PieceType::KING :
return king_attack(sq);
default :
assert(false);
return 0;
}
}
}
inline constexpr auto PseudoAttacks = []() constexpr {
std::array<std::array<Bitboard, SQUARE_NB>, PIECE_TYPE_NB> attacks{};
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
attacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
attacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
attacks[KING][s1] = Bitboards::pseudo_attacks(KING, s1);
attacks[KNIGHT][s1] = Bitboards::pseudo_attacks(KNIGHT, s1);
attacks[QUEEN][s1] = attacks[BISHOP][s1] = Bitboards::pseudo_attacks(BISHOP, s1);
attacks[QUEEN][s1] |= attacks[ROOK][s1] = Bitboards::pseudo_attacks(ROOK, s1);
}
return attacks;
}();
// Returns the pseudo attacks of the given piece type
// assuming an empty board.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Color c = COLOR_NB) {
assert((Pt != PAWN || c < COLOR_NB) && (is_ok(s)));
return Pt == PAWN ? PseudoAttacks[c][s] : PseudoAttacks[Pt][s];
}
// Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue passed an occupied square.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
assert((Pt != PAWN) && (is_ok(s)));
switch (Pt)
{
case BISHOP :
case ROOK :
return Magics[s][Pt - BISHOP].attacks_bb(occupied);
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[Pt][s];
}
}
// Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue passed an occupied square.
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
assert((pt != PAWN) && (is_ok(s)));
switch (pt)
{
case BISHOP :
return attacks_bb<BISHOP>(s, occupied);
case ROOK :
return attacks_bb<ROOK>(s, occupied);
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[pt][s];
}
}
inline Bitboard attacks_bb(Piece pc, Square s) {
if (type_of(pc) == PAWN)
return PseudoAttacks[color_of(pc)][s];
return PseudoAttacks[type_of(pc)][s];
}
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
if (type_of(pc) == PAWN)
return PseudoAttacks[color_of(pc)][s];
return attacks_bb(type_of(pc), s, occupied);
}
} // namespace Stockfish
#endif // #ifndef BITBOARD_H_INCLUDED
+490
View File
@@ -0,0 +1,490 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MPI
#include <array>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <map>
#include <mpi.h>
#include <string>
#include <vector>
#include "cluster.h"
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "search.h"
namespace Stockfish {
namespace Distributed {
// Total number of ranks and rank within the communicator
static int world_rank = MPI_PROC_NULL;
static int world_size = 0;
// Signals between ranks exchange basic info using a dedicated communicator
static MPI_Comm signalsComm = MPI_COMM_NULL;
static MPI_Request reqSignals = MPI_REQUEST_NULL;
static uint64_t signalsCallCounter = 0;
// Signals are the number of nodes searched, stop, table base hits, transposition table saves
enum Signals : int {
SIG_NODES = 0,
SIG_STOP = 1,
SIG_TB = 2,
SIG_TTS = 3,
SIG_NB = 4
};
static uint64_t signalsSend[SIG_NB] = {};
static uint64_t signalsRecv[SIG_NB] = {};
static uint64_t nodesSearchedOthers = 0;
static uint64_t tbHitsOthers = 0;
static uint64_t TTsavesOthers = 0;
static uint64_t stopSignalsPosted = 0;
// The UCI threads of each rank exchange use a dedicated communicator
static MPI_Comm InputComm = MPI_COMM_NULL;
// bestMove requires MoveInfo communicators and data types
static MPI_Comm MoveComm = MPI_COMM_NULL;
static MPI_Datatype MIDatatype = MPI_DATATYPE_NULL;
// TT entries are communicated with a dedicated communicator.
// The receive buffer is used to gather information from all ranks.
// THe TTCacheCounter tracks the number of local elements that are ready to be sent.
static MPI_Comm TTComm = MPI_COMM_NULL;
static std::array<std::vector<KeyedTTEntry>, 2> TTSendRecvBuffs;
static std::array<MPI_Request, 2> reqsTTSendRecv = {MPI_REQUEST_NULL, MPI_REQUEST_NULL};
static uint64_t sendRecvPosted = 0;
static std::atomic<uint64_t> TTCacheCounter = {};
/// Initialize MPI and associated data types. Note that the MPI library must be configured
/// to support MPI_THREAD_MULTIPLE, since multiple threads access MPI simultaneously.
void init() {
int thread_support;
MPI_Init_thread(nullptr, nullptr, MPI_THREAD_MULTIPLE, &thread_support);
if (thread_support < MPI_THREAD_MULTIPLE)
{
std::cerr << "Stockfish requires support for MPI_THREAD_MULTIPLE." << std::endl;
std::exit(EXIT_FAILURE);
}
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
const std::array<MPI_Aint, 5> MIdisps = {offsetof(MoveInfo, move), offsetof(MoveInfo, ponder),
offsetof(MoveInfo, depth), offsetof(MoveInfo, score),
offsetof(MoveInfo, rank)};
MPI_Type_create_hindexed_block(5, 1, MIdisps.data(), MPI_INT, &MIDatatype);
MPI_Type_commit(&MIDatatype);
MPI_Comm_dup(MPI_COMM_WORLD, &InputComm);
MPI_Comm_dup(MPI_COMM_WORLD, &TTComm);
MPI_Comm_dup(MPI_COMM_WORLD, &MoveComm);
MPI_Comm_dup(MPI_COMM_WORLD, &signalsComm);
}
/// Finalize MPI and free the associated data types.
void finalize() {
MPI_Type_free(&MIDatatype);
MPI_Comm_free(&InputComm);
MPI_Comm_free(&TTComm);
MPI_Comm_free(&MoveComm);
MPI_Comm_free(&signalsComm);
MPI_Finalize();
}
/// Return the total number of ranks
int size() { return world_size; }
/// Return the rank (index) of the process
int rank() { return world_rank; }
/// The receive buffer depends on the number of MPI ranks and threads, resize as needed
void ttSendRecvBuff_resize(size_t nThreads) {
for (int i : {0, 1})
{
TTSendRecvBuffs[i].resize(TTCacheSize * world_size * nThreads);
std::fill(TTSendRecvBuffs[i].begin(), TTSendRecvBuffs[i].end(), KeyedTTEntry());
}
}
/// As input is only received by the root (rank 0) of the cluster, this input must be relayed
/// to the UCI threads of all ranks, in order to setup the position, etc. We do this with a
/// dedicated getline implementation, where the root broadcasts to all other ranks the received
/// information.
bool getline(std::istream& input, std::string& str) {
int size;
std::vector<char> vec;
int state;
if (is_root())
{
state = static_cast<bool>(std::getline(input, str));
vec.assign(str.begin(), str.end());
size = vec.size();
}
// Some MPI implementations use busy-wait polling, while we need yielding as otherwise
// the UCI thread on the non-root ranks would be consuming resources.
static MPI_Request reqInput = MPI_REQUEST_NULL;
MPI_Ibcast(&size, 1, MPI_INT, 0, InputComm, &reqInput);
if (is_root())
MPI_Wait(&reqInput, MPI_STATUS_IGNORE);
else
{
while (true)
{
int flag;
MPI_Test(&reqInput, &flag, MPI_STATUS_IGNORE);
if (flag)
break;
else
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// Broadcast received string
if (!is_root())
vec.resize(size);
MPI_Bcast(vec.data(), size, MPI_CHAR, 0, InputComm);
if (!is_root())
str.assign(vec.begin(), vec.end());
MPI_Bcast(&state, 1, MPI_INT, 0, InputComm);
return state;
}
/// Sending part of the signal communication loop
namespace {
void signals_send(const ThreadPool& threads) {
signalsSend[SIG_NODES] = threads.nodes_searched();
signalsSend[SIG_TB] = threads.tb_hits();
signalsSend[SIG_TTS] = threads.TT_saves();
signalsSend[SIG_STOP] = threads.stop;
MPI_Iallreduce(signalsSend, signalsRecv, SIG_NB, MPI_UINT64_T, MPI_SUM, signalsComm,
&reqSignals);
++signalsCallCounter;
}
/// Processing part of the signal communication loop.
/// For some counters (e.g. nodes) we only keep their sum on the other nodes
/// allowing to add local counters at any time for more fine grained process,
/// which is useful to indicate progress during early iterations, and to have
/// node counts that exactly match the non-MPI code in the single rank case.
/// This call also propagates the stop signal between ranks.
void signals_process(ThreadPool& threads) {
nodesSearchedOthers = signalsRecv[SIG_NODES] - signalsSend[SIG_NODES];
tbHitsOthers = signalsRecv[SIG_TB] - signalsSend[SIG_TB];
TTsavesOthers = signalsRecv[SIG_TTS] - signalsSend[SIG_TTS];
stopSignalsPosted = signalsRecv[SIG_STOP];
if (signalsRecv[SIG_STOP] > 0)
threads.stop = true;
}
void sendrecv_post() {
++sendRecvPosted;
MPI_Irecv(TTSendRecvBuffs[sendRecvPosted % 2].data(),
TTSendRecvBuffs[sendRecvPosted % 2].size() * sizeof(KeyedTTEntry), MPI_BYTE,
(rank() + size() - 1) % size(), 42, TTComm, &reqsTTSendRecv[0]);
MPI_Isend(TTSendRecvBuffs[(sendRecvPosted + 1) % 2].data(),
TTSendRecvBuffs[(sendRecvPosted + 1) % 2].size() * sizeof(KeyedTTEntry), MPI_BYTE,
(rank() + 1) % size(), 42, TTComm, &reqsTTSendRecv[1]);
}
}
/// During search, most message passing is asynchronous, but at the end of
/// search it makes sense to bring them to a common, finalized state.
void signals_sync(ThreadPool& threads) {
while (stopSignalsPosted < uint64_t(size()))
signals_poll(threads);
// Finalize outstanding messages of the signal loops.
// We might have issued one call less than needed on some ranks.
uint64_t globalCounter;
MPI_Allreduce(&signalsCallCounter, &globalCounter, 1, MPI_UINT64_T, MPI_MAX, MoveComm);
if (signalsCallCounter < globalCounter)
{
MPI_Wait(&reqSignals, MPI_STATUS_IGNORE);
signals_send(threads);
}
assert(signalsCallCounter == globalCounter);
MPI_Wait(&reqSignals, MPI_STATUS_IGNORE);
signals_process(threads);
// Finalize outstanding messages in the sendRecv loop
MPI_Allreduce(&sendRecvPosted, &globalCounter, 1, MPI_UINT64_T, MPI_MAX, MoveComm);
while (sendRecvPosted < globalCounter)
{
MPI_Waitall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), MPI_STATUSES_IGNORE);
sendrecv_post();
}
assert(sendRecvPosted == globalCounter);
MPI_Waitall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), MPI_STATUSES_IGNORE);
}
/// Initialize signal counters to zero.
void signals_init() {
stopSignalsPosted = tbHitsOthers = TTsavesOthers = nodesSearchedOthers = 0;
signalsSend[SIG_NODES] = signalsRecv[SIG_NODES] = 0;
signalsSend[SIG_TB] = signalsRecv[SIG_TB] = 0;
signalsSend[SIG_TTS] = signalsRecv[SIG_TTS] = 0;
signalsSend[SIG_STOP] = signalsRecv[SIG_STOP] = 0;
}
/// Poll the signal loop, and start next round as needed.
void signals_poll(ThreadPool& threads) {
int flag;
MPI_Test(&reqSignals, &flag, MPI_STATUS_IGNORE);
if (flag)
{
signals_process(threads);
signals_send(threads);
}
}
/// Provide basic info related the cluster performance, in particular, the number of signals send,
/// signals per sounds (sps), the number of gathers, the number of positions gathered (per node and per second, gpps)
/// The number of TT saves and TT saves per second. If gpps equals approximately TTSavesps the gather loop has enough bandwidth.
void cluster_info(const ThreadPool& threads, Depth depth, TimePoint elapsed) {
// TimePoint elapsed = Time.elapsed() + 1;
uint64_t TTSaves = TT_saves(threads);
sync_cout << "info depth " << depth << " cluster "
<< " signals " << signalsCallCounter << " sps " << signalsCallCounter * 1000 / elapsed
<< " sendRecvs " << sendRecvPosted << " srpps "
<< TTSendRecvBuffs[0].size() * sendRecvPosted * 1000 / elapsed << " TTSaves "
<< TTSaves << " TTSavesps " << TTSaves * 1000 / elapsed << sync_endl;
}
/// When a TT entry is saved, additional steps are taken if the entry is of sufficient depth.
/// If sufficient entries has been collected, a communication is initiated.
/// If a communication has been completed, the received results are saved to the TT.
void save(TranspositionTable& TT,
ThreadPool& threads,
Search::Worker* thread,
TTWriter ttWriter,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8) {
// Standard save to the TT
ttWriter.write(k, v, PvHit, b, d, m, ev, generation8);
// If the entry is of sufficient depth to be worth communicating, take action.
if (d > 3)
{
// count the TTsaves to information: this should be relatively similar
// to the number of entries we can send/recv.
thread->TTsaves.fetch_add(1, std::memory_order_relaxed);
// Add to thread's send buffer, the locking here avoids races when the master thread
// prepares the send buffer.
{
std::lock_guard<std::mutex> lk(thread->ttCache.mutex);
thread->ttCache.buffer.replace(KeyedTTEntry(k, TTData(m, v, ev, d, b, PvHit)));
++TTCacheCounter;
}
size_t recvBuffPerRankSize = threads.size() * TTCacheSize;
// Communicate on main search thread, as soon the threads combined have collected
// sufficient data to fill the send buffers.
if (thread == threads.main_thread()->worker.get() && TTCacheCounter > recvBuffPerRankSize)
{
// Test communication status
int flag;
MPI_Testall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), &flag, MPI_STATUSES_IGNORE);
// Current communication is complete
if (flag)
{
// Save all received entries to TT, and store our TTCaches, ready for the next round of communication
for (size_t irank = 0; irank < size_t(size()); ++irank)
{
if (irank
== size_t(
rank())) // this is our part, fill the part of the buffer for sending
{
// Copy from the thread caches to the right spot in the buffer
size_t i = irank * recvBuffPerRankSize;
for (auto&& th : threads)
{
std::lock_guard<std::mutex> lk(th->worker->ttCache.mutex);
for (auto&& e : th->worker->ttCache.buffer)
TTSendRecvBuffs[sendRecvPosted % 2][i++] = e;
// Reset thread's send buffer
th->worker->ttCache.buffer = {};
}
TTCacheCounter = 0;
}
else // process data received from the corresponding rank.
for (size_t i = irank * recvBuffPerRankSize;
i < (irank + 1) * recvBuffPerRankSize; ++i)
{
auto&& e = TTSendRecvBuffs[sendRecvPosted % 2][i];
auto [ttHit, ttData, ttWriterForRecvd] = TT.probe(e.first);
ttWriterForRecvd.write(e.first, e.second.value, e.second.is_pv,
e.second.bound, e.second.depth, e.second.move,
e.second.eval, TT.generation());
}
}
// Start next communication
sendrecv_post();
// Force check of time on the next occasion, the above actions might have taken some time.
thread->main_manager()->callsCnt = 0;
}
}
}
}
/// Picks the bestMove across ranks, and send the associated info and PV to the root of the cluster.
/// Note that this bestMove and PV must be output by the root, the guarantee proper ordering of output.
/// TODO update to the scheme in master.. can this use aggregation of votes?
void pick_moves(MoveInfo& mi, std::vector<std::vector<char>>& serializedInfo) {
MoveInfo* pMoveInfo = NULL;
if (is_root())
{
pMoveInfo = (MoveInfo*) malloc(sizeof(MoveInfo) * size());
}
MPI_Gather(&mi, 1, MIDatatype, pMoveInfo, 1, MIDatatype, 0, MoveComm);
if (is_root())
{
std::map<int, int> votes;
int minScore = pMoveInfo[0].score;
for (int i = 0; i < size(); ++i)
{
minScore = std::min(minScore, pMoveInfo[i].score);
votes[pMoveInfo[i].move] = 0;
}
for (int i = 0; i < size(); ++i)
{
votes[pMoveInfo[i].move] += pMoveInfo[i].score - minScore + pMoveInfo[i].depth;
}
int bestVote = votes[pMoveInfo[0].move];
for (int i = 0; i < size(); ++i)
{
if (votes[pMoveInfo[i].move] > bestVote)
{
bestVote = votes[pMoveInfo[i].move];
mi = pMoveInfo[i];
}
}
free(pMoveInfo);
}
// Send around the final result
MPI_Bcast(&mi, 1, MIDatatype, 0, MoveComm);
// Send PV line to root as needed
if (mi.rank != 0 && mi.rank == rank())
{
int numLines = serializedInfo.size();
MPI_Send(&numLines, 1, MPI_INT, 0, 42, MoveComm);
for (const auto& serializedInfoOne : serializedInfo)
{
int size;
size = serializedInfoOne.size();
MPI_Send(&size, 1, MPI_INT, 0, 42, MoveComm);
MPI_Send(serializedInfoOne.data(), size, MPI_CHAR, 0, 42, MoveComm);
}
}
if (mi.rank != 0 && is_root())
{
serializedInfo.clear();
int numLines;
MPI_Recv(&numLines, 1, MPI_INT, mi.rank, 42, MoveComm, MPI_STATUS_IGNORE);
for (int i = 0; i < numLines; ++i)
{
int size;
std::vector<char> vec;
MPI_Recv(&size, 1, MPI_INT, mi.rank, 42, MoveComm, MPI_STATUS_IGNORE);
vec.resize(size);
MPI_Recv(vec.data(), size, MPI_CHAR, mi.rank, 42, MoveComm, MPI_STATUS_IGNORE);
serializedInfo.push_back(std::move(vec));
}
}
}
/// Return nodes searched (lazily updated cluster wide in the signal loop)
uint64_t nodes_searched(const ThreadPool& threads) {
return nodesSearchedOthers + threads.nodes_searched();
}
/// Return table base hits (lazily updated cluster wide in the signal loop)
uint64_t tb_hits(const ThreadPool& threads) { return tbHitsOthers + threads.tb_hits(); }
/// Return the number of saves to the TT buffers, (lazily updated cluster wide in the signal loop)
uint64_t TT_saves(const ThreadPool& threads) { return TTsavesOthers + threads.TT_saves(); }
}
}
#else
#include "cluster.h"
#include "thread.h"
namespace Stockfish {
namespace Distributed {
uint64_t nodes_searched(const ThreadPool& threads) { return threads.nodes_searched(); }
uint64_t tb_hits(const ThreadPool& threads) { return threads.tb_hits(); }
uint64_t TT_saves(const ThreadPool& threads) { return threads.TT_saves(); }
}
}
#endif // USE_MPI
+158
View File
@@ -0,0 +1,158 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLUSTER_H_INCLUDED
#define CLUSTER_H_INCLUDED
#include <algorithm>
#include <array>
#include <istream>
#include <string>
#include "misc.h"
#include "tt.h"
namespace Stockfish {
class Thread;
class ThreadPool;
namespace Search {
class Worker;
}
/// The Distributed namespace contains functionality required to run on distributed
/// memory architectures using MPI as the message passing interface. On a high level,
/// a 'lazy SMP'-like scheme is implemented where TT saves of sufficient depth are
/// collected on each rank and distributed to, and used by, all other ranks,
/// which search essentially independently. The root (MPI rank 0) of the cluster
/// is responsible for all I/O and time management, communicating this info to
/// the other ranks as needed. UCI options such as Threads and Hash specify these
/// quantities per MPI rank. It is recommended to have one rank (MPI process) per node.
/// For the non-MPI case, wrappers that will be compiler-optimized away are provided.
namespace Distributed {
/// Basic info to find the cluster-wide bestMove
struct MoveInfo {
int move;
int ponder;
int depth;
int score;
int rank;
};
#ifdef USE_MPI
// store the TTData with its (full) key, so it can be saved on the receiver side
using KeyedTTEntry = std::pair<Key, TTData>;
constexpr std::size_t TTCacheSize = 16;
// Threads locally cache their high-depth TT entries till a batch can be send by MPI
template<std::size_t N>
class TTCache: public std::array<KeyedTTEntry, N> {
struct Compare {
inline bool operator()(const KeyedTTEntry& lhs, const KeyedTTEntry& rhs) {
return lhs.second.depth > rhs.second.depth;
}
};
Compare compare;
public:
// Keep a heap of entries replacing low depth with high depth entries
bool replace(const KeyedTTEntry& value) {
if (compare(value, this->front()))
{
std::pop_heap(this->begin(), this->end(), compare);
this->back() = value;
std::push_heap(this->begin(), this->end(), compare);
return true;
}
return false;
}
};
void init();
void finalize();
bool getline(std::istream& input, std::string& str);
int size();
int rank();
inline bool is_root() { return rank() == 0; }
void save(TranspositionTable&,
ThreadPool&,
Search::Worker* thread,
TTWriter ttWriter,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8);
void pick_moves(MoveInfo& mi, std::vector<std::vector<char>>& PVLine);
void ttSendRecvBuff_resize(size_t nThreads);
uint64_t nodes_searched(const ThreadPool&);
uint64_t tb_hits(const ThreadPool&);
uint64_t TT_saves(const ThreadPool&);
void cluster_info(const ThreadPool&, Depth depth, TimePoint elapsed);
void signals_init();
void signals_poll(ThreadPool& threads);
void signals_sync(ThreadPool& threads);
#else
inline void init() {}
inline void finalize() {}
inline bool getline(std::istream& input, std::string& str) {
return static_cast<bool>(std::getline(input, str));
}
constexpr int size() { return 1; }
constexpr int rank() { return 0; }
constexpr bool is_root() { return true; }
inline void save(TranspositionTable&,
ThreadPool&,
Search::Worker*,
TTWriter ttWriter,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8) {
ttWriter.write(k, v, PvHit, b, d, m, ev, generation8);
}
inline void pick_moves(MoveInfo&, std::vector<std::vector<char>>&) {}
inline void ttSendRecvBuff_resize(size_t) {}
uint64_t nodes_searched(const ThreadPool&);
uint64_t tb_hits(const ThreadPool&);
uint64_t TT_saves(const ThreadPool&);
inline void cluster_info(const ThreadPool&, Depth, TimePoint) {}
inline void signals_init() {}
inline void signals_poll(ThreadPool&) {}
inline void signals_sync(ThreadPool&) {}
#endif /* USE_MPI */
}
}
#endif // #ifndef CLUSTER_H_INCLUDED
+45 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,10 +33,12 @@
#include "misc.h"
#include "nnue/network.h"
#include "nnue/nnue_common.h"
#include "nnue/nnue_misc.h"
#include "numa.h"
#include "perft.h"
#include "position.h"
#include "search.h"
#include "shm.h"
#include "syzygy/tbprobe.h"
#include "types.h"
#include "uci.h"
@@ -57,11 +59,14 @@ Engine::Engine(std::optional<std::string> path) :
threads(),
networks(
numaContext,
NN::Networks(
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
pos.set(StartFEN, false, &states->back());
// Heap-allocate because sizeof(NN::Networks) is large
std::make_unique<NN::Networks>(
std::make_unique<NN::NetworkBig>(NN::EvalFile{EvalFileDefaultNameBig, "None", ""},
NN::EmbeddedNNUEType::BIG),
std::make_unique<NN::NetworkSmall>(NN::EvalFile{EvalFileDefaultNameSmall, "None", ""},
NN::EmbeddedNNUEType::SMALL))) {
pos.set(StartFEN, false, &states->back());
options.add( //
"Debug Log File", Option("", [](const Option& o) {
@@ -235,7 +240,8 @@ void Engine::set_numa_config_from_option(const std::string& o) {
void Engine::resize_threads() {
threads.wait_for_search_finished();
threads.set(numaContext.get_numa_config(), {options, threads, tt, networks}, updateContext);
threads.set(numaContext.get_numa_config(), {options, threads, tt, sharedHists, networks},
updateContext);
// Reallocate the hash with the new threadpool size
set_tt_size(options["Hash"]);
@@ -245,6 +251,9 @@ void Engine::resize_threads() {
void Engine::set_tt_size(size_t mb) {
wait_for_search_finished();
tt.resize(mb, threads);
// Adjust cluster buffers
Distributed::ttSendRecvBuff_resize(threads.num_threads());
}
void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
@@ -254,6 +263,36 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
void Engine::verify_networks() const {
networks->big.verify(options["EvalFile"], onVerifyNetworks);
networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
auto statuses = networks.get_status_and_errors();
for (size_t i = 0; i < statuses.size(); ++i)
{
const auto [status, error] = statuses[i];
std::string message = "Network replica " + std::to_string(i + 1) + ": ";
if (status == SystemWideSharedConstantAllocationStatus::NoAllocation)
{
message += "No allocation.";
}
else if (status == SystemWideSharedConstantAllocationStatus::LocalMemory)
{
message += "Local memory.";
}
else if (status == SystemWideSharedConstantAllocationStatus::SharedMemory)
{
message += "Shared memory.";
}
else
{
message += "Unknown status.";
}
if (error.has_value())
{
message += " " + *error;
}
onVerifyNetworks(message);
}
}
void Engine::load_networks() {
+8 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,12 +22,14 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "history.h"
#include "nnue/network.h"
#include "numa.h"
#include "position.h"
@@ -115,13 +117,14 @@ class Engine {
Position pos;
StateListPtr states;
OptionsMap options;
ThreadPool threads;
TranspositionTable tt;
LazyNumaReplicated<Eval::NNUE::Networks> networks;
OptionsMap options;
ThreadPool threads;
TranspositionTable tt;
LazyNumaReplicatedSystemWide<Eval::NNUE::Networks> networks;
Search::SearchManager::UpdateContext updateContext;
std::function<void(std::string_view)> onVerifyNetworks;
std::map<NumaIndex, SharedHistories> sharedHists;
};
} // namespace Stockfish
+20 -24
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,17 +38,15 @@
namespace Stockfish {
// Returns a static, purely materialistic evaluation of the position from
// the point of view of the given color. It can be divided by PawnValue to get
// the point of view of the side to move. It can be divided by PawnValue to get
// an approximation of the material advantage on the board in terms of pawns.
int Eval::simple_eval(const Position& pos, Color c) {
return PawnValue * (pos.count<PAWN>(c) - pos.count<PAWN>(~c))
+ (pos.non_pawn_material(c) - pos.non_pawn_material(~c));
int Eval::simple_eval(const Position& pos) {
Color c = pos.side_to_move();
return PawnValue * (pos.count<PAWN>(c) - pos.count<PAWN>(~c)) + pos.non_pawn_material(c)
- pos.non_pawn_material(~c);
}
bool Eval::use_smallnet(const Position& pos) {
int simpleEval = simple_eval(pos, pos.side_to_move());
return std::abs(simpleEval) > 962;
}
bool Eval::use_smallnet(const Position& pos) { return std::abs(simple_eval(pos)) > 962; }
// Evaluate is the evaluator for the outer world. It returns a static evaluation
// of the position from the point of view of the side to move.
@@ -61,29 +59,29 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
assert(!pos.checkers());
bool smallNet = use_smallnet(pos);
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, accumulators, &caches.small)
: networks.big.evaluate(pos, accumulators, &caches.big);
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, accumulators, caches.small)
: networks.big.evaluate(pos, accumulators, caches.big);
Value nnue = (125 * psqt + 131 * positional) / 128;
// Re-evaluate the position when higher eval accuracy is worth the time spent
if (smallNet && (std::abs(nnue) < 236))
if (smallNet && (std::abs(nnue) < 277))
{
std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, &caches.big);
std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, caches.big);
nnue = (125 * psqt + 131 * positional) / 128;
smallNet = false;
}
// Blend optimism and eval with nnue complexity
int nnueComplexity = std::abs(psqt - positional);
optimism += optimism * nnueComplexity / 468;
nnue -= nnue * nnueComplexity / 18000;
optimism += optimism * nnueComplexity / 476;
nnue -= nnue * nnueComplexity / 18236;
int material = 535 * pos.count<PAWN>() + pos.non_pawn_material();
int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777;
int material = 534 * pos.count<PAWN>() + pos.non_pawn_material();
int v = (nnue * (77871 + material) + optimism * (7191 + material)) / 77871;
// Damp down the evaluation linearly when shuffling
v -= v * pos.rule50_count() / 212;
v -= v * pos.rule50_count() / 199;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
@@ -100,10 +98,8 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) {
if (pos.checkers())
return "Final evaluation: none (in check)";
Eval::NNUE::AccumulatorStack accumulators;
auto caches = std::make_unique<Eval::NNUE::AccumulatorCaches>(networks);
accumulators.reset(pos, networks, *caches);
auto accumulators = std::make_unique<Eval::NNUE::AccumulatorStack>();
auto caches = std::make_unique<Eval::NNUE::AccumulatorCaches>(networks);
std::stringstream ss;
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
@@ -111,12 +107,12 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) {
ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15);
auto [psqt, positional] = networks.big.evaluate(pos, accumulators, &caches->big);
auto [psqt, positional] = networks.big.evaluate(pos, *accumulators, caches->big);
Value v = psqt + positional;
v = pos.side_to_move() == WHITE ? v : -v;
ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n";
v = evaluate(networks, pos, accumulators, *caches, VALUE_ZERO);
v = evaluate(networks, pos, *accumulators, *caches, VALUE_ZERO);
v = pos.side_to_move() == WHITE ? v : -v;
ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)";
ss << " [with scaled NNUE, ...]";
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ namespace Eval {
// for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro or the location where this macro is defined, as it is used
// in the Makefile/Fishtest.
#define EvalFileDefaultNameBig "nn-1c0000000000.nnue"
#define EvalFileDefaultNameBig "nn-c288c895ea92.nnue"
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
namespace NNUE {
@@ -44,7 +44,7 @@ class AccumulatorStack;
std::string trace(Position& pos, const Eval::NNUE::Networks& networks);
int simple_eval(const Position& pos, Color c);
int simple_eval(const Position& pos);
bool use_smallnet(const Position& pos);
Value evaluate(const NNUE::Networks& networks,
const Position& pos,
+150 -48
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <cassert>
#include <cmath>
#include <cstdint>
@@ -28,67 +29,58 @@
#include <limits>
#include <type_traits> // IWYU pragma: keep
#include "memory.h"
#include "misc.h"
#include "position.h"
namespace Stockfish {
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int PAWN_HISTORY_BASE_SIZE = 8192; // has to be a power of 2
constexpr int UINT_16_HISTORY_SIZE = std::numeric_limits<uint16_t>::max() + 1;
constexpr int CORRHIST_BASE_SIZE = UINT_16_HISTORY_SIZE;
constexpr int CORRECTION_HISTORY_LIMIT = 1024;
constexpr int LOW_PLY_HISTORY_SIZE = 4;
constexpr int LOW_PLY_HISTORY_SIZE = 5;
static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0,
"PAWN_HISTORY_SIZE has to be a power of 2");
static_assert((PAWN_HISTORY_BASE_SIZE & (PAWN_HISTORY_BASE_SIZE - 1)) == 0,
"PAWN_HISTORY_BASE_SIZE has to be a power of 2");
static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0,
"CORRECTION_HISTORY_SIZE has to be a power of 2");
enum PawnHistoryType {
Normal,
Correction
};
template<PawnHistoryType T = Normal>
inline int pawn_structure_index(const Position& pos) {
return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1);
}
inline int minor_piece_index(const Position& pos) {
return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1);
}
template<Color c>
inline int non_pawn_index(const Position& pos) {
return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1);
}
static_assert((CORRHIST_BASE_SIZE & (CORRHIST_BASE_SIZE - 1)) == 0,
"CORRHIST_BASE_SIZE has to be a power of 2");
// StatsEntry is the container of various numerical statistics. We use a class
// instead of a naked value to directly call history update operator<<() on
// the entry. The first template parameter T is the base type of the array,
// and the second template parameter D limits the range of updates in [-D, D]
// when we update values with the << operator
template<typename T, int D>
class StatsEntry {
template<typename T, int D, bool Atomic = false>
struct StatsEntry {
static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
T entry;
private:
std::conditional_t<Atomic, std::atomic<T>, T> entry;
public:
StatsEntry& operator=(const T& v) {
entry = v;
return *this;
void operator=(const T& v) {
if constexpr (Atomic)
entry.store(v, std::memory_order_relaxed);
else
entry = v;
}
operator T() const {
if constexpr (Atomic)
return entry.load(std::memory_order_relaxed);
else
return entry;
}
operator const T&() const { return entry; }
void operator<<(int bonus) {
// Make sure that bonus is in range [-D, D]
int clampedBonus = std::clamp(bonus, -D, D);
entry += clampedBonus - entry * std::abs(clampedBonus) / D;
T val = *this;
*this = val + clampedBonus - val * std::abs(clampedBonus) / D;
assert(std::abs(entry) <= D);
assert(std::abs(T(*this)) <= D);
}
};
@@ -100,16 +92,51 @@ enum StatsType {
template<typename T, int D, std::size_t... Sizes>
using Stats = MultiArray<StatsEntry<T, D>, Sizes...>;
template<typename T, int D, std::size_t... Sizes>
using AtomicStats = MultiArray<StatsEntry<T, D, true>, Sizes...>;
// DynStats is a dynamically sized array of Stats, used for thread-shared histories
// which should scale with the total number of threads. The SizeMultiplier gives
// the per-thread allocation count of T.
template<typename T, int SizeMultiplier>
struct DynStats {
explicit DynStats(size_t s) {
size = s * SizeMultiplier;
data = make_unique_large_page<T[]>(size);
}
// Sets all values in the range to 0
void clear_range(int value, size_t threadIdx, size_t numaTotal) {
size_t start = uint64_t(threadIdx) * size / numaTotal;
assert(start < size);
size_t end = threadIdx + 1 == numaTotal ? size : uint64_t(threadIdx + 1) * size / numaTotal;
while (start < end)
data[start++].fill(value);
}
size_t get_size() const { return size; }
T& operator[](size_t index) {
assert(index < size);
return data.get()[index];
}
const T& operator[](size_t index) const {
assert(index < size);
return data.get()[index];
}
private:
size_t size;
LargePagePtr<T[]> data;
};
// ButterflyHistory records how often quiet moves have been successful or unsuccessful
// during the current search, and is used for reduction and move ordering decisions.
// It uses 2 tables (one for each color) indexed by the move's from and to squares,
// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo)
using ButterflyHistory = Stats<std::int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
// see https://www.chessprogramming.org/Butterfly_Boards
using ButterflyHistory = Stats<std::int16_t, 7183, COLOR_NB, UINT_16_HISTORY_SIZE>;
// LowPlyHistory is adressed by play and move's from and to squares, used
// LowPlyHistory is addressed by ply and move's from and to squares, used
// to improve move ordering near the root
using LowPlyHistory =
Stats<std::int16_t, 7183, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>;
using LowPlyHistory = Stats<std::int16_t, 7183, LOW_PLY_HISTORY_SIZE, UINT_16_HISTORY_SIZE>;
// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
using CapturePieceToHistory = Stats<std::int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
@@ -120,11 +147,11 @@ using PieceToHistory = Stats<std::int16_t, 30000, PIECE_NB, SQUARE_NB>;
// ContinuationHistory is the combined history of a given pair of moves, usually
// the current one given a previous one. The nested history table is based on
// PieceToHistory instead of ButterflyBoards.
// (~63 elo)
using ContinuationHistory = MultiArray<PieceToHistory, PIECE_NB, SQUARE_NB>;
// PawnHistory is addressed by the pawn structure and a move's [piece][to]
using PawnHistory = Stats<std::int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;
using PawnHistory =
DynStats<AtomicStats<std::int16_t, 8192, PIECE_NB, SQUARE_NB>, PAWN_HISTORY_BASE_SIZE>;
// Correction histories record differences between the static evaluation of
// positions and their search score. It is used to improve the static evaluation
@@ -138,11 +165,27 @@ enum CorrHistType {
Continuation, // Combined history of move pairs
};
template<typename T, int D>
struct CorrectionBundle {
StatsEntry<T, D, true> pawn;
StatsEntry<T, D, true> minor;
StatsEntry<T, D, true> nonPawnWhite;
StatsEntry<T, D, true> nonPawnBlack;
void operator=(T val) {
pawn = val;
minor = val;
nonPawnWhite = val;
nonPawnBlack = val;
}
};
namespace Detail {
template<CorrHistType>
struct CorrHistTypedef {
using type = Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, CORRECTION_HISTORY_SIZE, COLOR_NB>;
using type =
DynStats<Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB>, CORRHIST_BASE_SIZE>;
};
template<>
@@ -157,15 +200,74 @@ struct CorrHistTypedef<Continuation> {
template<>
struct CorrHistTypedef<NonPawn> {
using type =
Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, CORRECTION_HISTORY_SIZE, COLOR_NB, COLOR_NB>;
using type = DynStats<Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, COLOR_NB>,
CORRHIST_BASE_SIZE>;
};
}
using UnifiedCorrectionHistory =
DynStats<MultiArray<CorrectionBundle<std::int16_t, CORRECTION_HISTORY_LIMIT>, COLOR_NB>,
CORRHIST_BASE_SIZE>;
template<CorrHistType T>
using CorrectionHistory = typename Detail::CorrHistTypedef<T>::type;
using TTMoveHistory = StatsEntry<std::int16_t, 8192>;
// Set of histories shared between groups of threads. To avoid excessive
// cross-node data transfer, histories are shared only between threads
// on a given NUMA node. The passed size must be a power of two to make
// the indexing more efficient.
struct SharedHistories {
SharedHistories(size_t threadCount) :
correctionHistory(threadCount),
pawnHistory(threadCount) {
assert((threadCount & (threadCount - 1)) == 0 && threadCount != 0);
sizeMinus1 = correctionHistory.get_size() - 1;
pawnHistSizeMinus1 = pawnHistory.get_size() - 1;
}
size_t get_size() const { return sizeMinus1 + 1; }
auto& pawn_entry(const Position& pos) {
return pawnHistory[pos.pawn_key() & pawnHistSizeMinus1];
}
const auto& pawn_entry(const Position& pos) const {
return pawnHistory[pos.pawn_key() & pawnHistSizeMinus1];
}
auto& pawn_correction_entry(const Position& pos) {
return correctionHistory[pos.pawn_key() & sizeMinus1];
}
const auto& pawn_correction_entry(const Position& pos) const {
return correctionHistory[pos.pawn_key() & sizeMinus1];
}
auto& minor_piece_correction_entry(const Position& pos) {
return correctionHistory[pos.minor_piece_key() & sizeMinus1];
}
const auto& minor_piece_correction_entry(const Position& pos) const {
return correctionHistory[pos.minor_piece_key() & sizeMinus1];
}
template<Color c>
auto& nonpawn_correction_entry(const Position& pos) {
return correctionHistory[pos.non_pawn_key(c) & sizeMinus1];
}
template<Color c>
const auto& nonpawn_correction_entry(const Position& pos) const {
return correctionHistory[pos.non_pawn_key(c) & sizeMinus1];
}
UnifiedCorrectionHistory correctionHistory;
PawnHistory pawnHistory;
private:
size_t sizeMinus1, pawnHistSizeMinus1;
};
} // namespace Stockfish
#endif // #ifndef HISTORY_H_INCLUDED
+11 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,28 +17,31 @@
*/
#include <iostream>
#include <memory>
#include "bitboard.h"
#include "misc.h"
#include "position.h"
#include "types.h"
#include "uci.h"
#include "tune.h"
#include "uci.h"
using namespace Stockfish;
int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl;
Distributed::init();
if (Distributed::is_root())
std::cout << engine_info() << std::endl;
Bitboards::init();
Position::init();
UCIEngine uci(argc, argv);
auto uci = std::make_unique<UCIEngine>(argc, argv);
Tune::init(uci.engine_options());
Tune::init(uci->engine_options());
uci.loop();
uci->loop();
Distributed::finalize();
return 0;
}
+9 -78
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,12 +55,6 @@
// the calls at compile time), try to load them at runtime. To do this we need
// first to define the corresponding function pointers.
extern "C" {
using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE);
using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID);
using AdjustTokenPrivileges_t =
bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD);
}
#endif
@@ -106,77 +100,14 @@ void std_aligned_free(void* ptr) {
static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) {
#if !defined(_WIN64)
return nullptr;
#else
HANDLE hProcessToken{};
LUID luid{};
void* mem = nullptr;
const size_t largePageSize = GetLargePageMinimum();
if (!largePageSize)
return nullptr;
// Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges
HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll"));
if (!hAdvapi32)
hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
auto OpenProcessToken_f =
OpenProcessToken_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken"));
if (!OpenProcessToken_f)
return nullptr;
auto LookupPrivilegeValueA_f =
LookupPrivilegeValueA_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"));
if (!LookupPrivilegeValueA_f)
return nullptr;
auto AdjustTokenPrivileges_f =
AdjustTokenPrivileges_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"));
if (!AdjustTokenPrivileges_f)
return nullptr;
// We need SeLockMemoryPrivilege, so try to enable it for the process
if (!OpenProcessToken_f( // OpenProcessToken()
GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
return nullptr;
if (LookupPrivilegeValueA_f(nullptr, "SeLockMemoryPrivilege", &luid))
{
TOKEN_PRIVILEGES tp{};
TOKEN_PRIVILEGES prevTp{};
DWORD prevTpLen = 0;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges()
// succeeds, we still need to query GetLastError() to ensure that the privileges
// were actually obtained.
if (AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp,
&prevTpLen)
&& GetLastError() == ERROR_SUCCESS)
{
// Round up size to full pages and allocate
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES,
PAGE_READWRITE);
// Privilege no longer needed, restore previous state
AdjustTokenPrivileges_f(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr);
}
}
CloseHandle(hProcessToken);
return mem;
#endif
return windows_try_with_large_page_priviliges(
[&](size_t largePageSize) {
// Round up size to full pages and allocate
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
return VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES,
PAGE_READWRITE);
},
[]() { return (void*) nullptr; });
}
void* aligned_large_pages_alloc(size_t allocSize) {
+106 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,36 @@
#include "types.h"
#if defined(_WIN64)
#if _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
#endif
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <windows.h>
// Some Windows headers (RPC/old headers) define short macros such
// as 'small' expanding to 'char', which breaks identifiers in the code.
// Undefine those macros immediately after including <windows.h>.
#ifdef small
#undef small
#endif
#include <psapi.h>
extern "C" {
using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE);
using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID);
using AdjustTokenPrivileges_t =
bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD);
}
#endif
namespace Stockfish {
void* std_aligned_alloc(size_t alignment, size_t size);
@@ -52,7 +82,6 @@ void memory_deleter(T* ptr, FREE_FUNC free_func) {
ptr->~T();
free_func(ptr);
return;
}
// Frees memory which was placed there with placement new.
@@ -212,6 +241,81 @@ T* align_ptr_up(T* ptr) {
reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
}
#if defined(_WIN32)
template<typename FuncYesT, typename FuncNoT>
auto windows_try_with_large_page_priviliges([[maybe_unused]] FuncYesT&& fyes, FuncNoT&& fno) {
#if !defined(_WIN64)
return fno();
#else
HANDLE hProcessToken{};
LUID luid{};
const size_t largePageSize = GetLargePageMinimum();
if (!largePageSize)
return fno();
// Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges
HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll"));
if (!hAdvapi32)
hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
auto OpenProcessToken_f =
OpenProcessToken_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken"));
if (!OpenProcessToken_f)
return fno();
auto LookupPrivilegeValueA_f =
LookupPrivilegeValueA_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"));
if (!LookupPrivilegeValueA_f)
return fno();
auto AdjustTokenPrivileges_f =
AdjustTokenPrivileges_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"));
if (!AdjustTokenPrivileges_f)
return fno();
// We need SeLockMemoryPrivilege, so try to enable it for the process
if (!OpenProcessToken_f( // OpenProcessToken()
GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
return fno();
if (!LookupPrivilegeValueA_f(nullptr, "SeLockMemoryPrivilege", &luid))
return fno();
TOKEN_PRIVILEGES tp{};
TOKEN_PRIVILEGES prevTp{};
DWORD prevTpLen = 0;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges()
// succeeds, we still need to query GetLastError() to ensure that the privileges
// were actually obtained.
if (!AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp,
&prevTpLen)
|| GetLastError() != ERROR_SUCCESS)
return fno();
auto&& ret = fyes(largePageSize);
// Privilege no longer needed, restore previous state
AdjustTokenPrivileges_f(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr);
CloseHandle(hProcessToken);
return std::forward<decltype(ret)>(ret);
#endif
}
#endif
} // namespace Stockfish
+7 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,11 +40,11 @@ namespace Stockfish {
namespace {
// Version number or dev.
constexpr std::string_view version = "17.1";
constexpr std::string_view version = "dev";
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
// can toggle the logging of std::cout and std:cin at runtime whilst preserving
// can toggle the logging of std::cout and std::cin at runtime whilst preserving
// usual I/O functionality, all without changing a single line of code!
// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
@@ -237,6 +237,9 @@ std::string compiler_info() {
compiler += "\nCompilation settings : ";
compiler += (Is64Bit ? "64bit" : "32bit");
#if defined(USE_AVX512ICL)
compiler += " AVX512ICL";
#endif
#if defined(USE_VNNI)
compiler += " VNNI";
#endif
@@ -256,12 +259,12 @@ std::string compiler_info() {
#if defined(USE_SSE2)
compiler += " SSE2";
#endif
compiler += (HasPopCnt ? " POPCNT" : "");
#if defined(USE_NEON_DOTPROD)
compiler += " NEON_DOTPROD";
#elif defined(USE_NEON)
compiler += " NEON";
#endif
compiler += (HasPopCnt ? " POPCNT" : "");
#if !defined(NDEBUG)
compiler += " DEBUG";
+141 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,11 +23,15 @@
#include <array>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <exception> // IWYU pragma: keep
// IWYU pragma: no_include <__exception/terminate.h>
#include <functional>
#include <iosfwd>
#include <optional>
#include <cstring>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
@@ -130,10 +134,21 @@ class ValueList {
public:
std::size_t size() const { return size_; }
void push_back(const T& value) { values_[size_++] = value; }
const T* begin() const { return values_; }
const T* end() const { return values_ + size_; }
const T& operator[](int index) const { return values_[index]; }
int ssize() const { return int(size_); }
void push_back(const T& value) {
assert(size_ < MaxSize);
values_[size_++] = value;
}
const T* begin() const { return values_; }
const T* end() const { return values_ + size_; }
const T& operator[](int index) const { return values_[index]; }
T* make_space(size_t count) {
T* result = &values_[size_];
size_ += count;
assert(size_ <= MaxSize);
return result;
}
private:
T values_[MaxSize];
@@ -291,6 +306,94 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
}
template<typename T>
inline void hash_combine(std::size_t& seed, const T& v) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template<>
inline void hash_combine(std::size_t& seed, const std::size_t& v) {
seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template<typename T>
inline std::size_t get_raw_data_hash(const T& value) {
return std::hash<std::string_view>{}(
std::string_view(reinterpret_cast<const char*>(&value), sizeof(value)));
}
template<std::size_t Capacity>
class FixedString {
public:
FixedString() :
length_(0) {
data_[0] = '\0';
}
FixedString(const char* str) {
size_t len = std::strlen(str);
if (len > Capacity)
std::terminate();
std::memcpy(data_, str, len);
length_ = len;
data_[length_] = '\0';
}
FixedString(const std::string& str) {
if (str.size() > Capacity)
std::terminate();
std::memcpy(data_, str.data(), str.size());
length_ = str.size();
data_[length_] = '\0';
}
std::size_t size() const { return length_; }
std::size_t capacity() const { return Capacity; }
const char* c_str() const { return data_; }
const char* data() const { return data_; }
char& operator[](std::size_t i) { return data_[i]; }
const char& operator[](std::size_t i) const { return data_[i]; }
FixedString& operator+=(const char* str) {
size_t len = std::strlen(str);
if (length_ + len > Capacity)
std::terminate();
std::memcpy(data_ + length_, str, len);
length_ += len;
data_[length_] = '\0';
return *this;
}
FixedString& operator+=(const FixedString& other) { return (*this += other.c_str()); }
operator std::string() const { return std::string(data_, length_); }
operator std::string_view() const { return std::string_view(data_, length_); }
template<typename T>
bool operator==(const T& other) const noexcept {
return (std::string_view) (*this) == other;
}
template<typename T>
bool operator!=(const T& other) const noexcept {
return (std::string_view) (*this) != other;
}
void clear() {
length_ = 0;
data_[0] = '\0';
}
private:
char data_[Capacity + 1]; // +1 for null terminator
std::size_t length_;
};
struct CommandLine {
public:
CommandLine(int _argc, char** _argv) :
@@ -317,6 +420,38 @@ void move_to_front(std::vector<T>& vec, Predicate pred) {
}
}
#if defined(__GNUC__)
#define sf_always_inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define sf_always_inline __forceinline
#else
// do nothing for other compilers
#define sf_always_inline
#endif
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ >= 13
#define sf_assume(cond) __attribute__((assume(cond)))
#else
#define sf_assume(cond) \
do \
{ \
if (!(cond)) \
__builtin_unreachable(); \
} while (0)
#endif
#else
// do nothing for other compilers
#define sf_assume(cond)
#endif
} // namespace Stockfish
template<std::size_t N>
struct std::hash<Stockfish::FixedString<N>> {
std::size_t operator()(const Stockfish::FixedString<N>& fstr) const noexcept {
return std::hash<std::string_view>{}((std::string_view) fstr);
}
};
#endif // #ifndef MISC_H_INCLUDED
+96 -40
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,12 +24,89 @@
#include "bitboard.h"
#include "position.h"
#if defined(USE_AVX512ICL)
#include <array>
#include <algorithm>
#include <immintrin.h>
#endif
namespace Stockfish {
namespace {
#if defined(USE_AVX512ICL)
inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) {
// Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4
_mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList),
_mm512_maskz_compress_epi16(mask, vector));
return moveList + popcount(mask);
}
template<Direction offset>
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
alignas(64) static constexpr auto SPLAT_TABLE = [] {
std::array<Move, 64> table{};
for (int8_t i = 0; i < 64; i++)
{
Square from{std::clamp<int8_t>(i - offset, 0, 63)};
table[i] = {Move(from, Square{i})};
}
return table;
}();
auto table = reinterpret_cast<const __m512i*>(SPLAT_TABLE.data());
moveList =
write_moves(moveList, static_cast<uint32_t>(to_bb >> 0), _mm512_load_si512(table + 0));
moveList =
write_moves(moveList, static_cast<uint32_t>(to_bb >> 32), _mm512_load_si512(table + 1));
return moveList;
}
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
alignas(64) static constexpr auto SPLAT_TABLE = [] {
std::array<Move, 64> table{};
for (int8_t i = 0; i < 64; i++)
table[i] = {Move(SQUARE_ZERO, Square{i})};
return table;
}();
__m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw());
auto table = reinterpret_cast<const __m512i*>(SPLAT_TABLE.data());
moveList = write_moves(moveList, static_cast<uint32_t>(to_bb >> 0),
_mm512_or_si512(_mm512_load_si512(table + 0), fromVec));
moveList = write_moves(moveList, static_cast<uint32_t>(to_bb >> 32),
_mm512_or_si512(_mm512_load_si512(table + 1), fromVec));
return moveList;
}
#else
template<Direction offset>
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
while (to_bb)
{
Square to = pop_lsb(to_bb);
*moveList++ = Move(to - offset, to);
}
return moveList;
}
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
while (to_bb)
*moveList++ = Move(from, pop_lsb(to_bb));
return moveList;
}
#endif
template<GenType Type, Direction D, bool Enemy>
ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
Move* make_promotions(Move* moveList, [[maybe_unused]] Square to) {
constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS;
@@ -48,7 +125,7 @@ ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
Move* generate_pawn_moves(const Position& pos, Move* moveList, Bitboard target) {
constexpr Color Them = ~Us;
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
@@ -75,17 +152,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
b2 &= target;
}
while (b1)
{
Square to = pop_lsb(b1);
*moveList++ = Move(to - Up, to);
}
while (b2)
{
Square to = pop_lsb(b2);
*moveList++ = Move(to - Up - Up, to);
}
moveList = splat_pawn_moves<Up>(moveList, b1);
moveList = splat_pawn_moves<Up + Up>(moveList, b2);
}
// Promotions and underpromotions
@@ -114,17 +182,8 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
Bitboard b2 = shift<UpLeft>(pawnsNotOn7) & enemies;
while (b1)
{
Square to = pop_lsb(b1);
*moveList++ = Move(to - UpRight, to);
}
while (b2)
{
Square to = pop_lsb(b2);
*moveList++ = Move(to - UpLeft, to);
}
moveList = splat_pawn_moves<UpRight>(moveList, b1);
moveList = splat_pawn_moves<UpLeft>(moveList, b2);
if (pos.ep_square() != SQ_NONE)
{
@@ -134,7 +193,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
return moveList;
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
b1 = pawnsNotOn7 & attacks_bb<PAWN>(pos.ep_square(), Them);
assert(b1);
@@ -148,7 +207,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
template<Color Us, PieceType Pt>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
Move* generate_moves(const Position& pos, Move* moveList, Bitboard target) {
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
@@ -159,8 +218,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target)
Square from = pop_lsb(bb);
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
while (b)
*moveList++ = Move(from, pop_lsb(b));
moveList = splat_moves(moveList, from, b);
}
return moveList;
@@ -168,7 +226,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target)
template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
Move* generate_all(const Position& pos, Move* moveList) {
static_assert(Type != LEGAL, "Unsupported type in generate_all()");
@@ -192,8 +250,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
while (b)
*moveList++ = Move(ksq, pop_lsb(b));
moveList = splat_moves(moveList, ksq, b);
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
@@ -213,7 +270,7 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
//
// Returns a pointer to the end of the move list.
template<GenType Type>
ExtMove* generate(const Position& pos, ExtMove* moveList) {
Move* generate(const Position& pos, Move* moveList) {
static_assert(Type != LEGAL, "Unsupported type in generate()");
assert((Type == EVASIONS) == bool(pos.checkers()));
@@ -225,21 +282,20 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) {
}
// Explicit template instantiations
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
template Move* generate<CAPTURES>(const Position&, Move*);
template Move* generate<QUIETS>(const Position&, Move*);
template Move* generate<EVASIONS>(const Position&, Move*);
template Move* generate<NON_EVASIONS>(const Position&, Move*);
// generate<LEGAL> generates all the legal moves in the given position
template<>
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
Move* generate<LEGAL>(const Position& pos, Move* moveList) {
Color us = pos.side_to_move();
Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us);
Square ksq = pos.square<KING>(us);
ExtMove* cur = moveList;
Move* cur = moveList;
moveList =
pos.checkers() ? generate<EVASIONS>(pos, moveList) : generate<NON_EVASIONS>(pos, moveList);
+7 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ struct ExtMove: public Move {
inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; }
template<GenType>
ExtMove* generate(const Position& pos, ExtMove* moveList);
Move* generate(const Position& pos, Move* moveList);
// The MoveList struct wraps the generate() function and returns a convenient
// list of moves. Using MoveList is sometimes preferable to directly calling
@@ -59,13 +59,13 @@ struct MoveList {
explicit MoveList(const Position& pos) :
last(generate<T>(pos, moveList)) {}
const ExtMove* begin() const { return moveList; }
const ExtMove* end() const { return last; }
size_t size() const { return last - moveList; }
bool contains(Move move) const { return std::find(begin(), end(), move) != end(); }
const Move* begin() const { return moveList; }
const Move* end() const { return last; }
size_t size() const { return last - moveList; }
bool contains(Move move) const { return std::find(begin(), end(), move) != end(); }
private:
ExtMove moveList[MAX_MOVES], *last;
Move moveList[MAX_MOVES], *last;
};
} // namespace Stockfish
+74 -81
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include <cassert>
#include <limits>
#include <utility>
#include "bitboard.h"
#include "misc.h"
@@ -55,6 +56,7 @@ enum Stages {
QCAPTURE
};
// Sort moves in descending order up to and including a given limit.
// The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
@@ -85,14 +87,14 @@ MovePicker::MovePicker(const Position& p,
const LowPlyHistory* lph,
const CapturePieceToHistory* cph,
const PieceToHistory** ch,
const PawnHistory* ph,
const SharedHistories* sh,
int pl) :
pos(p),
mainHistory(mh),
lowPlyHistory(lph),
captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
sharedHistory(sh),
ttMove(ttm),
depth(d),
ply(pl) {
@@ -113,86 +115,79 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo
threshold(th) {
assert(!pos.checkers());
stage = PROBCUT_TT
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm));
}
// Assigns a numerical value to each move in a list, used for sorting.
// Captures are ordered by Most Valuable Victim (MVV), preferring captures
// with a good history. Quiets moves are ordered using the history tables.
template<GenType Type>
void MovePicker::score() {
ExtMove* MovePicker::score(MoveList<Type>& ml) {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
[[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook,
threatenedPieces;
Color us = pos.side_to_move();
[[maybe_unused]] Bitboard threatByLesser[KING + 1];
if constexpr (Type == QUIETS)
{
Color us = pos.side_to_move();
threatenedByPawn = pos.attacks_by<PAWN>(~us);
threatenedByMinor =
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
// Pieces threatened by pieces of lesser material value
threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook)
| (pos.pieces(us, ROOK) & threatenedByMinor)
| (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn);
threatByLesser[PAWN] = 0;
threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us);
threatByLesser[ROOK] =
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT];
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
threatByLesser[KING] = pos.attacks_by<QUEEN>(~us) | threatByLesser[QUEEN];
}
for (auto& m : *this)
ExtMove* it = cur;
for (auto move : ml)
{
ExtMove& m = *it++;
m = move;
const Square from = m.from_sq();
const Square to = m.to_sq();
const Piece pc = pos.moved_piece(m);
const PieceType pt = type_of(pc);
const Piece capturedPiece = pos.piece_on(to);
if constexpr (Type == CAPTURES)
m.value =
7 * int(PieceValue[pos.piece_on(m.to_sq())])
+ (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))];
m.value = (*captureHistory)[pc][to][type_of(capturedPiece)]
+ 7 * int(PieceValue[capturedPiece]);
else if constexpr (Type == QUIETS)
{
Piece pc = pos.moved_piece(m);
PieceType pt = type_of(pc);
Square from = m.from_sq();
Square to = m.to_sq();
// histories
m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()];
m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to];
m.value = 2 * (*mainHistory)[us][m.raw()];
m.value += 2 * sharedHistory->pawn_entry(pos)[pc][to];
m.value += (*continuationHistory[0])[pc][to];
m.value += (*continuationHistory[1])[pc][to];
m.value += (*continuationHistory[2])[pc][to];
m.value += (*continuationHistory[3])[pc][to];
m.value += (*continuationHistory[4])[pc][to] / 3;
m.value += (*continuationHistory[5])[pc][to];
// bonus for checks
m.value += bool(pos.check_squares(pt) & to) * 16384;
m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384;
// bonus for escaping from capture
m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51700
: pt == ROOK && !(to & threatenedByMinor) ? 25600
: !(to & threatenedByPawn) ? 14450
: 0)
: 0;
// penalty for moving to a square threatened by a lesser piece
// or bonus for escaping an attack by a lesser piece.
int v = threatByLesser[pt] & to ? -19 : 20 * bool(threatByLesser[pt] & from);
m.value += PieceValue[pt] * v;
// malus for putting piece en prise
m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000
: pt == ROOK && bool(to & threatenedByMinor) ? 24335
: 0);
if (ply < LOW_PLY_HISTORY_SIZE)
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply);
m.value += 8 * (*lowPlyHistory)[ply][m.raw()] / (1 + ply);
}
else // Type == EVASIONS
{
if (pos.capture_stage(m))
m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28);
m.value = PieceValue[capturedPiece] + (1 << 28);
else
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
+ (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][m.to_sq()];
m.value = (*mainHistory)[us][m.raw()] + (*continuationHistory[0])[pc][to];
}
}
return it;
}
// Returns the next move satisfying a predicate function.
@@ -200,7 +195,7 @@ void MovePicker::score() {
template<typename Pred>
Move MovePicker::select(Pred filter) {
for (; cur < endMoves; ++cur)
for (; cur < endCur; ++cur)
if (*cur != ttMove && filter())
return *cur++;
@@ -212,8 +207,7 @@ Move MovePicker::select(Pred filter) {
// picking the move with the highest score from a list of generated moves.
Move MovePicker::next_move() {
auto quiet_threshold = [](Depth d) { return -3560 * d; };
constexpr int goodQuietThreshold = -14000;
top:
switch (stage)
{
@@ -227,20 +221,23 @@ top:
case CAPTURE_INIT :
case PROBCUT_INIT :
case QCAPTURE_INIT :
cur = endBadCaptures = moves;
endMoves = generate<CAPTURES>(pos, cur);
case QCAPTURE_INIT : {
MoveList<CAPTURES> ml(pos);
score<CAPTURES>();
partial_insertion_sort(cur, endMoves, std::numeric_limits<int>::min());
cur = endBadCaptures = moves;
endCur = endCaptures = score<CAPTURES>(ml);
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
goto top;
}
case GOOD_CAPTURE :
if (select([&]() {
// Move losing capture to endBadCaptures to be tried later
return pos.see_ge(*cur, -cur->value / 18) ? true
: (*endBadCaptures++ = *cur, false);
if (pos.see_ge(*cur, -cur->value / 18))
return true;
std::swap(*endBadCaptures++, *cur);
return false;
}))
return *(cur - 1);
@@ -250,29 +247,23 @@ top:
case QUIET_INIT :
if (!skipQuiets)
{
cur = endBadCaptures;
endMoves = beginBadQuiets = endBadQuiets = generate<QUIETS>(pos, cur);
MoveList<QUIETS> ml(pos);
score<QUIETS>();
partial_insertion_sort(cur, endMoves, quiet_threshold(depth));
endCur = endGenerated = score<QUIETS>(ml);
partial_insertion_sort(cur, endCur, -3560 * depth);
}
++stage;
[[fallthrough]];
case GOOD_QUIET :
if (!skipQuiets && select([]() { return true; }))
{
if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth))
return *(cur - 1);
// Remaining quiets are bad
beginBadQuiets = cur - 1;
}
if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; }))
return *(cur - 1);
// Prepare the pointers to loop over the bad captures
cur = moves;
endMoves = endBadCaptures;
cur = moves;
endCur = endBadCaptures;
++stage;
[[fallthrough]];
@@ -281,27 +272,29 @@ top:
if (select([]() { return true; }))
return *(cur - 1);
// Prepare the pointers to loop over the bad quiets
cur = beginBadQuiets;
endMoves = endBadQuiets;
// Prepare the pointers to loop over quiets again
cur = endCaptures;
endCur = endGenerated;
++stage;
[[fallthrough]];
case BAD_QUIET :
if (!skipQuiets)
return select([]() { return true; });
return select([&]() { return cur->value <= goodQuietThreshold; });
return Move::none();
case EVASION_INIT :
cur = moves;
endMoves = generate<EVASIONS>(pos, cur);
case EVASION_INIT : {
MoveList<EVASIONS> ml(pos);
score<EVASIONS>();
partial_insertion_sort(cur, endMoves, std::numeric_limits<int>::min());
cur = moves;
endCur = endGenerated = score<EVASIONS>(ml);
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
[[fallthrough]];
}
case EVASION :
case QCAPTURE :
+7 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,7 +45,7 @@ class MovePicker {
const LowPlyHistory*,
const CapturePieceToHistory*,
const PieceToHistory**,
const PawnHistory*,
const SharedHistories*,
int);
MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
Move next_move();
@@ -54,19 +54,19 @@ class MovePicker {
private:
template<typename Pred>
Move select(Pred);
template<GenType>
void score();
template<GenType T>
ExtMove* score(MoveList<T>&);
ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; }
ExtMove* end() { return endCur; }
const Position& pos;
const ButterflyHistory* mainHistory;
const LowPlyHistory* lowPlyHistory;
const CapturePieceToHistory* captureHistory;
const PieceToHistory** continuationHistory;
const PawnHistory* pawnHistory;
const SharedHistories* sharedHistory;
Move ttMove;
ExtMove * cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets;
ExtMove * cur, *endCur, *endBadCaptures, *endCaptures, *endGenerated;
int stage;
int threshold;
Depth depth;
+361
View File
@@ -0,0 +1,361 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Definition of input features FullThreats of NNUE evaluation function
#include "full_threats.h"
#include <array>
#include <cstdint>
#include <initializer_list>
#include <utility>
#include "../../bitboard.h"
#include "../../misc.h"
#include "../../position.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Features {
struct HelperOffsets {
int cumulativePieceOffset, cumulativeOffset;
};
// Information on a particular pair of pieces and whether they should be excluded
struct PiecePairData {
// Layout: bits 8..31 are the index contribution of this piece pair, bits 0 and 1 are exclusion info
uint32_t data;
constexpr PiecePairData() :
data(0) {}
constexpr PiecePairData(bool excluded_pair,
bool semi_excluded_pair,
IndexType feature_index_base) :
data((uint32_t(excluded_pair) << 1) | (uint32_t(semi_excluded_pair && !excluded_pair))
| (uint32_t(feature_index_base) << 8)) {}
// lsb: excluded if from < to; 2nd lsb: always excluded
constexpr uint8_t excluded_pair_info() const { return static_cast<uint8_t>(data); }
constexpr IndexType feature_index_base() const { return static_cast<IndexType>(data >> 8); }
};
constexpr std::array<Piece, 12> AllPieces = {
W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
};
template<PieceType PT>
constexpr auto make_piece_indices_type() {
static_assert(PT != PieceType::PAWN);
std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};
for (Square from = SQ_A1; from <= SQ_H8; ++from)
{
Bitboard attacks = PseudoAttacks[PT][from];
for (Square to = SQ_A1; to <= SQ_H8; ++to)
{
out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);
}
}
return out;
}
template<Piece P>
constexpr auto make_piece_indices_piece() {
static_assert(type_of(P) == PieceType::PAWN);
std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB> out{};
constexpr Color C = color_of(P);
for (Square from = SQ_A1; from <= SQ_H8; ++from)
{
Bitboard attacks = PseudoAttacks[C][from];
for (Square to = SQ_A1; to <= SQ_H8; ++to)
{
out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks);
}
}
return out;
}
constexpr auto index_lut2_array() {
constexpr auto KNIGHT_ATTACKS = make_piece_indices_type<PieceType::KNIGHT>();
constexpr auto BISHOP_ATTACKS = make_piece_indices_type<PieceType::BISHOP>();
constexpr auto ROOK_ATTACKS = make_piece_indices_type<PieceType::ROOK>();
constexpr auto QUEEN_ATTACKS = make_piece_indices_type<PieceType::QUEEN>();
constexpr auto KING_ATTACKS = make_piece_indices_type<PieceType::KING>();
std::array<std::array<std::array<uint8_t, SQUARE_NB>, SQUARE_NB>, PIECE_NB> indices{};
indices[W_PAWN] = make_piece_indices_piece<W_PAWN>();
indices[B_PAWN] = make_piece_indices_piece<B_PAWN>();
indices[W_KNIGHT] = KNIGHT_ATTACKS;
indices[B_KNIGHT] = KNIGHT_ATTACKS;
indices[W_BISHOP] = BISHOP_ATTACKS;
indices[B_BISHOP] = BISHOP_ATTACKS;
indices[W_ROOK] = ROOK_ATTACKS;
indices[B_ROOK] = ROOK_ATTACKS;
indices[W_QUEEN] = QUEEN_ATTACKS;
indices[B_QUEEN] = QUEEN_ATTACKS;
indices[W_KING] = KING_ATTACKS;
indices[B_KING] = KING_ATTACKS;
return indices;
}
constexpr auto init_threat_offsets() {
std::array<HelperOffsets, PIECE_NB> indices{};
std::array<std::array<IndexType, SQUARE_NB>, PIECE_NB> offsets{};
int cumulativeOffset = 0;
for (Piece piece : AllPieces)
{
int pieceIdx = piece;
int cumulativePieceOffset = 0;
for (Square from = SQ_A1; from <= SQ_H8; ++from)
{
offsets[pieceIdx][from] = cumulativePieceOffset;
if (type_of(piece) != PAWN)
{
Bitboard attacks = PseudoAttacks[type_of(piece)][from];
cumulativePieceOffset += constexpr_popcount(attacks);
}
else if (from >= SQ_A2 && from <= SQ_H7)
{
Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb<WHITE>(square_bb(from))
: pawn_attacks_bb<BLACK>(square_bb(from));
cumulativePieceOffset += constexpr_popcount(attacks);
}
}
indices[pieceIdx] = {cumulativePieceOffset, cumulativeOffset};
cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset;
}
return std::pair{indices, offsets};
}
constexpr auto helper_offsets = init_threat_offsets().first;
// Lookup array for indexing threats
constexpr auto offsets = init_threat_offsets().second;
constexpr auto init_index_luts() {
std::array<std::array<PiecePairData, PIECE_NB>, PIECE_NB> indices{};
for (Piece attacker : AllPieces)
{
for (Piece attacked : AllPieces)
{
bool enemy = (attacker ^ attacked) == 8;
PieceType attackerType = type_of(attacker);
PieceType attackedType = type_of(attacked);
int map = FullThreats::map[attackerType - 1][attackedType - 1];
bool semi_excluded = attackerType == attackedType && (enemy || attackerType != PAWN);
IndexType feature = helper_offsets[attacker].cumulativeOffset
+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)
* helper_offsets[attacker].cumulativePieceOffset;
bool excluded = map < 0;
indices[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature);
}
}
return indices;
}
// The final index is calculated from summing data found in these two LUTs, as well
// as offsets[attacker][from]
// [attacker][attacked]
constexpr auto index_lut1 = init_index_luts();
// [attacker][from][to]
constexpr auto index_lut2 = index_lut2_array();
// Index of a feature for a given king position and another piece on some square
inline sf_always_inline IndexType FullThreats::make_index(
Color perspective, Piece attacker, Square from, Square to, Piece attacked, Square ksq) {
const std::int8_t orientation = OrientTBL[ksq] ^ (56 * perspective);
unsigned from_oriented = uint8_t(from) ^ orientation;
unsigned to_oriented = uint8_t(to) ^ orientation;
std::int8_t swap = 8 * perspective;
unsigned attacker_oriented = attacker ^ swap;
unsigned attacked_oriented = attacked ^ swap;
const auto piecePairData = index_lut1[attacker_oriented][attacked_oriented];
const bool less_than = from_oriented < to_oriented;
if ((piecePairData.excluded_pair_info() + less_than) & 2)
return FullThreats::Dimensions;
const IndexType index = piecePairData.feature_index_base()
+ offsets[attacker_oriented][from_oriented]
+ index_lut2[attacker_oriented][from_oriented][to_oriented];
sf_assume(index < Dimensions);
return index;
}
// Get a list of indices for active features in ascending order
void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) {
Square ksq = pos.square<KING>(perspective);
Bitboard occupied = pos.pieces();
for (Color color : {WHITE, BLACK})
{
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
Color c = Color(perspective ^ color);
Piece attacker = make_piece(c, pt);
Bitboard bb = pos.pieces(c, pt);
if (pt == PAWN)
{
auto right = (c == WHITE) ? NORTH_EAST : SOUTH_WEST;
auto left = (c == WHITE) ? NORTH_WEST : SOUTH_EAST;
auto attacks_left =
((c == WHITE) ? shift<NORTH_EAST>(bb) : shift<SOUTH_WEST>(bb)) & occupied;
auto attacks_right =
((c == WHITE) ? shift<NORTH_WEST>(bb) : shift<SOUTH_EAST>(bb)) & occupied;
while (attacks_left)
{
Square to = pop_lsb(attacks_left);
Square from = to - right;
Piece attacked = pos.piece_on(to);
IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
if (index < Dimensions)
active.push_back(index);
}
while (attacks_right)
{
Square to = pop_lsb(attacks_right);
Square from = to - left;
Piece attacked = pos.piece_on(to);
IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
if (index < Dimensions)
active.push_back(index);
}
}
else
{
while (bb)
{
Square from = pop_lsb(bb);
Bitboard attacks = (attacks_bb(pt, from, occupied)) & occupied;
while (attacks)
{
Square to = pop_lsb(attacks);
Piece attacked = pos.piece_on(to);
IndexType index =
make_index(perspective, attacker, from, to, attacked, ksq);
if (index < Dimensions)
active.push_back(index);
}
}
}
}
}
}
// Get a list of indices for recently changed features
void FullThreats::append_changed_indices(Color perspective,
Square ksq,
const DiffType& diff,
IndexList& removed,
IndexList& added,
FusedUpdateData* fusedData,
bool first) {
for (const auto& dirty : diff.list)
{
auto attacker = dirty.pc();
auto attacked = dirty.threatened_pc();
auto from = dirty.pc_sq();
auto to = dirty.threatened_sq();
auto add = dirty.add();
if (fusedData)
{
if (from == fusedData->dp2removed)
{
if (add)
{
if (first)
{
fusedData->dp2removedOriginBoard |= to;
continue;
}
}
else if (fusedData->dp2removedOriginBoard & to)
continue;
}
if (to != SQ_NONE && to == fusedData->dp2removed)
{
if (add)
{
if (first)
{
fusedData->dp2removedTargetBoard |= from;
continue;
}
}
else if (fusedData->dp2removedTargetBoard & from)
continue;
}
}
auto& insert = add ? added : removed;
const IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
if (index < Dimensions)
insert.push_back(index);
}
}
bool FullThreats::requires_refresh(const DiffType& diff, Color perspective) {
return perspective == diff.us && (int8_t(diff.ksq) & 0b100) != (int8_t(diff.prevKsq) & 0b100);
}
} // namespace Stockfish::Eval::NNUE::Features
+104
View File
@@ -0,0 +1,104 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Definition of input features Simplified_Threats of NNUE evaluation function
#ifndef NNUE_FEATURES_FULL_THREATS_INCLUDED
#define NNUE_FEATURES_FULL_THREATS_INCLUDED
#include <cstdint>
#include "../../misc.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish {
class Position;
}
namespace Stockfish::Eval::NNUE::Features {
static constexpr int numValidTargets[PIECE_NB] = {0, 6, 12, 10, 10, 12, 8, 0,
0, 6, 12, 10, 10, 12, 8, 0};
class FullThreats {
public:
// Feature name
static constexpr const char* Name = "Full_Threats(Friend)";
// Hash value embedded in the evaluation file
static constexpr std::uint32_t HashValue = 0x8f234cb8u;
// Number of feature dimensions
static constexpr IndexType Dimensions = 79856;
// clang-format off
// Orient a square according to perspective (rotates by 180 for black)
static constexpr std::int8_t OrientTBL[SQUARE_NB] = {
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
SQ_A1, SQ_A1, SQ_A1, SQ_A1, SQ_H1, SQ_H1, SQ_H1, SQ_H1,
};
static constexpr int map[PIECE_TYPE_NB-2][PIECE_TYPE_NB-2] = {
{0, 1, -1, 2, -1, -1},
{0, 1, 2, 3, 4, 5},
{0, 1, 2, 3, -1, 4},
{0, 1, 2, 3, -1, 4},
{0, 1, 2, 3, 4, 5},
{0, 1, 2, 3, -1, -1}
};
// clang-format on
struct FusedUpdateData {
Bitboard dp2removedOriginBoard = 0;
Bitboard dp2removedTargetBoard = 0;
Square dp2removed;
};
// Maximum number of simultaneously active features.
static constexpr IndexType MaxActiveDimensions = 128;
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
using DiffType = DirtyThreats;
static IndexType
make_index(Color perspective, Piece attkr, Square from, Square to, Piece attkd, Square ksq);
// Get a list of indices for active features
static void append_active_indices(Color perspective, const Position& pos, IndexList& active);
// Get a list of indices for recently changed features
static void append_changed_indices(Color perspective,
Square ksq,
const DiffType& diff,
IndexList& removed,
IndexList& added,
FusedUpdateData* fd = nullptr,
bool first = false);
// Returns whether the change stored in this DirtyPiece means
// that a full accumulator refresh is required.
static bool requires_refresh(const DiffType& diff, Color perspective);
};
} // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_FULL_THREATS_INCLUDED
+25 -40
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,62 +23,47 @@
#include "../../bitboard.h"
#include "../../position.h"
#include "../../types.h"
#include "../nnue_accumulator.h"
#include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Features {
// Index of a feature for a given king position and another piece on some square
template<Color Perspective>
inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) {
return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc]
+ KingBuckets[Perspective][ksq]);
IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) {
const IndexType flip = 56 * perspective;
return (IndexType(s) ^ OrientTBL[ksq] ^ flip) + PieceSquareIndex[perspective][pc]
+ KingBuckets[int(ksq) ^ flip];
}
// Get a list of indices for active features
template<Color Perspective>
void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) {
Square ksq = pos.square<KING>(Perspective);
void HalfKAv2_hm::append_active_indices(Color perspective, const Position& pos, IndexList& active) {
Square ksq = pos.square<KING>(perspective);
Bitboard bb = pos.pieces();
while (bb)
{
Square s = pop_lsb(bb);
active.push_back(make_index<Perspective>(s, pos.piece_on(s), ksq));
active.push_back(make_index(perspective, s, pos.piece_on(s), ksq));
}
}
// Explicit template instantiations
template void HalfKAv2_hm::append_active_indices<WHITE>(const Position& pos, IndexList& active);
template void HalfKAv2_hm::append_active_indices<BLACK>(const Position& pos, IndexList& active);
template IndexType HalfKAv2_hm::make_index<WHITE>(Square s, Piece pc, Square ksq);
template IndexType HalfKAv2_hm::make_index<BLACK>(Square s, Piece pc, Square ksq);
// Get a list of indices for recently changed features
template<Color Perspective>
void HalfKAv2_hm::append_changed_indices(Square ksq,
const DirtyPiece& dp,
IndexList& removed,
IndexList& added) {
for (int i = 0; i < dp.dirty_num; ++i)
{
if (dp.from[i] != SQ_NONE)
removed.push_back(make_index<Perspective>(dp.from[i], dp.piece[i], ksq));
if (dp.to[i] != SQ_NONE)
added.push_back(make_index<Perspective>(dp.to[i], dp.piece[i], ksq));
}
void HalfKAv2_hm::append_changed_indices(
Color perspective, Square ksq, const DiffType& diff, IndexList& removed, IndexList& added) {
removed.push_back(make_index(perspective, diff.from, diff.pc, ksq));
if (diff.to != SQ_NONE)
added.push_back(make_index(perspective, diff.to, diff.pc, ksq));
if (diff.remove_sq != SQ_NONE)
removed.push_back(make_index(perspective, diff.remove_sq, diff.remove_pc, ksq));
if (diff.add_sq != SQ_NONE)
added.push_back(make_index(perspective, diff.add_sq, diff.add_pc, ksq));
}
// Explicit template instantiations
template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq,
const DirtyPiece& dp,
IndexList& removed,
IndexList& added);
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq,
const DirtyPiece& dp,
IndexList& removed,
IndexList& added);
bool HalfKAv2_hm::requires_refresh(const DirtyPiece& dirtyPiece, Color perspective) {
return dirtyPiece.piece[0] == make_piece(perspective, KING);
bool HalfKAv2_hm::requires_refresh(const DiffType& diff, Color perspective) {
return diff.pc == make_piece(perspective, KING);
}
} // namespace Stockfish::Eval::NNUE::Features
+15 -31
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -75,68 +75,52 @@ class HalfKAv2_hm {
#define B(v) (v * PS_NB)
// clang-format off
static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = {
{ B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28),
static constexpr IndexType KingBuckets[SQUARE_NB] = {
B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28),
B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) },
{ B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0),
B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) }
B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0),
};
// clang-format on
#undef B
// clang-format off
// Orient a square according to perspective (rotates by 180 for black)
static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = {
{ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
static constexpr IndexType OrientTBL[SQUARE_NB] = {
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 },
{ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 }
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 ,
};
// clang-format on
// Maximum number of simultaneously active features.
static constexpr IndexType MaxActiveDimensions = 32;
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
using DiffType = DirtyPiece;
// Index of a feature for a given king position and another piece on some square
template<Color Perspective>
static IndexType make_index(Square s, Piece pc, Square ksq);
static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq);
// Get a list of indices for active features
template<Color Perspective>
static void append_active_indices(const Position& pos, IndexList& active);
static void append_active_indices(Color perspective, const Position& pos, IndexList& active);
// Get a list of indices for recently changed features
template<Color Perspective>
static void
append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
static void append_changed_indices(
Color perspective, Square ksq, const DiffType& diff, IndexList& removed, IndexList& added);
// Returns whether the change stored in this DirtyPiece means
// that a full accumulator refresh is required.
static bool requires_refresh(const DirtyPiece& dirtyPiece, Color perspective);
static bool requires_refresh(const DiffType& diff, Color perspective);
};
} // namespace Stockfish::Eval::NNUE::Features
+22 -17
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
#include <iostream>
#include "../nnue_common.h"
#include "simd.h"
#include "../simd.h"
/*
This file contains the definition for a fully connected layer (aka affine transform).
@@ -102,7 +102,7 @@ static void affine_transform_non_ssse3(std::int32_t* output,
product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]);
sum = vpadalq_s16(sum, product);
}
output[i] = Simd::neon_m128_reduce_add_epi32(sum);
output[i] = SIMD::neon_m128_reduce_add_epi32(sum);
#endif
}
@@ -181,6 +181,15 @@ class AffineTransform {
return !stream.fail();
}
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, get_raw_data_hash(biases));
hash_combine(h, get_raw_data_hash(weights));
hash_combine(h, get_hash_value(0));
return h;
}
// Forward propagation
void propagate(const InputType* input, OutputType* output) const {
@@ -191,20 +200,20 @@ class AffineTransform {
#if defined(USE_AVX512)
using vec_t = __m512i;
#define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32
#elif defined(USE_AVX2)
using vec_t = __m256i;
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32
#elif defined(USE_SSSE3)
using vec_t = __m128i;
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32
#elif defined(USE_NEON_DOTPROD)
using vec_t = int32x4_t;
#define vec_set_32 vdupq_n_s32
#define vec_add_dpbusd_32(acc, a, b) \
Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \
SIMD::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \
vreinterpretq_s8_s32(b))
#endif
@@ -245,23 +254,20 @@ class AffineTransform {
#if defined(USE_AVX2)
using vec_t = __m256i;
#define vec_setzero() _mm256_setzero_si256()
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_hadd Simd::m256_hadd
#define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32
#define vec_hadd SIMD::m256_hadd
#elif defined(USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero() _mm_setzero_si128()
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_hadd Simd::m128_hadd
#define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32
#define vec_hadd SIMD::m128_hadd
#elif defined(USE_NEON_DOTPROD)
using vec_t = int32x4_t;
#define vec_setzero() vdupq_n_s32(0)
#define vec_set_32 vdupq_n_s32
#define vec_add_dpbusd_32(acc, a, b) \
Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \
SIMD::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \
vreinterpretq_s8_s32(b))
#define vec_hadd Simd::neon_m128_hadd
#define vec_hadd SIMD::neon_m128_hadd
#endif
const auto inputVector = reinterpret_cast<const vec_t*>(input);
@@ -282,7 +288,6 @@ class AffineTransform {
output[0] = vec_hadd(sum0, biases[0]);
#undef vec_setzero
#undef vec_set_32
#undef vec_add_dpbusd_32
#undef vec_hadd
}
+158 -89
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,14 +22,13 @@
#define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include "../../bitboard.h"
#include "../simd.h"
#include "../nnue_common.h"
#include "affine_transform.h"
#include "simd.h"
/*
This file contains the definition for a fully connected layer (aka affine transform) with block sparse input.
@@ -51,11 +50,7 @@ constexpr int constexpr_lsb(uint64_t bb) {
alignas(CacheLineSize) static constexpr struct OffsetIndices {
#if (USE_SSE41)
std::uint8_t offset_indices[256][8];
#else
std::uint16_t offset_indices[256][8];
#endif
constexpr OffsetIndices() :
offset_indices() {
@@ -74,56 +69,83 @@ alignas(CacheLineSize) static constexpr struct OffsetIndices {
} Lookup;
#if defined(__GNUC__) || defined(__clang__)
#define RESTRICT __restrict__
#elif defined(_MSC_VER)
#define RESTRICT __restrict
#else
#define RESTRICT
#endif
// Find indices of nonzero numbers in an int32_t array
template<const IndexType InputDimensions>
void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) {
#if defined(USE_SSSE3)
#if defined(USE_AVX512)
using vec_t = __m512i;
#define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512())
#elif defined(USE_AVX2)
using vec_t = __m256i;
#if defined(USE_VNNI) && !defined(USE_AVXVNNI)
#define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256())
#else
#define vec_nnz(a) \
_mm256_movemask_ps( \
_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256())))
#endif
#elif defined(USE_SSSE3)
using vec_t = __m128i;
#define vec_nnz(a) \
_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
#endif
using vec128_t = __m128i;
#define vec128_zero _mm_setzero_si128()
#define vec128_set_16(a) _mm_set1_epi16(a)
#if (USE_SSE41)
#define vec128_load(a) _mm_cvtepu8_epi16(_mm_loadl_epi64(a))
#else
#define vec128_load(a) _mm_load_si128(a)
#endif
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
#define vec128_add(a, b) _mm_add_epi16(a, b)
#elif defined(USE_NEON)
using vec_t = uint32x4_t;
static const std::uint32_t Mask[4] = {1, 2, 4, 8};
#define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask)))
using vec128_t = uint16x8_t;
#define vec128_zero vdupq_n_u16(0)
#define vec128_set_16(a) vdupq_n_u16(a)
#define vec128_load(a) vld1q_u16(reinterpret_cast<const std::uint16_t*>(a))
#define vec128_storeu(a, b) vst1q_u16(reinterpret_cast<std::uint16_t*>(a), b)
#define vec128_add(a, b) vaddq_u16(a, b)
#endif
constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t);
// Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8)
constexpr IndexType ChunkSize = std::max<IndexType>(InputSimdWidth, 8);
constexpr IndexType NumChunks = InputDimensions / ChunkSize;
constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth;
constexpr IndexType OutputsPerChunk = ChunkSize / 8;
void find_nnz(const std::int32_t* RESTRICT input,
std::uint16_t* RESTRICT out,
IndexType& count_out) {
const auto inputVector = reinterpret_cast<const vec_t*>(input);
#if defined(USE_AVX512ICL)
constexpr IndexType SimdWidthIn = 16; // 512 bits / 32 bits
constexpr IndexType SimdWidthOut = 32; // 512 bits / 16 bits
constexpr IndexType NumChunks = InputDimensions / SimdWidthOut;
const __m512i increment = _mm512_set1_epi16(SimdWidthOut);
__m512i base = _mm512_set_epi16( // Same permute order as _mm512_packus_epi32()
31, 30, 29, 28, 15, 14, 13, 12, 27, 26, 25, 24, 11, 10, 9, 8, 23, 22, 21, 20, 7, 6, 5, 4, 19,
18, 17, 16, 3, 2, 1, 0);
IndexType count = 0;
for (IndexType i = 0; i < NumChunks; ++i)
{
const __m512i inputV0 = _mm512_load_si512(input + i * 2 * SimdWidthIn);
const __m512i inputV1 = _mm512_load_si512(input + i * 2 * SimdWidthIn + SimdWidthIn);
// Get a bitmask and gather non zero indices
const __m512i inputV01 = _mm512_packus_epi32(inputV0, inputV1);
const __mmask32 nnzMask = _mm512_test_epi16_mask(inputV01, inputV01);
// Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4
__m512i nnz = _mm512_maskz_compress_epi16(nnzMask, base);
_mm512_storeu_si512(out + count, nnz);
count += popcount(nnzMask);
base = _mm512_add_epi16(base, increment);
}
count_out = count;
#elif defined(USE_AVX512)
constexpr IndexType SimdWidth = 16; // 512 bits / 32 bits
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
const __m512i increment = _mm512_set1_epi32(SimdWidth);
__m512i base = _mm512_set_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
IndexType count = 0;
for (IndexType i = 0; i < NumChunks; ++i)
{
const __m512i inputV = _mm512_load_si512(input + i * SimdWidth);
// Get a bitmask and gather non zero indices
const __mmask16 nnzMask = _mm512_test_epi32_mask(inputV, inputV);
const __m512i nnzV = _mm512_maskz_compress_epi32(nnzMask, base);
_mm512_mask_cvtepi32_storeu_epi16(out + count, 0xFFFF, nnzV);
count += popcount(nnzMask);
base = _mm512_add_epi32(base, increment);
}
count_out = count;
#else
using namespace SIMD;
constexpr IndexType InputSimdWidth = sizeof(vec_uint_t) / sizeof(std::int32_t);
// Outputs are processed 8 elements at a time, even if the SIMD width is narrower
constexpr IndexType ChunkSize = 8;
constexpr IndexType NumChunks = InputDimensions / ChunkSize;
constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth;
static_assert(InputsPerChunk > 0 && "SIMD width too wide");
const auto inputVector = reinterpret_cast<const vec_uint_t*>(input);
IndexType count = 0;
vec128_t base = vec128_zero;
const vec128_t increment = vec128_set_16(8);
@@ -133,27 +155,19 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou
unsigned nnz = 0;
for (IndexType j = 0; j < InputsPerChunk; ++j)
{
const vec_t inputChunk = inputVector[i * InputsPerChunk + j];
const vec_uint_t inputChunk = inputVector[i * InputsPerChunk + j];
nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth);
}
for (IndexType j = 0; j < OutputsPerChunk; ++j)
{
const unsigned lookup = (nnz >> (j * 8)) & 0xFF;
const vec128_t offsets =
vec128_load(reinterpret_cast<const vec128_t*>(&Lookup.offset_indices[lookup]));
vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
count += popcount(lookup);
base = vec128_add(base, increment);
}
const vec128_t offsets =
vec128_load(reinterpret_cast<const vec128_t*>(&Lookup.offset_indices[nnz]));
vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
count += popcount(nnz);
base = vec128_add(base, increment);
}
count_out = count;
#endif
}
#undef vec_nnz
#undef vec128_zero
#undef vec128_set_16
#undef vec128_load
#undef vec128_storeu
#undef vec128_add
#endif
// Sparse input implementation
@@ -224,6 +238,15 @@ class AffineTransformSparseInput {
return !stream.fail();
}
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, get_raw_data_hash(biases));
hash_combine(h, get_raw_data_hash(weights));
hash_combine(h, get_hash_value(0));
return h;
}
// Forward propagation
void propagate(const InputType* input, OutputType* output) const {
@@ -231,35 +254,44 @@ class AffineTransformSparseInput {
#if defined(USE_AVX512)
using invec_t = __m512i;
using outvec_t = __m512i;
#define vec_add_32 _mm512_add_epi32
#define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32
#elif defined(USE_AVX2)
using invec_t = __m256i;
using outvec_t = __m256i;
#define vec_add_32 _mm256_add_epi32
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32
#elif defined(USE_SSSE3)
using invec_t = __m128i;
using outvec_t = __m128i;
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::m128_add_dpbusd_epi32
#elif defined(USE_NEON_DOTPROD)
using invec_t = int8x16_t;
using outvec_t = int32x4_t;
#define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a))
#define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::dotprod_m128_add_dpbusd_epi32
#elif defined(USE_NEON)
using invec_t = int8x16_t;
using outvec_t = int32x4_t;
#define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a))
#define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32
#define vec_add_dpbusd_32 SIMD::neon_m128_add_dpbusd_epi32
#endif
static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType);
constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType);
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize;
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
std::uint16_t nnz[NumChunks];
IndexType count;
constexpr IndexType NumAccums = OutputDimensions / OutputSimdWidth;
// If we're using high-latency dot product instructions, split the accumulators
// to create 3 separate dependency chains and merge at the end
constexpr IndexType NumRegs =
#if defined(USE_VNNI)
3 * NumAccums;
#else
NumAccums;
#endif
std::uint16_t nnz[NumChunks];
IndexType count;
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
@@ -268,24 +300,61 @@ class AffineTransformSparseInput {
const outvec_t* biasvec = reinterpret_cast<const outvec_t*>(biases);
outvec_t acc[NumRegs];
for (IndexType k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < NumAccums; ++k)
acc[k] = biasvec[k];
for (IndexType j = 0; j < count; ++j)
const auto* start = nnz;
const auto* end = nnz + count;
// convince GCC to not do weird pointer arithmetic in the following loop
const std::int8_t* weights_cp = weights;
#if defined(USE_VNNI)
for (IndexType k = NumAccums; k < NumRegs; ++k)
acc[k] = vec_zero();
while (start < end - 2)
{
const auto i = nnz[j];
const invec_t in = vec_set_32(input32[i]);
const auto col =
reinterpret_cast<const invec_t*>(&weights[i * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumRegs; ++k)
const std::ptrdiff_t i0 = *start++;
const std::ptrdiff_t i1 = *start++;
const std::ptrdiff_t i2 = *start++;
const invec_t in0 = vec_set_32(input32[i0]);
const invec_t in1 = vec_set_32(input32[i1]);
const invec_t in2 = vec_set_32(input32[i2]);
const auto col0 =
reinterpret_cast<const invec_t*>(&weights_cp[i0 * OutputDimensions * ChunkSize]);
const auto col1 =
reinterpret_cast<const invec_t*>(&weights_cp[i1 * OutputDimensions * ChunkSize]);
const auto col2 =
reinterpret_cast<const invec_t*>(&weights_cp[i2 * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumAccums; ++k)
{
vec_add_dpbusd_32(acc[k], in0, col0[k]);
vec_add_dpbusd_32(acc[k + NumAccums], in1, col1[k]);
vec_add_dpbusd_32(acc[k + 2 * NumAccums], in2, col2[k]);
}
}
for (IndexType k = 0; k < NumAccums; ++k)
acc[k] = vec_add_32(vec_add_32(acc[k], acc[k + NumAccums]), acc[k + 2 * NumAccums]);
#endif
while (start < end)
{
const std::ptrdiff_t i = *start++;
const invec_t in = vec_set_32(input32[i]);
const auto col =
reinterpret_cast<const invec_t*>(&weights_cp[i * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumAccums; ++k)
vec_add_dpbusd_32(acc[k], in, col[k]);
}
outvec_t* outptr = reinterpret_cast<outvec_t*>(output);
for (IndexType k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < NumAccums; ++k)
outptr[k] = acc[k];
#undef vec_set_32
#undef vec_add_dpbusd_32
#ifdef vec_add_32
#undef vec_add_32
#endif
#else
// Use dense implementation for the other architectures.
affine_transform_non_ssse3<InputDimensions, PaddedInputDimensions, OutputDimensions>(
+7 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,6 +58,12 @@ class ClippedReLU {
// Write network parameters
bool write_parameters(std::ostream&) const { return true; }
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, get_hash_value(0));
return h;
}
// Forward propagation
void propagate(const InputType* input, OutputType* output) const {
-134
View File
@@ -1,134 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STOCKFISH_SIMD_H_INCLUDED
#define STOCKFISH_SIMD_H_INCLUDED
#if defined(USE_AVX2)
#include <immintrin.h>
#elif defined(USE_SSE41)
#include <smmintrin.h>
#elif defined(USE_SSSE3)
#include <tmmintrin.h>
#elif defined(USE_SSE2)
#include <emmintrin.h>
#elif defined(USE_NEON)
#include <arm_neon.h>
#endif
namespace Stockfish::Simd {
#if defined(USE_AVX512)
[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) {
return _mm512_reduce_add_epi32(sum) + bias;
}
[[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) {
#if defined(USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a, b);
#else
__m512i product0 = _mm512_maddubs_epi16(a, b);
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, product0);
#endif
}
#endif
#if defined(USE_AVX2)
[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) {
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
return _mm_cvtsi128_si32(sum128) + bias;
}
[[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) {
#if defined(USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a, b);
#else
__m256i product0 = _mm256_maddubs_epi16(a, b);
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, product0);
#endif
}
#endif
#if defined(USE_SSSE3)
[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) {
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
return _mm_cvtsi128_si32(sum) + bias;
}
[[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) {
__m128i product0 = _mm_maddubs_epi16(a, b);
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, product0);
}
#endif
#if defined(USE_NEON_DOTPROD)
[[maybe_unused]] static void
dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
acc = vdotq_s32(acc, a, b);
}
#endif
#if defined(USE_NEON)
[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
#if USE_NEON >= 8
return vaddvq_s32(s);
#else
return s[0] + s[1] + s[2] + s[3];
#endif
}
[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) {
return neon_m128_reduce_add_epi32(sum) + bias;
}
#endif
#if USE_NEON >= 8
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b));
int16x8_t product1 = vmull_high_s8(a, b);
int16x8_t sum = vpaddq_s16(product0, product1);
acc = vpadalq_s16(acc, sum);
}
#endif
}
#endif // STOCKFISH_SIMD_H_INCLUDED
+7 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,6 +58,12 @@ class SqrClippedReLU {
// Write network parameters
bool write_parameters(std::ostream&) const { return true; }
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, get_hash_value(0));
return h;
}
// Forward propagation
void propagate(const InputType* input, OutputType* output) const {
+46 -91
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <optional>
#include <type_traits>
#include <vector>
@@ -29,8 +28,8 @@
#define INCBIN_SILENCE_BITCODE_WARNING
#include "../incbin/incbin.h"
#include "../cluster.h"
#include "../evaluate.h"
#include "../memory.h"
#include "../misc.h"
#include "../position.h"
#include "../types.h"
@@ -101,7 +100,7 @@ bool read_parameters(std::istream& stream, T& reference) {
// Write evaluation function parameters
template<typename T>
bool write_parameters(std::ostream& stream, T& reference) {
bool write_parameters(std::ostream& stream, const T& reference) {
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
return reference.write_parameters(stream);
@@ -109,43 +108,6 @@ bool write_parameters(std::ostream& stream, T& reference) {
} // namespace Detail
template<typename Arch, typename Transformer>
Network<Arch, Transformer>::Network(const Network<Arch, Transformer>& other) :
evalFile(other.evalFile),
embeddedType(other.embeddedType) {
if (other.featureTransformer)
featureTransformer = make_unique_large_page<Transformer>(*other.featureTransformer);
network = make_unique_aligned<Arch[]>(LayerStacks);
if (!other.network)
return;
for (std::size_t i = 0; i < LayerStacks; ++i)
network[i] = other.network[i];
}
template<typename Arch, typename Transformer>
Network<Arch, Transformer>&
Network<Arch, Transformer>::operator=(const Network<Arch, Transformer>& other) {
evalFile = other.evalFile;
embeddedType = other.embeddedType;
if (other.featureTransformer)
featureTransformer = make_unique_large_page<Transformer>(*other.featureTransformer);
network = make_unique_aligned<Arch[]>(LayerStacks);
if (!other.network)
return *this;
for (std::size_t i = 0; i < LayerStacks; ++i)
network[i] = other.network[i];
return *this;
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::load(const std::string& rootDirectory, std::string evalfilePath) {
#if defined(DEFAULT_NNUE_DIRECTORY)
@@ -160,14 +122,14 @@ void Network<Arch, Transformer>::load(const std::string& rootDirectory, std::str
for (const auto& directory : dirs)
{
if (evalFile.current != evalfilePath)
if (std::string(evalFile.current) != evalfilePath)
{
if (directory != "<internal>")
{
load_user_net(directory, evalfilePath);
}
if (directory == "<internal>" && evalfilePath == evalFile.defaultName)
if (directory == "<internal>" && evalfilePath == std::string(evalFile.defaultName))
{
load_internal();
}
@@ -185,7 +147,7 @@ bool Network<Arch, Transformer>::save(const std::optional<std::string>& filename
actualFilename = filename.value();
else
{
if (evalFile.current != evalFile.defaultName)
if (std::string(evalFile.current) != std::string(evalFile.defaultName))
{
msg = "Failed to export a net. "
"A non-embedded net can only be saved if the filename is specified";
@@ -211,28 +173,18 @@ template<typename Arch, typename Transformer>
NetworkOutput
Network<Arch, Transformer>::evaluate(const Position& pos,
AccumulatorStack& accumulatorStack,
AccumulatorCaches::Cache<FTDimensions>* cache) const {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
AccumulatorCaches::Cache<FTDimensions>& cache) const {
constexpr uint64_t alignment = CacheLineSize;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType
transformedFeaturesUnaligned[FeatureTransformer<FTDimensions, nullptr>::BufferSize
+ alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment) TransformedFeatureType
transformedFeatures[FeatureTransformer<FTDimensions, nullptr>::BufferSize];
#endif
alignas(alignment)
TransformedFeatureType transformedFeatures[FeatureTransformer<FTDimensions>::BufferSize];
ASSERT_ALIGNED(transformedFeatures, alignment);
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
const auto psqt =
featureTransformer->transform(pos, accumulatorStack, cache, transformedFeatures, bucket);
featureTransformer.transform(pos, accumulatorStack, cache, transformedFeatures, bucket);
const auto positional = network[bucket].propagate(transformedFeatures);
return {static_cast<Value>(psqt / OutputScale), static_cast<Value>(positional / OutputScale)};
}
@@ -244,7 +196,7 @@ void Network<Arch, Transformer>::verify(std::string
if (evalfilePath.empty())
evalfilePath = evalFile.defaultName;
if (evalFile.current != evalfilePath)
if (std::string(evalFile.current) != evalfilePath)
{
if (f)
{
@@ -255,7 +207,7 @@ void Network<Arch, Transformer>::verify(std::string
"including the directory name, to the network file.";
std::string msg4 = "The default net can be downloaded from: "
"https://tests.stockfishchess.org/api/nn/"
+ evalFile.defaultName;
+ std::string(evalFile.defaultName);
std::string msg5 = "The engine will be terminated now.";
std::string msg = "ERROR: " + msg1 + '\n' + "ERROR: " + msg2 + '\n' + "ERROR: " + msg3
@@ -269,12 +221,13 @@ void Network<Arch, Transformer>::verify(std::string
if (f)
{
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
+ "MiB, (" + std::to_string(featureTransformer->InputDimensions) + ", "
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
+ ", 1))");
size_t size = sizeof(featureTransformer) + sizeof(Arch) * LayerStacks;
if (Distributed::is_root())
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
+ "MiB, (" + std::to_string(featureTransformer.TotalInputDimensions) + ", "
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
+ std::to_string(network[0].FC_0_OUTPUTS) + ", "
+ std::to_string(network[0].FC_1_OUTPUTS) + ", 1))");
}
}
@@ -283,21 +236,12 @@ template<typename Arch, typename Transformer>
NnueEvalTrace
Network<Arch, Transformer>::trace_evaluate(const Position& pos,
AccumulatorStack& accumulatorStack,
AccumulatorCaches::Cache<FTDimensions>* cache) const {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
AccumulatorCaches::Cache<FTDimensions>& cache) const {
constexpr uint64_t alignment = CacheLineSize;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType
transformedFeaturesUnaligned[FeatureTransformer<FTDimensions, nullptr>::BufferSize
+ alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment) TransformedFeatureType
transformedFeatures[FeatureTransformer<FTDimensions, nullptr>::BufferSize];
#endif
alignas(alignment)
TransformedFeatureType transformedFeatures[FeatureTransformer<FTDimensions>::BufferSize];
ASSERT_ALIGNED(transformedFeatures, alignment);
@@ -306,7 +250,7 @@ Network<Arch, Transformer>::trace_evaluate(const Position&
for (IndexType bucket = 0; bucket < LayerStacks; ++bucket)
{
const auto materialist =
featureTransformer->transform(pos, accumulatorStack, cache, transformedFeatures, bucket);
featureTransformer.transform(pos, accumulatorStack, cache, transformedFeatures, bucket);
const auto positional = network[bucket].propagate(transformedFeatures);
t.psqt[bucket] = static_cast<Value>(materialist / OutputScale);
@@ -360,8 +304,7 @@ void Network<Arch, Transformer>::load_internal() {
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::initialize() {
featureTransformer = make_unique_large_page<Transformer>();
network = make_unique_aligned<Arch[]>(LayerStacks);
initialized = true;
}
@@ -385,6 +328,20 @@ std::optional<std::string> Network<Arch, Transformer>::load(std::istream& stream
}
template<typename Arch, typename Transformer>
std::size_t Network<Arch, Transformer>::get_content_hash() const {
if (!initialized)
return 0;
std::size_t h = 0;
hash_combine(h, featureTransformer);
for (auto&& layerstack : network)
hash_combine(h, layerstack);
hash_combine(h, evalFile);
hash_combine(h, static_cast<int>(embeddedType));
return h;
}
// Read network header
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::read_header(std::istream& stream,
@@ -418,13 +375,13 @@ bool Network<Arch, Transformer>::write_header(std::ostream& stream,
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::read_parameters(std::istream& stream,
std::string& netDescription) const {
std::string& netDescription) {
std::uint32_t hashValue;
if (!read_header(stream, &hashValue, &netDescription))
return false;
if (hashValue != Network::hash)
return false;
if (!Detail::read_parameters(stream, *featureTransformer))
if (!Detail::read_parameters(stream, featureTransformer))
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
@@ -440,7 +397,7 @@ bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
const std::string& netDescription) const {
if (!write_header(stream, Network::hash, netDescription))
return false;
if (!Detail::write_parameters(stream, *featureTransformer))
if (!Detail::write_parameters(stream, featureTransformer))
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
@@ -452,12 +409,10 @@ bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
// Explicit template instantiations
template class Network<
NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>,
FeatureTransformer<TransformedFeatureDimensionsBig, &AccumulatorState::accumulatorBig>>;
template class Network<NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>,
FeatureTransformer<TransformedFeatureDimensionsBig>>;
template class Network<
NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>,
FeatureTransformer<TransformedFeatureDimensionsSmall, &AccumulatorState::accumulatorSmall>>;
template class Network<NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>,
FeatureTransformer<TransformedFeatureDimensionsSmall>>;
} // namespace Stockfish::Eval::NNUE
+44 -20
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,19 +19,22 @@
#ifndef NETWORK_H_INCLUDED
#define NETWORK_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include "../memory.h"
#include "../misc.h"
#include "../types.h"
#include "nnue_accumulator.h"
#include "nnue_architecture.h"
#include "nnue_common.h"
#include "nnue_feature_transformer.h"
#include "nnue_misc.h"
@@ -48,6 +51,9 @@ enum class EmbeddedNNUEType {
using NetworkOutput = std::tuple<Value, Value>;
// The network must be a trivial type, i.e. the memory must be in-line.
// This is required to allow sharing the network via shared memory, as
// there is no way to run destructors.
template<typename Arch, typename Transformer>
class Network {
static constexpr IndexType FTDimensions = Arch::TransformedFeatureDimensions;
@@ -57,24 +63,26 @@ class Network {
evalFile(file),
embeddedType(type) {}
Network(const Network& other);
Network(Network&& other) = default;
Network(const Network& other) = default;
Network(Network&& other) = default;
Network& operator=(const Network& other);
Network& operator=(Network&& other) = default;
Network& operator=(const Network& other) = default;
Network& operator=(Network&& other) = default;
void load(const std::string& rootDirectory, std::string evalfilePath);
bool save(const std::optional<std::string>& filename) const;
std::size_t get_content_hash() const;
NetworkOutput evaluate(const Position& pos,
AccumulatorStack& accumulatorStack,
AccumulatorCaches::Cache<FTDimensions>* cache) const;
AccumulatorCaches::Cache<FTDimensions>& cache) const;
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
NnueEvalTrace trace_evaluate(const Position& pos,
AccumulatorStack& accumulatorStack,
AccumulatorCaches::Cache<FTDimensions>* cache) const;
AccumulatorCaches::Cache<FTDimensions>& cache) const;
private:
void load_user_net(const std::string&, const std::string&);
@@ -88,35 +96,33 @@ class Network {
bool read_header(std::istream&, std::uint32_t*, std::string*) const;
bool write_header(std::ostream&, std::uint32_t, const std::string&) const;
bool read_parameters(std::istream&, std::string&) const;
bool read_parameters(std::istream&, std::string&);
bool write_parameters(std::ostream&, const std::string&) const;
// Input feature converter
LargePagePtr<Transformer> featureTransformer;
Transformer featureTransformer;
// Evaluation function
AlignedPtr<Arch[]> network;
Arch network[LayerStacks];
EvalFile evalFile;
EmbeddedNNUEType embeddedType;
bool initialized = false;
// Hash value of evaluation function structure
static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value();
template<IndexType Size>
friend struct AccumulatorCaches::Cache;
friend class AccumulatorStack;
};
// Definitions of the network types
using SmallFeatureTransformer =
FeatureTransformer<TransformedFeatureDimensionsSmall, &AccumulatorState::accumulatorSmall>;
using SmallFeatureTransformer = FeatureTransformer<TransformedFeatureDimensionsSmall>;
using SmallNetworkArchitecture =
NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>;
using BigFeatureTransformer =
FeatureTransformer<TransformedFeatureDimensionsBig, &AccumulatorState::accumulatorBig>;
using BigFeatureTransformer = FeatureTransformer<TransformedFeatureDimensionsBig>;
using BigNetworkArchitecture = NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>;
using NetworkBig = Network<BigNetworkArchitecture, BigFeatureTransformer>;
@@ -124,9 +130,9 @@ using NetworkSmall = Network<SmallNetworkArchitecture, SmallFeatureTransformer>;
struct Networks {
Networks(NetworkBig&& nB, NetworkSmall&& nS) :
big(std::move(nB)),
small(std::move(nS)) {}
Networks(std::unique_ptr<NetworkBig>&& nB, std::unique_ptr<NetworkSmall>&& nS) :
big(std::move(*nB)),
small(std::move(*nS)) {}
NetworkBig big;
NetworkSmall small;
@@ -135,4 +141,22 @@ struct Networks {
} // namespace Stockfish
template<typename ArchT, typename FeatureTransformerT>
struct std::hash<Stockfish::Eval::NNUE::Network<ArchT, FeatureTransformerT>> {
std::size_t operator()(
const Stockfish::Eval::NNUE::Network<ArchT, FeatureTransformerT>& network) const noexcept {
return network.get_content_hash();
}
};
template<>
struct std::hash<Stockfish::Eval::NNUE::Networks> {
std::size_t operator()(const Stockfish::Eval::NNUE::Networks& networks) const noexcept {
std::size_t h = 0;
Stockfish::hash_combine(h, networks.big);
Stockfish::hash_combine(h, networks.small);
return h;
}
};
#endif
File diff suppressed because it is too large Load Diff
+90 -67
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
#include <utility>
#include "../types.h"
#include "nnue_architecture.h"
@@ -37,27 +37,18 @@ class Position;
namespace Stockfish::Eval::NNUE {
using BiasType = std::int16_t;
using PSQTWeightType = std::int32_t;
using IndexType = std::uint32_t;
struct Networks;
template<IndexType Size>
struct alignas(CacheLineSize) Accumulator;
struct AccumulatorState;
template<IndexType TransformedFeatureDimensions,
Accumulator<TransformedFeatureDimensions> AccumulatorState::*accPtr>
template<IndexType TransformedFeatureDimensions>
class FeatureTransformer;
// Class that holds the result of affine transformation of input features
template<IndexType Size>
struct alignas(CacheLineSize) Accumulator {
std::int16_t accumulation[COLOR_NB][Size];
std::int32_t psqtAccumulation[COLOR_NB][PSQTBuckets];
std::array<bool, COLOR_NB> computed;
std::array<std::array<std::int16_t, Size>, COLOR_NB> accumulation;
std::array<std::array<std::int32_t, PSQTBuckets>, COLOR_NB> psqtAccumulation;
std::array<bool, COLOR_NB> computed = {};
};
@@ -78,18 +69,17 @@ struct AccumulatorCaches {
struct alignas(CacheLineSize) Cache {
struct alignas(CacheLineSize) Entry {
BiasType accumulation[Size];
PSQTWeightType psqtAccumulation[PSQTBuckets];
Bitboard byColorBB[COLOR_NB];
Bitboard byTypeBB[PIECE_TYPE_NB];
std::array<BiasType, Size> accumulation;
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
std::array<Piece, SQUARE_NB> pieces;
Bitboard pieceBB;
// To initialize a refresh entry, we set all its bitboards empty,
// so we put the biases in the accumulation, without any weights on top
void clear(const BiasType* biases) {
std::memcpy(accumulation, biases, sizeof(accumulation));
std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0,
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
void clear(const std::array<BiasType, Size>& biases) {
accumulation = biases;
std::memset(reinterpret_cast<std::byte*>(this) + offsetof(Entry, psqtAccumulation),
0, sizeof(Entry) - offsetof(Entry, psqtAccumulation));
}
};
@@ -97,7 +87,7 @@ struct AccumulatorCaches {
void clear(const Network& network) {
for (auto& entries1D : entries)
for (auto& entry : entries1D)
entry.clear(network.featureTransformer->biases);
entry.clear(network.featureTransformer.biases);
}
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
@@ -116,66 +106,99 @@ struct AccumulatorCaches {
};
template<typename FeatureSet>
struct AccumulatorState {
Accumulator<TransformedFeatureDimensionsBig> accumulatorBig;
Accumulator<TransformedFeatureDimensionsSmall> accumulatorSmall;
DirtyPiece dirtyPiece;
typename FeatureSet::DiffType diff;
void reset(const DirtyPiece& dp) noexcept;
template<IndexType Size>
auto& acc() noexcept {
static_assert(Size == TransformedFeatureDimensionsBig
|| Size == TransformedFeatureDimensionsSmall,
"Invalid size for accumulator");
if constexpr (Size == TransformedFeatureDimensionsBig)
return accumulatorBig;
else if constexpr (Size == TransformedFeatureDimensionsSmall)
return accumulatorSmall;
}
template<IndexType Size>
const auto& acc() const noexcept {
static_assert(Size == TransformedFeatureDimensionsBig
|| Size == TransformedFeatureDimensionsSmall,
"Invalid size for accumulator");
if constexpr (Size == TransformedFeatureDimensionsBig)
return accumulatorBig;
else if constexpr (Size == TransformedFeatureDimensionsSmall)
return accumulatorSmall;
}
void reset(const typename FeatureSet::DiffType& dp) noexcept {
diff = dp;
accumulatorBig.computed.fill(false);
accumulatorSmall.computed.fill(false);
}
typename FeatureSet::DiffType& reset() noexcept {
accumulatorBig.computed.fill(false);
accumulatorSmall.computed.fill(false);
return diff;
}
};
class AccumulatorStack {
public:
AccumulatorStack() :
m_accumulators(MAX_PLY + 1),
m_current_idx{} {}
static constexpr std::size_t MaxSize = MAX_PLY + 1;
[[nodiscard]] const AccumulatorState& latest() const noexcept;
template<typename T>
[[nodiscard]] const AccumulatorState<T>& latest() const noexcept;
void
reset(const Position& rootPos, const Networks& networks, AccumulatorCaches& caches) noexcept;
void push(const DirtyPiece& dirtyPiece) noexcept;
void pop() noexcept;
void reset() noexcept;
std::pair<DirtyPiece&, DirtyThreats&> push() noexcept;
void pop() noexcept;
template<IndexType Dimensions, Accumulator<Dimensions> AccumulatorState::*accPtr>
void evaluate(const Position& pos,
const FeatureTransformer<Dimensions, accPtr>& featureTransformer,
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
template<IndexType Dimensions>
void evaluate(const Position& pos,
const FeatureTransformer<Dimensions>& featureTransformer,
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
private:
[[nodiscard]] AccumulatorState& mut_latest() noexcept;
template<typename T>
[[nodiscard]] AccumulatorState<T>& mut_latest() noexcept;
template<Color Perspective,
IndexType Dimensions,
Accumulator<Dimensions> AccumulatorState::*accPtr>
void evaluate_side(const Position& pos,
const FeatureTransformer<Dimensions, accPtr>& featureTransformer,
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
template<typename T>
[[nodiscard]] const std::array<AccumulatorState<T>, MaxSize>& accumulators() const noexcept;
template<Color Perspective,
IndexType Dimensions,
Accumulator<Dimensions> AccumulatorState::*accPtr>
[[nodiscard]] std::size_t find_last_usable_accumulator() const noexcept;
template<typename T>
[[nodiscard]] std::array<AccumulatorState<T>, MaxSize>& mut_accumulators() noexcept;
template<Color Perspective,
IndexType Dimensions,
Accumulator<Dimensions> AccumulatorState::*accPtr>
void
forward_update_incremental(const Position& pos,
const FeatureTransformer<Dimensions, accPtr>& featureTransformer,
const std::size_t begin) noexcept;
template<typename FeatureSet, IndexType Dimensions>
void evaluate_side(Color perspective,
const Position& pos,
const FeatureTransformer<Dimensions>& featureTransformer,
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
template<Color Perspective,
IndexType Dimensions,
Accumulator<Dimensions> AccumulatorState::*accPtr>
void
backward_update_incremental(const Position& pos,
const FeatureTransformer<Dimensions, accPtr>& featureTransformer,
const std::size_t end) noexcept;
template<typename FeatureSet, IndexType Dimensions>
[[nodiscard]] std::size_t find_last_usable_accumulator(Color perspective) const noexcept;
std::vector<AccumulatorState> m_accumulators;
std::size_t m_current_idx;
template<typename FeatureSet, IndexType Dimensions>
void forward_update_incremental(Color perspective,
const Position& pos,
const FeatureTransformer<Dimensions>& featureTransformer,
const std::size_t begin) noexcept;
template<typename FeatureSet, IndexType Dimensions>
void backward_update_incremental(Color perspective,
const Position& pos,
const FeatureTransformer<Dimensions>& featureTransformer,
const std::size_t end) noexcept;
std::array<AccumulatorState<PSQFeatureSet>, MaxSize> psq_accumulators;
std::array<AccumulatorState<ThreatFeatureSet>, MaxSize> threat_accumulators;
std::size_t size = 1;
};
} // namespace Stockfish::Eval::NNUE
+32 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include <iosfwd>
#include "features/half_ka_v2_hm.h"
#include "features/full_threats.h"
#include "layers/affine_transform.h"
#include "layers/affine_transform_sparse_input.h"
#include "layers/clipped_relu.h"
@@ -35,10 +36,11 @@
namespace Stockfish::Eval::NNUE {
// Input features used in evaluation function
using FeatureSet = Features::HalfKAv2_hm;
using ThreatFeatureSet = Features::FullThreats;
using PSQFeatureSet = Features::HalfKAv2_hm;
// Number of input feature dimensions after conversion
constexpr IndexType TransformedFeatureDimensionsBig = 3072;
constexpr IndexType TransformedFeatureDimensionsBig = 1024;
constexpr int L2Big = 15;
constexpr int L3Big = 32;
@@ -49,6 +51,12 @@ constexpr int L3Small = 32;
constexpr IndexType PSQTBuckets = 8;
constexpr IndexType LayerStacks = 8;
// If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's
// vector registers.
static_assert(PSQTBuckets % 8 == 0,
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
template<IndexType L1, int L2, int L3>
struct NetworkArchitecture {
static constexpr IndexType TransformedFeatureDimensions = L1;
@@ -91,7 +99,7 @@ struct NetworkArchitecture {
&& fc_2.write_parameters(stream);
}
std::int32_t propagate(const TransformedFeatureType* transformedFeatures) {
std::int32_t propagate(const TransformedFeatureType* transformedFeatures) const {
struct alignas(CacheLineSize) Buffer {
alignas(CacheLineSize) typename decltype(fc_0)::OutputBuffer fc_0_out;
alignas(CacheLineSize) typename decltype(ac_sqr_0)::OutputType
@@ -130,8 +138,28 @@ struct NetworkArchitecture {
return outputValue;
}
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, fc_0.get_content_hash());
hash_combine(h, ac_sqr_0.get_content_hash());
hash_combine(h, ac_0.get_content_hash());
hash_combine(h, fc_1.get_content_hash());
hash_combine(h, ac_1.get_content_hash());
hash_combine(h, fc_2.get_content_hash());
hash_combine(h, get_hash_value());
return h;
}
};
} // namespace Stockfish::Eval::NNUE
template<Stockfish::Eval::NNUE::IndexType L1, int L2, int L3>
struct std::hash<Stockfish::Eval::NNUE::NetworkArchitecture<L1, L2, L3>> {
std::size_t
operator()(const Stockfish::Eval::NNUE::NetworkArchitecture<L1, L2, L3>& arch) const noexcept {
return arch.get_content_hash();
}
};
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
+16 -16
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,6 +48,12 @@
namespace Stockfish::Eval::NNUE {
using BiasType = std::int16_t;
using ThreatWeightType = std::int8_t;
using WeightType = std::int16_t;
using PSQTWeightType = std::int32_t;
using IndexType = std::uint32_t;
// Version of the evaluation file
constexpr std::uint32_t Version = 0x7AF32F20u;
@@ -76,7 +82,6 @@ constexpr std::size_t MaxSimdWidth = 32;
// Type of input feature after conversion
using TransformedFeatureType = std::uint8_t;
using IndexType = std::uint32_t;
// Round n up to be a multiple of base
template<typename IntType>
@@ -168,8 +173,8 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, std
// Read N signed integers from the stream s, putting them in the array out.
// The stream is assumed to be compressed using the signed LEB128 format.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename IntType>
inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) {
template<typename IntType, std::size_t Count>
inline void read_leb_128(std::istream& stream, std::array<IntType, Count>& out) {
// Check the presence of our LEB128 magic string
char leb128MagicString[Leb128MagicStringSize];
@@ -184,7 +189,7 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count)
auto bytes_left = read_little_endian<std::uint32_t>(stream);
std::uint32_t buf_pos = BUF_SIZE;
for (std::size_t i = 0; i < count; ++i)
for (std::size_t i = 0; i < Count; ++i)
{
IntType result = 0;
size_t shift = 0;
@@ -219,8 +224,8 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count)
// This takes N integers from array values, compresses them with
// the LEB128 algorithm and writes the result on the stream s.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename IntType>
inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) {
template<typename IntType, std::size_t Count>
inline void write_leb_128(std::ostream& stream, const std::array<IntType, Count>& values) {
// Write our LEB128 magic string
stream.write(Leb128MagicString, Leb128MagicStringSize);
@@ -228,7 +233,7 @@ inline void write_leb_128(std::ostream& stream, const IntType* values, std::size
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
std::uint32_t byte_count = 0;
for (std::size_t i = 0; i < count; ++i)
for (std::size_t i = 0; i < Count; ++i)
{
IntType value = values[i];
std::uint8_t byte;
@@ -254,13 +259,13 @@ inline void write_leb_128(std::ostream& stream, const IntType* values, std::size
}
};
auto write = [&](std::uint8_t byte) {
buf[buf_pos++] = byte;
auto write = [&](std::uint8_t b) {
buf[buf_pos++] = b;
if (buf_pos == BUF_SIZE)
flush();
};
for (std::size_t i = 0; i < count; ++i)
for (std::size_t i = 0; i < Count; ++i)
{
IntType value = values[i];
while (true)
@@ -279,11 +284,6 @@ inline void write_leb_128(std::ostream& stream, const IntType* values, std::size
flush();
}
enum IncUpdateDirection {
FORWARD,
BACKWARDS
};
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED
+193 -218
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,124 +25,17 @@
#include <cstdint>
#include <cstring>
#include <iosfwd>
#include <iterator>
#include "../position.h"
#include "../types.h"
#include "nnue_accumulator.h"
#include "nnue_architecture.h"
#include "nnue_common.h"
#include "simd.h"
namespace Stockfish::Eval::NNUE {
using BiasType = std::int16_t;
using WeightType = std::int16_t;
using PSQTWeightType = std::int32_t;
// If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's
// vector registers.
#define VECTOR
static_assert(PSQTBuckets % 8 == 0,
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
#ifdef USE_AVX512
using vec_t = __m512i;
using psqt_vec_t = __m256i;
#define vec_load(a) _mm512_load_si512(a)
#define vec_store(a, b) _mm512_store_si512(a, b)
#define vec_add_16(a, b) _mm512_add_epi16(a, b)
#define vec_sub_16(a, b) _mm512_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b)
#define vec_zero() _mm512_setzero_epi32()
#define vec_set_16(a) _mm512_set1_epi16(a)
#define vec_max_16(a, b) _mm512_max_epi16(a, b)
#define vec_min_16(a, b) _mm512_min_epi16(a, b)
#define vec_slli_16(a, b) _mm512_slli_epi16(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) _mm512_packus_epi16(a, b)
#define vec_load_psqt(a) _mm256_load_si256(a)
#define vec_store_psqt(a, b) _mm256_store_si256(a, b)
#define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b)
#define vec_zero_psqt() _mm256_setzero_si256()
#define NumRegistersSIMD 16
#define MaxChunkSize 64
#elif USE_AVX2
using vec_t = __m256i;
using psqt_vec_t = __m256i;
#define vec_load(a) _mm256_load_si256(a)
#define vec_store(a, b) _mm256_store_si256(a, b)
#define vec_add_16(a, b) _mm256_add_epi16(a, b)
#define vec_sub_16(a, b) _mm256_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b)
#define vec_zero() _mm256_setzero_si256()
#define vec_set_16(a) _mm256_set1_epi16(a)
#define vec_max_16(a, b) _mm256_max_epi16(a, b)
#define vec_min_16(a, b) _mm256_min_epi16(a, b)
#define vec_slli_16(a, b) _mm256_slli_epi16(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) _mm256_packus_epi16(a, b)
#define vec_load_psqt(a) _mm256_load_si256(a)
#define vec_store_psqt(a, b) _mm256_store_si256(a, b)
#define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b)
#define vec_zero_psqt() _mm256_setzero_si256()
#define NumRegistersSIMD 16
#define MaxChunkSize 32
#elif USE_SSE2
using vec_t = __m128i;
using psqt_vec_t = __m128i;
#define vec_load(a) (*(a))
#define vec_store(a, b) *(a) = (b)
#define vec_add_16(a, b) _mm_add_epi16(a, b)
#define vec_sub_16(a, b) _mm_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm_mulhi_epi16(a, b)
#define vec_zero() _mm_setzero_si128()
#define vec_set_16(a) _mm_set1_epi16(a)
#define vec_max_16(a, b) _mm_max_epi16(a, b)
#define vec_min_16(a, b) _mm_min_epi16(a, b)
#define vec_slli_16(a, b) _mm_slli_epi16(a, b)
#define vec_packus_16(a, b) _mm_packus_epi16(a, b)
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) _mm_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm_sub_epi32(a, b)
#define vec_zero_psqt() _mm_setzero_si128()
#define NumRegistersSIMD (Is64Bit ? 16 : 8)
#define MaxChunkSize 16
#elif USE_NEON
using vec_t = int16x8_t;
using psqt_vec_t = int32x4_t;
#define vec_load(a) (*(a))
#define vec_store(a, b) *(a) = (b)
#define vec_add_16(a, b) vaddq_s16(a, b)
#define vec_sub_16(a, b) vsubq_s16(a, b)
#define vec_mulhi_16(a, b) vqdmulhq_s16(a, b)
#define vec_zero() \
vec_t { 0 }
#define vec_set_16(a) vdupq_n_s16(a)
#define vec_max_16(a, b) vmaxq_s16(a, b)
#define vec_min_16(a, b) vminq_s16(a, b)
#define vec_slli_16(a, b) vshlq_s16(a, vec_set_16(b))
#define vec_packus_16(a, b) reinterpret_cast<vec_t>(vcombine_u8(vqmovun_s16(a), vqmovun_s16(b)))
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) vaddq_s32(a, b)
#define vec_sub_psqt_32(a, b) vsubq_s32(a, b)
#define vec_zero_psqt() \
psqt_vec_t { 0 }
#define NumRegistersSIMD 16
#define MaxChunkSize 16
#else
#undef VECTOR
#endif
// Returns the inverse of a permutation
template<std::size_t Len>
constexpr std::array<std::size_t, Len>
@@ -156,7 +49,7 @@ invert_permutation(const std::array<std::size_t, Len>& order) {
// Divide a byte region of size TotalSize to chunks of size
// BlockSize, and permute the blocks by a given order
template<std::size_t BlockSize, typename T, std::size_t N, std::size_t OrderSize>
void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
void permute(std::array<T, N>& data, const std::array<std::size_t, OrderSize>& order) {
constexpr std::size_t TotalSize = N * sizeof(T);
static_assert(TotalSize % (BlockSize * OrderSize) == 0,
@@ -166,7 +59,7 @@ void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
std::array<std::byte, ProcessChunkSize> buffer{};
std::byte* const bytes = reinterpret_cast<std::byte*>(data);
std::byte* const bytes = reinterpret_cast<std::byte*>(data.data());
for (std::size_t i = 0; i < TotalSize; i += ProcessChunkSize)
{
@@ -184,66 +77,11 @@ void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
}
}
// Compute optimal SIMD register count for feature transformer accumulation.
template<IndexType TransformedFeatureWidth, IndexType HalfDimensions>
class SIMDTiling {
#ifdef VECTOR
// We use __m* types as template arguments, which causes GCC to emit warnings
// about losing some attribute information. This is irrelevant to us as we
// only take their size, so the following pragma are harmless.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
static constexpr int BestRegisterCount() {
constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType);
constexpr std::size_t LaneSize = sizeof(LaneType);
static_assert(RegisterSize >= LaneSize);
static_assert(MaxRegisters <= NumRegistersSIMD);
static_assert(MaxRegisters > 0);
static_assert(NumRegistersSIMD > 0);
static_assert(RegisterSize % LaneSize == 0);
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
const int ideal = (NumLanes * LaneSize) / RegisterSize;
if (ideal <= MaxRegisters)
return ideal;
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
for (int divisor = MaxRegisters; divisor > 1; --divisor)
if (ideal % divisor == 0)
return divisor;
return 1;
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
public:
static constexpr int NumRegs =
BestRegisterCount<vec_t, WeightType, TransformedFeatureWidth, NumRegistersSIMD>();
static constexpr int NumPsqtRegs =
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
#endif
};
// Input feature converter
template<IndexType TransformedFeatureDimensions,
Accumulator<TransformedFeatureDimensions> AccumulatorState::*accPtr>
template<IndexType TransformedFeatureDimensions>
class FeatureTransformer {
static constexpr bool UseThreats =
(TransformedFeatureDimensions == TransformedFeatureDimensionsBig);
// Number of output dimensions for one side
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
@@ -252,7 +90,10 @@ class FeatureTransformer {
using OutputType = TransformedFeatureType;
// Number of input/output dimensions
static constexpr IndexType InputDimensions = FeatureSet::Dimensions;
static constexpr IndexType InputDimensions = PSQFeatureSet::Dimensions;
static constexpr IndexType ThreatInputDimensions = ThreatFeatureSet::Dimensions;
static constexpr IndexType TotalInputDimensions =
InputDimensions + (UseThreats ? ThreatInputDimensions : 0);
static constexpr IndexType OutputDimensions = HalfDimensions;
// Size of forward propagation buffer
@@ -283,75 +124,152 @@ class FeatureTransformer {
// Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value() {
return FeatureSet::HashValue ^ (OutputDimensions * 2);
return (UseThreats ? ThreatFeatureSet::HashValue : PSQFeatureSet::HashValue)
^ (OutputDimensions * 2);
}
void permute_weights() {
permute<16>(biases, PackusEpi16Order);
permute<16>(weights, PackusEpi16Order);
if (UseThreats)
permute<8>(threatWeights, PackusEpi16Order);
}
void unpermute_weights() {
permute<16>(biases, InversePackusEpi16Order);
permute<16>(weights, InversePackusEpi16Order);
if (UseThreats)
permute<8>(threatWeights, InversePackusEpi16Order);
}
inline void scale_weights(bool read) {
for (IndexType j = 0; j < InputDimensions; ++j)
{
WeightType* w = &weights[j * HalfDimensions];
for (IndexType i = 0; i < HalfDimensions; ++i)
w[i] = read ? w[i] * 2 : w[i] / 2;
}
for (IndexType i = 0; i < HalfDimensions; ++i)
biases[i] = read ? biases[i] * 2 : biases[i] / 2;
for (auto& w : weights)
w = read ? w * 2 : w / 2;
for (auto& b : biases)
b = read ? b * 2 : b / 2;
}
// Read network parameters
// TODO: This is ugly. Currently LEB128 on the entire L1 necessitates
// reading the weights into a combined array, and then splitting.
bool read_parameters(std::istream& stream) {
read_leb_128<BiasType>(stream, biases);
read_leb_128<BiasType>(stream, biases, HalfDimensions);
read_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
if (UseThreats)
{
read_little_endian<ThreatWeightType>(stream, threatWeights.data(),
ThreatInputDimensions * HalfDimensions);
read_leb_128<WeightType>(stream, weights);
auto combinedPsqtWeights =
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
read_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
std::copy(combinedPsqtWeights->begin(),
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
std::begin(threatPsqtWeights));
std::copy(combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
combinedPsqtWeights->begin()
+ (ThreatInputDimensions + InputDimensions) * PSQTBuckets,
std::begin(psqtWeights));
}
else
{
read_leb_128<WeightType>(stream, weights);
read_leb_128<PSQTWeightType>(stream, psqtWeights);
}
permute_weights();
scale_weights(true);
if (!UseThreats)
scale_weights(true);
return !stream.fail();
}
// Write network parameters
bool write_parameters(std::ostream& stream) {
bool write_parameters(std::ostream& stream) const {
std::unique_ptr<FeatureTransformer> copy = std::make_unique<FeatureTransformer>(*this);
unpermute_weights();
scale_weights(false);
copy->unpermute_weights();
write_leb_128<BiasType>(stream, biases, HalfDimensions);
write_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
write_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
if (!UseThreats)
copy->scale_weights(false);
write_leb_128<BiasType>(stream, copy->biases);
if (UseThreats)
{
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(),
ThreatInputDimensions * HalfDimensions);
write_leb_128<WeightType>(stream, copy->weights);
auto combinedPsqtWeights =
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
std::copy(std::begin(copy->threatPsqtWeights),
std::begin(copy->threatPsqtWeights) + ThreatInputDimensions * PSQTBuckets,
combinedPsqtWeights->begin());
std::copy(std::begin(copy->psqtWeights),
std::begin(copy->psqtWeights) + InputDimensions * PSQTBuckets,
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets);
write_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
}
else
{
write_leb_128<WeightType>(stream, copy->weights);
write_leb_128<PSQTWeightType>(stream, copy->psqtWeights);
}
permute_weights();
scale_weights(true);
return !stream.fail();
}
std::size_t get_content_hash() const {
std::size_t h = 0;
hash_combine(h, get_raw_data_hash(biases));
hash_combine(h, get_raw_data_hash(weights));
hash_combine(h, get_raw_data_hash(psqtWeights));
hash_combine(h, get_hash_value());
return h;
}
// Convert input features
std::int32_t transform(const Position& pos,
AccumulatorStack& accumulatorStack,
AccumulatorCaches::Cache<HalfDimensions>* cache,
AccumulatorCaches::Cache<HalfDimensions>& cache,
OutputType* output,
int bucket) const {
accumulatorStack.evaluate(pos, *this, *cache);
const auto& accumulatorState = accumulatorStack.latest();
using namespace SIMD;
accumulatorStack.evaluate(pos, *this, cache);
const auto& accumulatorState = accumulatorStack.latest<PSQFeatureSet>();
const auto& threatAccumulatorState = accumulatorStack.latest<ThreatFeatureSet>();
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
const auto& psqtAccumulation = (accumulatorState.*accPtr).psqtAccumulation;
const auto psqt =
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket])
/ 2;
const auto& psqtAccumulation = (accumulatorState.acc<HalfDimensions>()).psqtAccumulation;
auto psqt =
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]);
const auto& accumulation = (accumulatorState.*accPtr).accumulation;
if (UseThreats)
{
const auto& threatPsqtAccumulation =
(threatAccumulatorState.acc<HalfDimensions>()).psqtAccumulation;
psqt = (psqt + threatPsqtAccumulation[perspectives[0]][bucket]
- threatPsqtAccumulation[perspectives[1]][bucket])
/ 2;
}
else
psqt /= 2;
const auto& accumulation = (accumulatorState.acc<HalfDimensions>()).accumulation;
const auto& threatAccumulation =
(threatAccumulatorState.acc<HalfDimensions>()).accumulation;
for (IndexType p = 0; p < 2; ++p)
{
@@ -364,7 +282,7 @@ class FeatureTransformer {
constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
const vec_t Zero = vec_zero();
const vec_t One = vec_set_16(127 * 2);
const vec_t One = vec_set_16(UseThreats ? 255 : 127 * 2);
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
const vec_t* in1 =
@@ -430,20 +348,48 @@ class FeatureTransformer {
#else
6;
#endif
for (IndexType j = 0; j < NumOutputChunks; ++j)
if (UseThreats)
{
const vec_t sum0a =
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift);
const vec_t sum0b =
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift);
const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One);
const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One);
const vec_t* tin0 =
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
const vec_t* tin1 = reinterpret_cast<const vec_t*>(
&(threatAccumulation[perspectives[p]][HalfDimensions / 2]));
for (IndexType j = 0; j < NumOutputChunks; ++j)
{
const vec_t acc0a = vec_add_16(in0[j * 2 + 0], tin0[j * 2 + 0]);
const vec_t acc0b = vec_add_16(in0[j * 2 + 1], tin0[j * 2 + 1]);
const vec_t acc1a = vec_add_16(in1[j * 2 + 0], tin1[j * 2 + 0]);
const vec_t acc1b = vec_add_16(in1[j * 2 + 1], tin1[j * 2 + 1]);
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
const vec_t sum0a =
vec_slli_16(vec_max_16(vec_min_16(acc0a, One), Zero), shift);
const vec_t sum0b =
vec_slli_16(vec_max_16(vec_min_16(acc0b, One), Zero), shift);
const vec_t sum1a = vec_min_16(acc1a, One);
const vec_t sum1b = vec_min_16(acc1b, One);
out[j] = vec_packus_16(pa, pb);
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
out[j] = vec_packus_16(pa, pb);
}
}
else
{
for (IndexType j = 0; j < NumOutputChunks; ++j)
{
const vec_t sum0a =
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift);
const vec_t sum0b =
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift);
const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One);
const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One);
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
out[j] = vec_packus_16(pa, pb);
}
}
#else
@@ -453,8 +399,21 @@ class FeatureTransformer {
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
BiasType sum1 =
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
sum0 = std::clamp<BiasType>(sum0, 0, 127 * 2);
sum1 = std::clamp<BiasType>(sum1, 0, 127 * 2);
if (UseThreats)
{
BiasType sum0t = threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
BiasType sum1t =
threatAccumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
sum0 = std::clamp<BiasType>(sum0 + sum0t, 0, 255);
sum1 = std::clamp<BiasType>(sum1 + sum1t, 0, 255);
}
else
{
sum0 = std::clamp<BiasType>(sum0, 0, 127 * 2);
sum1 = std::clamp<BiasType>(sum1, 0, 127 * 2);
}
output[offset + j] = static_cast<OutputType>(unsigned(sum0 * sum1) / 512);
}
@@ -464,11 +423,27 @@ class FeatureTransformer {
return psqt;
} // end of function transform()
alignas(CacheLineSize) BiasType biases[HalfDimensions];
alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
alignas(CacheLineSize) std::array<BiasType, HalfDimensions> biases;
alignas(CacheLineSize) std::array<WeightType, HalfDimensions * InputDimensions> weights;
alignas(CacheLineSize)
std::array<ThreatWeightType,
UseThreats ? HalfDimensions * ThreatInputDimensions : 0> threatWeights;
alignas(CacheLineSize) std::array<PSQTWeightType, InputDimensions * PSQTBuckets> psqtWeights;
alignas(CacheLineSize)
std::array<PSQTWeightType,
UseThreats ? ThreatInputDimensions * PSQTBuckets : 0> threatPsqtWeights;
};
} // namespace Stockfish::Eval::NNUE
template<Stockfish::Eval::NNUE::IndexType TransformedFeatureDimensions>
struct std::hash<Stockfish::Eval::NNUE::FeatureTransformer<TransformedFeatureDimensions>> {
std::size_t
operator()(const Stockfish::Eval::NNUE::FeatureTransformer<TransformedFeatureDimensions>& ft)
const noexcept {
return ft.get_content_hash();
}
};
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
+7 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -120,12 +120,11 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
format_cp_compact(value, &board[y + 2][x + 2], pos);
};
AccumulatorStack accumulators;
accumulators.reset(pos, networks, caches);
auto accumulators = std::make_unique<AccumulatorStack>();
// We estimate the value of each piece by doing a differential evaluation from
// the current base eval, simulating the removal of the piece from its square.
auto [psqt, positional] = networks.big.evaluate(pos, accumulators, &caches.big);
auto [psqt, positional] = networks.big.evaluate(pos, *accumulators, caches.big);
Value base = psqt + positional;
base = pos.side_to_move() == WHITE ? base : -base;
@@ -140,8 +139,8 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
{
pos.remove_piece(sq);
accumulators.reset(pos, networks, caches);
std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, &caches.big);
accumulators->reset();
std::tie(psqt, positional) = networks.big.evaluate(pos, *accumulators, caches.big);
Value eval = psqt + positional;
eval = pos.side_to_move() == WHITE ? eval : -eval;
v = base - eval;
@@ -157,8 +156,8 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
ss << board[row] << '\n';
ss << '\n';
accumulators.reset(pos, networks, caches);
auto t = networks.big.trace_evaluate(pos, accumulators, &caches.big);
accumulators->reset();
auto t = networks.big.trace_evaluate(pos, *accumulators, caches.big);
ss << " NNUE network contributions "
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
+18 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,10 @@
#define NNUE_MISC_H_INCLUDED
#include <cstddef>
#include <memory>
#include <string>
#include "../misc.h"
#include "../types.h"
#include "nnue_architecture.h"
@@ -31,17 +33,17 @@ class Position;
namespace Eval::NNUE {
// EvalFile uses fixed string types because it's part of the network structure which must be trivial.
struct EvalFile {
// Default net name, will use one of the EvalFileDefaultName* macros defined
// in evaluate.h
std::string defaultName;
FixedString<256> defaultName;
// Selected net name, either via uci option or default
std::string current;
FixedString<256> current;
// Net description extracted from the net file
std::string netDescription;
FixedString<256> netDescription;
};
struct NnueEvalTrace {
static_assert(LayerStacks == PSQTBuckets);
@@ -58,4 +60,15 @@ std::string trace(Position& pos, const Networks& networks, AccumulatorCaches& ca
} // namespace Stockfish::Eval::NNUE
} // namespace Stockfish
template<>
struct std::hash<Stockfish::Eval::NNUE::EvalFile> {
std::size_t operator()(const Stockfish::Eval::NNUE::EvalFile& evalFile) const noexcept {
std::size_t h = 0;
Stockfish::hash_combine(h, evalFile.defaultName);
Stockfish::hash_combine(h, evalFile.current);
Stockfish::hash_combine(h, evalFile.netDescription);
return h;
}
};
#endif // #ifndef NNUE_MISC_H_INCLUDED
+434
View File
@@ -0,0 +1,434 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NNUE_SIMD_H_INCLUDED
#define NNUE_SIMD_H_INCLUDED
#if defined(USE_AVX2)
#include <immintrin.h>
#elif defined(USE_SSE41)
#include <smmintrin.h>
#elif defined(USE_SSSE3)
#include <tmmintrin.h>
#elif defined(USE_SSE2)
#include <emmintrin.h>
#elif defined(USE_NEON)
#include <arm_neon.h>
#endif
#include "../types.h"
#include "nnue_common.h"
namespace Stockfish::Eval::NNUE::SIMD {
// If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's
// vector registers.
#define VECTOR
#ifdef USE_AVX512
using vec_t = __m512i;
using vec_i8_t = __m256i;
using vec128_t = __m128i;
using psqt_vec_t = __m256i;
using vec_uint_t = __m512i;
#define vec_load(a) _mm512_load_si512(a)
#define vec_store(a, b) _mm512_store_si512(a, b)
#define vec_convert_8_16(a) _mm512_cvtepi8_epi16(a)
#define vec_add_16(a, b) _mm512_add_epi16(a, b)
#define vec_sub_16(a, b) _mm512_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b)
#define vec_zero() _mm512_setzero_epi32()
#define vec_set_16(a) _mm512_set1_epi16(a)
#define vec_max_16(a, b) _mm512_max_epi16(a, b)
#define vec_min_16(a, b) _mm512_min_epi16(a, b)
#define vec_slli_16(a, b) _mm512_slli_epi16(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) _mm512_packus_epi16(a, b)
#define vec_load_psqt(a) _mm256_load_si256(a)
#define vec_store_psqt(a, b) _mm256_store_si256(a, b)
#define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b)
#define vec_zero_psqt() _mm256_setzero_si256()
#ifdef USE_SSSE3
#define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512())
#endif
#define vec128_zero _mm_setzero_si128()
#define vec128_set_16(a) _mm_set1_epi16(a)
#define vec128_load(a) _mm_load_si128(a)
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
#define vec128_add(a, b) _mm_add_epi16(a, b)
#define NumRegistersSIMD 16
#define MaxChunkSize 64
#elif USE_AVX2
using vec_t = __m256i;
using vec_i8_t = __m128i;
using vec128_t = __m128i;
using psqt_vec_t = __m256i;
using vec_uint_t = __m256i;
#define vec_load(a) _mm256_load_si256(a)
#define vec_store(a, b) _mm256_store_si256(a, b)
#define vec_convert_8_16(a) _mm256_cvtepi8_epi16(a)
#define vec_add_16(a, b) _mm256_add_epi16(a, b)
#define vec_sub_16(a, b) _mm256_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b)
#define vec_zero() _mm256_setzero_si256()
#define vec_set_16(a) _mm256_set1_epi16(a)
#define vec_max_16(a, b) _mm256_max_epi16(a, b)
#define vec_min_16(a, b) _mm256_min_epi16(a, b)
#define vec_slli_16(a, b) _mm256_slli_epi16(a, b)
// Inverse permuted at load time
#define vec_packus_16(a, b) _mm256_packus_epi16(a, b)
#define vec_load_psqt(a) _mm256_load_si256(a)
#define vec_store_psqt(a, b) _mm256_store_si256(a, b)
#define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b)
#define vec_zero_psqt() _mm256_setzero_si256()
#ifdef USE_SSSE3
#if defined(USE_VNNI) && !defined(USE_AVXVNNI)
#define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256())
#else
#define vec_nnz(a) \
_mm256_movemask_ps( \
_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256())))
#endif
#endif
#define vec128_zero _mm_setzero_si128()
#define vec128_set_16(a) _mm_set1_epi16(a)
#define vec128_load(a) _mm_load_si128(a)
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
#define vec128_add(a, b) _mm_add_epi16(a, b)
#define NumRegistersSIMD 12
#define MaxChunkSize 32
#elif USE_SSE2
using vec_t = __m128i;
using vec_i8_t = std::uint64_t; // for the correct size -- will be loaded into an xmm reg
using vec128_t = __m128i;
using psqt_vec_t = __m128i;
using vec_uint_t = __m128i;
#define vec_load(a) (*(a))
#define vec_store(a, b) *(a) = (b)
#define vec_add_16(a, b) _mm_add_epi16(a, b)
#define vec_sub_16(a, b) _mm_sub_epi16(a, b)
#define vec_mulhi_16(a, b) _mm_mulhi_epi16(a, b)
#define vec_zero() _mm_setzero_si128()
#define vec_set_16(a) _mm_set1_epi16(a)
#define vec_max_16(a, b) _mm_max_epi16(a, b)
#define vec_min_16(a, b) _mm_min_epi16(a, b)
#define vec_slli_16(a, b) _mm_slli_epi16(a, b)
#define vec_packus_16(a, b) _mm_packus_epi16(a, b)
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) _mm_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm_sub_epi32(a, b)
#define vec_zero_psqt() _mm_setzero_si128()
#ifdef USE_SSSE3
#define vec_nnz(a) \
_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
#endif
#ifdef __i386__
inline __m128i _mm_cvtsi64_si128(int64_t val) {
return _mm_loadl_epi64(reinterpret_cast<const __m128i*>(&val));
}
#endif
#ifdef USE_SSE41
#define vec_convert_8_16(a) _mm_cvtepi8_epi16(_mm_cvtsi64_si128(static_cast<int64_t>(a)))
#else
// Credit: Yoshie2000
inline __m128i vec_convert_8_16(uint64_t x) {
__m128i v8 = _mm_cvtsi64_si128(static_cast<int64_t>(x));
__m128i sign = _mm_cmpgt_epi8(_mm_setzero_si128(), v8);
return _mm_unpacklo_epi8(v8, sign);
}
#endif
#define vec128_zero _mm_setzero_si128()
#define vec128_set_16(a) _mm_set1_epi16(a)
#define vec128_load(a) _mm_load_si128(a)
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
#define vec128_add(a, b) _mm_add_epi16(a, b)
#define NumRegistersSIMD (Is64Bit ? 12 : 6)
#define MaxChunkSize 16
#elif USE_NEON
using vec_t = int16x8_t;
using vec_i8_t = int8x16_t;
using psqt_vec_t = int32x4_t;
using vec128_t = uint16x8_t;
using vec_uint_t = uint32x4_t;
#define vec_load(a) (*(a))
#define vec_store(a, b) *(a) = (b)
#define vec_add_16(a, b) vaddq_s16(a, b)
#define vec_sub_16(a, b) vsubq_s16(a, b)
#define vec_mulhi_16(a, b) vqdmulhq_s16(a, b)
#define vec_zero() vec_t{0}
#define vec_set_16(a) vdupq_n_s16(a)
#define vec_max_16(a, b) vmaxq_s16(a, b)
#define vec_min_16(a, b) vminq_s16(a, b)
#define vec_slli_16(a, b) vshlq_s16(a, vec_set_16(b))
#define vec_packus_16(a, b) reinterpret_cast<vec_t>(vcombine_u8(vqmovun_s16(a), vqmovun_s16(b)))
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) vaddq_s32(a, b)
#define vec_sub_psqt_32(a, b) vsubq_s32(a, b)
#define vec_zero_psqt() psqt_vec_t{0}
static constexpr std::uint32_t Mask[4] = {1, 2, 4, 8};
#define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask)))
#define vec128_zero vdupq_n_u16(0)
#define vec128_set_16(a) vdupq_n_u16(a)
#define vec128_load(a) vld1q_u16(reinterpret_cast<const std::uint16_t*>(a))
#define vec128_storeu(a, b) vst1q_u16(reinterpret_cast<std::uint16_t*>(a), b)
#define vec128_add(a, b) vaddq_u16(a, b)
#define NumRegistersSIMD 16
#define MaxChunkSize 16
#ifndef __aarch64__
// Single instruction doesn't exist on 32-bit ARM
inline int8x16_t vmovl_high_s8(int8x16_t val) { return vmovl_s8(vget_high_s8(val)); }
#endif
#else
#undef VECTOR
#endif
struct Vec16Wrapper {
#ifdef VECTOR
using type = vec_t;
static type add(const type& lhs, const type& rhs) { return vec_add_16(lhs, rhs); }
static type sub(const type& lhs, const type& rhs) { return vec_sub_16(lhs, rhs); }
#else
using type = BiasType;
static type add(const type& lhs, const type& rhs) { return lhs + rhs; }
static type sub(const type& lhs, const type& rhs) { return lhs - rhs; }
#endif
};
struct Vec32Wrapper {
#ifdef VECTOR
using type = psqt_vec_t;
static type add(const type& lhs, const type& rhs) { return vec_add_psqt_32(lhs, rhs); }
static type sub(const type& lhs, const type& rhs) { return vec_sub_psqt_32(lhs, rhs); }
#else
using type = PSQTWeightType;
static type add(const type& lhs, const type& rhs) { return lhs + rhs; }
static type sub(const type& lhs, const type& rhs) { return lhs - rhs; }
#endif
};
enum UpdateOperation {
Add,
Sub
};
template<typename VecWrapper,
UpdateOperation... ops,
std::enable_if_t<sizeof...(ops) == 0, bool> = true>
typename VecWrapper::type fused(const typename VecWrapper::type& in) {
return in;
}
template<typename VecWrapper,
UpdateOperation update_op,
UpdateOperation... ops,
typename T,
typename... Ts,
std::enable_if_t<is_all_same_v<typename VecWrapper::type, T, Ts...>, bool> = true,
std::enable_if_t<sizeof...(ops) == sizeof...(Ts), bool> = true>
typename VecWrapper::type
fused(const typename VecWrapper::type& in, const T& operand, const Ts&... operands) {
switch (update_op)
{
case Add :
return fused<VecWrapper, ops...>(VecWrapper::add(in, operand), operands...);
case Sub :
return fused<VecWrapper, ops...>(VecWrapper::sub(in, operand), operands...);
default :
static_assert(update_op == Add || update_op == Sub,
"Only Add and Sub are currently supported.");
return typename VecWrapper::type();
}
}
#if defined(USE_AVX512)
[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) {
return _mm512_reduce_add_epi32(sum) + bias;
}
[[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) {
#if defined(USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a, b);
#else
__m512i product0 = _mm512_maddubs_epi16(a, b);
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, product0);
#endif
}
#endif
#if defined(USE_AVX2)
[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) {
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
return _mm_cvtsi128_si32(sum128) + bias;
}
[[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) {
#if defined(USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a, b);
#else
__m256i product0 = _mm256_maddubs_epi16(a, b);
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, product0);
#endif
}
#endif
#if defined(USE_SSSE3)
[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) {
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
return _mm_cvtsi128_si32(sum) + bias;
}
[[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) {
__m128i product0 = _mm_maddubs_epi16(a, b);
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, product0);
}
#endif
#if defined(USE_NEON_DOTPROD)
[[maybe_unused]] static void
dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
acc = vdotq_s32(acc, a, b);
}
#endif
#if defined(USE_NEON)
[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
#if USE_NEON >= 8
return vaddvq_s32(s);
#else
return s[0] + s[1] + s[2] + s[3];
#endif
}
[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) {
return neon_m128_reduce_add_epi32(sum) + bias;
}
#endif
#if USE_NEON >= 8
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b));
int16x8_t product1 = vmull_high_s8(a, b);
int16x8_t sum = vpaddq_s16(product0, product1);
acc = vpadalq_s16(acc, sum);
}
#endif
// Compute optimal SIMD register count for feature transformer accumulation.
template<IndexType TransformedFeatureWidth, IndexType HalfDimensions, IndexType PSQTBuckets>
class SIMDTiling {
#ifdef VECTOR
// We use __m* types as template arguments, which causes GCC to emit warnings
// about losing some attribute information. This is irrelevant to us as we
// only take their size, so the following pragma are harmless.
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
static constexpr int BestRegisterCount() {
constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType);
constexpr std::size_t LaneSize = sizeof(LaneType);
static_assert(RegisterSize >= LaneSize);
static_assert(MaxRegisters <= NumRegistersSIMD);
static_assert(MaxRegisters > 0);
static_assert(NumRegistersSIMD > 0);
static_assert(RegisterSize % LaneSize == 0);
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
const int ideal = (NumLanes * LaneSize) / RegisterSize;
if (ideal <= MaxRegisters)
return ideal;
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
for (int divisor = MaxRegisters; divisor > 1; --divisor)
if (ideal % divisor == 0)
return divisor;
return 1;
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
public:
static constexpr int NumRegs =
BestRegisterCount<vec_t, WeightType, TransformedFeatureWidth, NumRegistersSIMD>();
static constexpr int NumPsqtRegs =
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
#endif
};
}
#endif
+140 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@
#include <vector>
#include <cstring>
#include "memory.h"
#include "shm.h"
// We support linux very well, but we explicitly do NOT support Android,
// because there is no affected systems, not worth maintaining.
@@ -945,10 +945,11 @@ class NumaConfig {
th.join();
}
private:
std::vector<std::set<CpuIndex>> nodes;
std::map<CpuIndex, NumaIndex> nodeByCpu;
CpuIndex highestCpuIndex;
private:
CpuIndex highestCpuIndex;
bool customAffinity;
@@ -1263,6 +1264,141 @@ class LazyNumaReplicated: public NumaReplicatedBase {
}
};
// Utilizes shared memory.
template<typename T>
class LazyNumaReplicatedSystemWide: public NumaReplicatedBase {
public:
using ReplicatorFuncType = std::function<T(const T&)>;
LazyNumaReplicatedSystemWide(NumaReplicationContext& ctx) :
NumaReplicatedBase(ctx) {
prepare_replicate_from(std::make_unique<T>());
}
LazyNumaReplicatedSystemWide(NumaReplicationContext& ctx, std::unique_ptr<T>&& source) :
NumaReplicatedBase(ctx) {
prepare_replicate_from(std::move(source));
}
LazyNumaReplicatedSystemWide(const LazyNumaReplicatedSystemWide&) = delete;
LazyNumaReplicatedSystemWide(LazyNumaReplicatedSystemWide&& other) noexcept :
NumaReplicatedBase(std::move(other)),
instances(std::exchange(other.instances, {})) {}
LazyNumaReplicatedSystemWide& operator=(const LazyNumaReplicatedSystemWide&) = delete;
LazyNumaReplicatedSystemWide& operator=(LazyNumaReplicatedSystemWide&& other) noexcept {
NumaReplicatedBase::operator=(*this, std::move(other));
instances = std::exchange(other.instances, {});
return *this;
}
LazyNumaReplicatedSystemWide& operator=(std::unique_ptr<T>&& source) {
prepare_replicate_from(std::move(source));
return *this;
}
~LazyNumaReplicatedSystemWide() override = default;
const T& operator[](NumaReplicatedAccessToken token) const {
assert(token.get_numa_index() < instances.size());
ensure_present(token.get_numa_index());
return *(instances[token.get_numa_index()]);
}
const T& operator*() const { return *(instances[0]); }
const T* operator->() const { return &*instances[0]; }
std::vector<std::pair<SystemWideSharedConstantAllocationStatus, std::optional<std::string>>>
get_status_and_errors() const {
std::vector<std::pair<SystemWideSharedConstantAllocationStatus, std::optional<std::string>>>
status;
status.reserve(instances.size());
for (const auto& instance : instances)
{
status.emplace_back(instance.get_status(), instance.get_error_message());
}
return status;
}
template<typename FuncT>
void modify_and_replicate(FuncT&& f) {
auto source = std::make_unique<T>(*instances[0]);
std::forward<FuncT>(f)(*source);
prepare_replicate_from(std::move(source));
}
void on_numa_config_changed() override {
// Use the first one as the source. It doesn't matter which one we use,
// because they all must be identical, but the first one is guaranteed to exist.
auto source = std::make_unique<T>(*instances[0]);
prepare_replicate_from(std::move(source));
}
private:
mutable std::vector<SystemWideSharedConstant<T>> instances;
mutable std::mutex mutex;
std::size_t get_discriminator(NumaIndex idx) const {
const NumaConfig& cfg = get_numa_config();
const NumaConfig& cfg_sys = NumaConfig::from_system(false);
// as a discriminator, locate the hardware/system numadomain this cpuindex belongs to
CpuIndex cpu = *cfg.nodes[idx].begin(); // get a CpuIndex from NumaIndex
NumaIndex sys_idx = cfg_sys.is_cpu_assigned(cpu) ? cfg_sys.nodeByCpu.at(cpu) : 0;
std::string s = cfg_sys.to_string() + "$" + std::to_string(sys_idx);
return std::hash<std::string>{}(s);
}
void ensure_present(NumaIndex idx) const {
assert(idx < instances.size());
if (instances[idx] != nullptr)
return;
assert(idx != 0);
std::unique_lock<std::mutex> lock(mutex);
// Check again for races.
if (instances[idx] != nullptr)
return;
const NumaConfig& cfg = get_numa_config();
cfg.execute_on_numa_node(idx, [this, idx]() {
instances[idx] = SystemWideSharedConstant<T>(*instances[0], get_discriminator(idx));
});
}
void prepare_replicate_from(std::unique_ptr<T>&& source) {
instances.clear();
const NumaConfig& cfg = get_numa_config();
// We just need to make sure the first instance is there.
// Note that we cannot move here as we need to reallocate the data
// on the correct NUMA node.
// Even in the case of a single NUMA node we have to copy since it's shared memory.
if (cfg.requires_memory_replication())
{
assert(cfg.num_numa_nodes() > 0);
cfg.execute_on_numa_node(0, [this, &source]() {
instances.emplace_back(SystemWideSharedConstant<T>(*source, get_discriminator(0)));
});
// Prepare others for lazy init.
instances.resize(cfg.num_numa_nodes());
}
else
{
assert(cfg.num_numa_nodes() == 1);
instances.emplace_back(SystemWideSharedConstant<T>(*source, get_discriminator(0)));
}
}
};
class NumaReplicationContext {
public:
NumaReplicationContext(NumaConfig&& cfg) :
+6 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <cstdint>
#include "cluster.h"
#include "movegen.h"
#include "position.h"
#include "types.h"
@@ -49,16 +50,16 @@ uint64_t perft(Position& pos, Depth depth) {
nodes += cnt;
pos.undo_move(m);
}
if (Root)
if (Root && Distributed::is_root())
sync_cout << UCIEngine::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
}
return nodes;
}
inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) {
StateListPtr states(new std::deque<StateInfo>(1));
Position p;
p.set(fen, isChess960, &states->back());
StateInfo st;
Position p;
p.set(fen, isChess960, &st);
return perft<true>(p, depth);
}
+325 -79
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@
#include <utility>
#include "bitboard.h"
#include "history.h"
#include "misc.h"
#include "movegen.h"
#include "syzygy/tbprobe.h"
@@ -48,14 +49,15 @@ Key psq[PIECE_NB][SQUARE_NB];
Key enpassant[FILE_NB];
Key castling[CASTLING_RIGHT_NB];
Key side, noPawns;
}
namespace {
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING};
static constexpr Piece Pieces[] = {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING};
} // namespace
@@ -79,7 +81,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
for (Bitboard b = pos.checkers(); b;)
os << UCIEngine::square(pop_lsb(b)) << " ";
if (int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
if (Tablebases::MaxCardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
{
StateInfo st;
@@ -200,7 +202,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si) {
Square sq = SQ_A8;
std::istringstream ss(fenStr);
std::memset(this, 0, sizeof(Position));
std::memset(reinterpret_cast<char*>(this), 0, sizeof(Position));
std::memset(si, 0, sizeof(StateInfo));
st = si;
@@ -270,7 +272,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si) {
// a) side to move have a pawn threatening epSquare
// b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
enpassant = attacks_bb<PAWN>(st->epSquare, ~sideToMove) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
}
@@ -321,7 +323,7 @@ void Position::set_check_info() const {
Square ksq = square<KING>(~sideToMove);
st->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
st->checkSquares[PAWN] = attacks_bb<PAWN>(ksq, ~sideToMove);
st->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
st->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
st->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
@@ -335,8 +337,8 @@ void Position::set_check_info() const {
// The function is only used when a new position is set up
void Position::set_state() const {
st->key = st->materialKey = 0;
st->minorPieceKey = 0;
st->key = 0;
st->minorPieceKey = 0;
st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0;
st->pawnKey = Zobrist::noPawns;
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
@@ -374,10 +376,15 @@ void Position::set_state() const {
st->key ^= Zobrist::side;
st->key ^= Zobrist::castling[st->castlingRights];
st->materialKey = compute_material_key();
}
Key Position::compute_material_key() const {
Key k = 0;
for (Piece pc : Pieces)
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
st->materialKey ^= Zobrist::psq[pc][8 + cnt];
k ^= Zobrist::psq[pc][8 + cnt];
return k;
}
@@ -487,8 +494,8 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
| (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
| (attacks_bb<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_bb<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING));
}
@@ -498,7 +505,7 @@ bool Position::attackers_to_exist(Square s, Bitboard occupied, Color c) const {
&& (attacks_bb<ROOK>(s, occupied) & pieces(c, ROOK, QUEEN)))
|| ((attacks_bb<BISHOP>(s) & pieces(c, BISHOP, QUEEN))
&& (attacks_bb<BISHOP>(s, occupied) & pieces(c, BISHOP, QUEEN)))
|| (((pawn_attacks_bb(~c, s) & pieces(PAWN)) | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|| (((attacks_bb<PAWN>(s, ~c) & pieces(PAWN)) | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
| (attacks_bb<KING>(s) & pieces(KING)))
& pieces(c));
}
@@ -597,10 +604,14 @@ bool Position::pseudo_legal(const Move m) const {
if ((Rank8BB | Rank1BB) & to)
return false;
if (!(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
&& !((from + 2 * pawn_push(us) == to) // Not a double push
&& (relative_rank(us, from) == RANK_2) && empty(to) && empty(to - pawn_push(us))))
// Check if it's a valid capture, single push, or double push
const bool isCapture = bool(attacks_bb<PAWN>(from, us) & pieces(~us) & to);
const bool isSinglePush = (from + pawn_push(us) == to) && empty(to);
const bool isDoublePush = (from + 2 * pawn_push(us) == to)
&& (relative_rank(us, from) == RANK_2) && empty(to)
&& empty(to - pawn_push(us));
if (!(isCapture || isSinglePush || isDoublePush))
return false;
}
else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
@@ -682,11 +693,14 @@ bool Position::gives_check(Move m) const {
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
// moves should be filtered out before this function is called.
// If a pointer to the TT table is passed, the entry for the new position
// will be prefetched
DirtyPiece Position::do_move(Move m,
StateInfo& newSt,
bool givesCheck,
const TranspositionTable* tt = nullptr) {
// will be prefetched, and likewise for shared history.
void Position::do_move(Move m,
StateInfo& newSt,
bool givesCheck,
DirtyPiece& dp,
DirtyThreats& dts,
const TranspositionTable* tt = nullptr,
const SharedHistories* history = nullptr) {
assert(m.is_ok());
assert(&newSt != st);
@@ -698,7 +712,6 @@ DirtyPiece Position::do_move(Move m,
// our state pointer to point to the new (ready to be updated) state.
std::memcpy(&newSt, st, offsetof(StateInfo, key));
newSt.previous = st;
st->next = &newSt;
st = &newSt;
// Increment ply counters. In particular, rule50 will be reset to zero later on
@@ -707,9 +720,6 @@ DirtyPiece Position::do_move(Move m,
++st->rule50;
++st->pliesFromNull;
DirtyPiece dp;
dp.dirty_num = 1;
Color us = sideToMove;
Color them = ~us;
Square from = m.from_sq();
@@ -717,6 +727,16 @@ DirtyPiece Position::do_move(Move m,
Piece pc = piece_on(from);
Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
bool checkEP = false;
dp.pc = pc;
dp.from = from;
dp.to = to;
dp.add_sq = SQ_NONE;
dts.us = us;
dts.prevKsq = square<KING>(us);
dts.threatenedSqs = dts.threateningSqs = 0;
assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us));
assert(type_of(captured) != KING);
@@ -727,14 +747,13 @@ DirtyPiece Position::do_move(Move m,
assert(captured == make_piece(us, ROOK));
Square rfrom, rto;
do_castling<true>(us, from, to, rfrom, rto, &dp);
do_castling<true>(us, from, to, rfrom, rto, &dts, &dp);
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
captured = NO_PIECE;
}
if (captured)
else if (captured)
{
Square capsq = to;
@@ -751,6 +770,9 @@ DirtyPiece Position::do_move(Move m,
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(to) == NO_PIECE);
assert(piece_on(capsq) == make_piece(them, PAWN));
// Update board and piece lists in ep case, normal captures are updated later
remove_piece(capsq, &dts);
}
st->pawnKey ^= Zobrist::psq[captured][capsq];
@@ -764,20 +786,18 @@ DirtyPiece Position::do_move(Move m,
st->minorPieceKey ^= Zobrist::psq[captured][capsq];
}
dp.dirty_num = 2; // 1 piece moved, 1 piece captured
dp.piece[1] = captured;
dp.from[1] = capsq;
dp.to[1] = SQ_NONE;
// Update board and piece lists
remove_piece(capsq);
dp.remove_pc = captured;
dp.remove_sq = capsq;
k ^= Zobrist::psq[captured][capsq];
st->materialKey ^= Zobrist::psq[captured][8 + pieceCount[captured]];
st->materialKey ^=
Zobrist::psq[captured][8 + pieceCount[captured] - (m.type_of() != EN_PASSANT)];
// Reset rule 50 counter
st->rule50 = 0;
}
else
dp.remove_sq = SQ_NONE;
// Update hash key
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
@@ -800,23 +820,21 @@ DirtyPiece Position::do_move(Move m,
// Move the piece. The tricky Chess960 castling is handled earlier
if (m.type_of() != CASTLING)
{
dp.piece[0] = pc;
dp.from[0] = from;
dp.to[0] = to;
move_piece(from, to);
if (captured && m.type_of() != EN_PASSANT)
{
remove_piece(from, &dts);
swap_piece(to, pc, &dts);
}
else
move_piece(from, to, &dts);
}
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
{
// Set en passant square if the moved pawn can be captured
if ((int(to) ^ int(from)) == 16
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
// Check later if the en passant square needs to be set
if ((int(to) ^ int(from)) == 16)
checkEP = true;
else if (m.type_of() == PROMOTION)
{
@@ -826,21 +844,18 @@ DirtyPiece Position::do_move(Move m,
assert(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
remove_piece(to);
put_piece(promotion, to);
swap_piece(to, promotion, &dts);
// Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
dp.to[0] = SQ_NONE;
dp.piece[dp.dirty_num] = promotion;
dp.from[dp.dirty_num] = SQ_NONE;
dp.to[dp.dirty_num] = to;
dp.dirty_num++;
dp.add_pc = promotion;
dp.add_sq = to;
dp.to = SQ_NONE;
// Update hash keys
// Zobrist::psq[pc][to] is zero, so we don't need to clear it
k ^= Zobrist::psq[promotion][to];
st->materialKey ^= Zobrist::psq[promotion][8 + pieceCount[promotion] - 1]
^ Zobrist::psq[pc][8 + pieceCount[pc]];
st->nonPawnKey[us] ^= Zobrist::psq[promotion][to];
if (promotionType <= BISHOP)
st->minorPieceKey ^= Zobrist::psq[promotion][to];
@@ -864,10 +879,18 @@ DirtyPiece Position::do_move(Move m,
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
}
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// If en passant is impossible, then k will not change and we can prefetch earlier
if (tt && !checkEP)
prefetch(tt->first_entry(adjust_key50(k)));
if (history)
{
prefetch(&history->pawn_entry(*this)[pc][to]);
prefetch(&history->pawn_correction_entry(*this));
prefetch(&history->minor_piece_correction_entry(*this));
prefetch(&history->nonpawn_correction_entry<WHITE>(*this));
prefetch(&history->nonpawn_correction_entry<BLACK>(*this));
}
// Set capture piece
st->capturedPiece = captured;
@@ -880,6 +903,63 @@ DirtyPiece Position::do_move(Move m,
// Update king attacks used for fast check detection
set_check_info();
// Accurate e.p. info is needed for correct zobrist key generation and 3-fold checking
while (checkEP)
{
auto updateEpSquare = [&] {
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
};
Bitboard pawns = attacks_bb<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN);
// If there are no pawns attacking the ep square, ep is not possible
if (!pawns)
break;
// If there are checkers other than the to be captured pawn, ep is never legal
if (checkers() & ~square_bb(to))
break;
if (more_than_one(pawns))
{
// If there are two pawns potentially being able to capture and at least one
// is not pinned, ep is legal as there are no horizontal exposed checks
if (!more_than_one(blockers_for_king(them) & pawns))
{
updateEpSquare();
break;
}
// If there is no pawn on our king's file, and thus both pawns are pinned
// by bishops, ep is not legal as the king square must be in front of the to square.
// And because the ep square and the king are not on a common diagonal, either ep capture
// would expose the king to a check from one of the bishops
if (!(file_bb(square<KING>(them)) & pawns))
break;
// Otherwise remove the pawn on the king file, as an ep capture by it can never be legal and the
// check below relies on there only being one pawn
pawns &= ~file_bb(square<KING>(them));
}
Square ksq = square<KING>(them);
Square capsq = to;
Bitboard occupied = (pieces() ^ lsb(pawns) ^ capsq) | (to - pawn_push(us));
// If our king is not attacked after making the move, ep is legal.
if (!(attacks_bb<ROOK>(ksq, occupied) & pieces(us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(us, QUEEN, BISHOP)))
updateEpSquare();
break;
}
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero
// if the position was not repeated.
@@ -899,9 +979,14 @@ DirtyPiece Position::do_move(Move m,
}
}
dts.ksq = square<KING>(us);
assert(pos_is_ok());
return dp;
assert(dp.pc != NO_PIECE);
assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE));
assert(dp.from != SQ_NONE);
assert(!(dp.add_sq != SQ_NONE) ^ (m.type_of() == PROMOTION || m.type_of() == CASTLING));
}
@@ -967,12 +1052,172 @@ void Position::undo_move(Move m) {
assert(pos_is_ok());
}
template<bool PutPiece>
inline void add_dirty_threat(
DirtyThreats* const dts, Piece pc, Piece threatened, Square s, Square threatenedSq) {
if (PutPiece)
{
dts->threatenedSqs |= square_bb(threatenedSq);
dts->threateningSqs |= square_bb(s);
}
dts->list.push_back({pc, threatened, s, threatenedSq, PutPiece});
}
#ifdef USE_AVX512ICL
// Given a DirtyThreat template and bit offsets to insert the piece type and square, write the threats
// present at the given bitboard.
template<int SqShift, int PcShift>
void write_multiple_dirties(const Position& p,
Bitboard mask,
DirtyThreat dt_template,
DirtyThreats* dts) {
static_assert(sizeof(DirtyThreat) == 4);
const __m512i board = _mm512_loadu_si512(p.piece_array().data());
const __m512i AllSquares = _mm512_set_epi8(
63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41,
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
const int dt_count = popcount(mask);
assert(dt_count <= 16);
const __m512i template_v = _mm512_set1_epi32(dt_template.raw());
auto* write = dts->list.make_space(dt_count);
// Extract the list of squares and upconvert to 32 bits. There are never more than 16
// incoming threats so this is sufficient.
__m512i threat_squares = _mm512_maskz_compress_epi8(mask, AllSquares);
threat_squares = _mm512_cvtepi8_epi32(_mm512_castsi512_si128(threat_squares));
__m512i threat_pieces =
_mm512_maskz_permutexvar_epi8(0x1111111111111111ULL, threat_squares, board);
// Shift the piece and square into place
threat_squares = _mm512_slli_epi32(threat_squares, SqShift);
threat_pieces = _mm512_slli_epi32(threat_pieces, PcShift);
const __m512i dirties =
_mm512_ternarylogic_epi32(template_v, threat_squares, threat_pieces, 254 /* A | B | C */);
_mm512_storeu_si512(reinterpret_cast<__m512i*>(write), dirties);
}
#endif
template<bool PutPiece, bool ComputeRay>
void Position::update_piece_threats(Piece pc,
Square s,
DirtyThreats* const dts,
[[maybe_unused]] Bitboard noRaysContaining) const {
const Bitboard occupied = pieces();
const Bitboard rookQueens = pieces(ROOK, QUEEN);
const Bitboard bishopQueens = pieces(BISHOP, QUEEN);
const Bitboard knights = pieces(KNIGHT);
const Bitboard kings = pieces(KING);
const Bitboard whitePawns = pieces(WHITE, PAWN);
const Bitboard blackPawns = pieces(BLACK, PAWN);
const Bitboard rAttacks = attacks_bb<ROOK>(s, occupied);
const Bitboard bAttacks = attacks_bb<BISHOP>(s, occupied);
Bitboard threatened = attacks_bb(pc, s, occupied) & occupied;
Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks);
Bitboard incoming_threats =
(PseudoAttacks[KNIGHT][s] & knights) | (attacks_bb<PAWN>(s, WHITE) & blackPawns)
| (attacks_bb<PAWN>(s, BLACK) & whitePawns) | (PseudoAttacks[KING][s] & kings);
#ifdef USE_AVX512ICL
if (threatened)
{
if constexpr (PutPiece)
{
dts->threatenedSqs |= threatened;
dts->threateningSqs |= square_bb(s);
}
DirtyThreat dt_template{pc, NO_PIECE, s, Square(0), PutPiece};
write_multiple_dirties<DirtyThreat::ThreatenedSqOffset, DirtyThreat::ThreatenedPcOffset>(
*this, threatened, dt_template, dts);
}
Bitboard all_attackers = sliders | incoming_threats;
if (!all_attackers)
return; // Square s is threatened iff there's at least one attacker
if constexpr (PutPiece)
{
dts->threatenedSqs |= square_bb(s);
dts->threateningSqs |= all_attackers;
}
DirtyThreat dt_template{NO_PIECE, pc, Square(0), s, PutPiece};
write_multiple_dirties<DirtyThreat::PcSqOffset, DirtyThreat::PcOffset>(*this, all_attackers,
dt_template, dts);
#else
while (threatened)
{
Square threatenedSq = pop_lsb(threatened);
Piece threatenedPc = piece_on(threatenedSq);
assert(threatenedSq != s);
assert(threatenedPc);
add_dirty_threat<PutPiece>(dts, pc, threatenedPc, s, threatenedSq);
}
#endif
if constexpr (ComputeRay)
{
while (sliders)
{
Square sliderSq = pop_lsb(sliders);
Piece slider = piece_on(sliderSq);
const Bitboard ray = RayPassBB[sliderSq][s] & ~BetweenBB[sliderSq][s];
const Bitboard discovered = ray & (rAttacks | bAttacks) & occupied;
assert(!more_than_one(discovered));
if (discovered && (RayPassBB[sliderSq][s] & noRaysContaining) != noRaysContaining)
{
const Square threatenedSq = lsb(discovered);
const Piece threatenedPc = piece_on(threatenedSq);
add_dirty_threat<!PutPiece>(dts, slider, threatenedPc, sliderSq, threatenedSq);
}
#ifndef USE_AVX512ICL // for ICL, direct threats were processed earlier (all_attackers)
add_dirty_threat<PutPiece>(dts, slider, pc, sliderSq, s);
#endif
}
}
else
{
incoming_threats |= sliders;
}
#ifndef USE_AVX512ICL
while (incoming_threats)
{
Square srcSq = pop_lsb(incoming_threats);
Piece srcPc = piece_on(srcSq);
assert(srcSq != s);
assert(srcPc != NO_PIECE);
add_dirty_threat<PutPiece>(dts, srcPc, pc, srcSq, s);
}
#endif
}
// Helper used to do/undo a castling move. This is a bit
// tricky in Chess960 where from/to squares can overlap.
template<bool Do>
void Position::do_castling(
Color us, Square from, Square& to, Square& rfrom, Square& rto, DirtyPiece* const dp) {
void Position::do_castling(Color us,
Square from,
Square& to,
Square& rfrom,
Square& rto,
DirtyThreats* const dts,
DirtyPiece* const dp) {
bool kingSide = to > from;
rfrom = to; // Castling is encoded as "king captures friendly rook"
@@ -983,22 +1228,17 @@ void Position::do_castling(
if (Do)
{
dp->piece[0] = make_piece(us, KING);
dp->from[0] = from;
dp->to[0] = to;
dp->piece[1] = make_piece(us, ROOK);
dp->from[1] = rfrom;
dp->to[1] = rto;
dp->dirty_num = 2;
dp->to = to;
dp->remove_pc = dp->add_pc = make_piece(us, ROOK);
dp->remove_sq = rfrom;
dp->add_sq = rto;
}
// Remove both pieces first since squares could overlap in Chess960
remove_piece(Do ? from : to);
remove_piece(Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] =
NO_PIECE; // remove_piece does not do this for us
put_piece(make_piece(us, KING), Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
remove_piece(Do ? from : to, dts);
remove_piece(Do ? rfrom : rto, dts);
put_piece(make_piece(us, KING), Do ? to : from, dts);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom, dts);
}
@@ -1012,7 +1252,6 @@ void Position::do_null_move(StateInfo& newSt, const TranspositionTable& tt) {
std::memcpy(&newSt, st, sizeof(StateInfo));
newSt.previous = st;
st->next = &newSt;
st = &newSt;
if (st->epSquare != SQ_NONE)
@@ -1059,6 +1298,8 @@ bool Position::see_ge(Move m, int threshold) const {
Square from = m.from_sq(), to = m.to_sq();
assert(piece_on(from) != NO_PIECE);
int swap = PieceValue[piece_on(to)] - threshold;
if (swap < 0)
return false;
@@ -1263,6 +1504,9 @@ void Position::flip() {
}
bool Position::material_key_is_ok() const { return compute_material_key() == st->materialKey; }
// Performs some consistency checks for the position object
// and raise an assert if something wrong is detected.
// This is meant to be helpful when debugging.
@@ -1297,7 +1541,7 @@ bool Position::pos_is_ok() const {
for (Piece pc : Pieces)
if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
|| pieceCount[pc] != std::count(board.begin(), board.end(), pc))
assert(0 && "pos_is_ok: Pieces");
for (Color c : {WHITE, BLACK})
@@ -1312,6 +1556,8 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: Castling");
}
assert(material_key_is_ok() && "pos_is_ok: materialKey");
return true;
}
+98 -58
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,10 +19,12 @@
#ifndef POSITION_H_INCLUDED
#define POSITION_H_INCLUDED
#include <array>
#include <cassert>
#include <deque>
#include <iosfwd>
#include <memory>
#include <new>
#include <string>
#include "bitboard.h"
@@ -31,6 +33,7 @@
namespace Stockfish {
class TranspositionTable;
struct SharedHistories;
// StateInfo struct stores information needed to restore a Position object to
// its previous state when we retract a move. Whenever a move is made on the
@@ -53,7 +56,6 @@ struct StateInfo {
Key key;
Bitboard checkersBB;
StateInfo* previous;
StateInfo* next;
Bitboard blockersForKing[COLOR_NB];
Bitboard pinners[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB];
@@ -68,7 +70,6 @@ struct StateInfo {
// elements are not invalidated upon list resizing.
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;
// Position class stores information regarding the board representation as
// pieces, side to move, hash keys, castling info, etc. Important methods are
// do_move() and undo_move(), used by the search to update node info when
@@ -87,15 +88,16 @@ class Position {
std::string fen() const;
// Position representation
Bitboard pieces(PieceType pt = ALL_PIECES) const;
Bitboard pieces() const; // All pieces
template<typename... PieceTypes>
Bitboard pieces(PieceType pt, PieceTypes... pts) const;
Bitboard pieces(PieceTypes... pts) const;
Bitboard pieces(Color c) const;
template<typename... PieceTypes>
Bitboard pieces(Color c, PieceTypes... pts) const;
Piece piece_on(Square s) const;
Square ep_square() const;
bool empty(Square s) const;
Bitboard pieces(Color c, PieceTypes... pts) const;
Piece piece_on(Square s) const;
const std::array<Piece, SQUARE_NB>& piece_array() const;
Square ep_square() const;
bool empty(Square s) const;
template<PieceType Pt>
int count(Color c) const;
template<PieceType Pt>
@@ -104,10 +106,9 @@ class Position {
Square square(Color c) const;
// Castling
CastlingRights castling_rights(Color c) const;
bool can_castle(CastlingRights cr) const;
bool castling_impeded(CastlingRights cr) const;
Square castling_rook_square(CastlingRights cr) const;
bool can_castle(CastlingRights cr) const;
bool castling_impeded(CastlingRights cr) const;
Square castling_rook_square(CastlingRights cr) const;
// Checking
Bitboard checkers() const;
@@ -133,11 +134,17 @@ class Position {
Piece captured_piece() const;
// Doing and undoing moves
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
DirtyPiece do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
void undo_move(Move m);
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
void undo_null_move();
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
void do_move(Move m,
StateInfo& newSt,
bool givesCheck,
DirtyPiece& dp,
DirtyThreats& dts,
const TranspositionTable* tt,
const SharedHistories* worker);
void undo_move(Move m);
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
void undo_null_move();
// Static Exchange Evaluation
bool see_ge(Move m, int threshold = 0) const;
@@ -163,44 +170,54 @@ class Position {
// Position consistency check, for debugging
bool pos_is_ok() const;
bool material_key_is_ok() const;
void flip();
// Used by NNUE
StateInfo* state() const;
void put_piece(Piece pc, Square s);
void remove_piece(Square s);
void put_piece(Piece pc, Square s, DirtyThreats* const dts = nullptr);
void remove_piece(Square s, DirtyThreats* const dts = nullptr);
void swap_piece(Square s, Piece pc, DirtyThreats* const dts = nullptr);
private:
// Initialization helpers (used while setting up a position)
void set_castling_right(Color c, Square rfrom);
Key compute_material_key() const;
void set_state() const;
void set_check_info() const;
// Other helpers
void move_piece(Square from, Square to);
template<bool PutPiece, bool ComputeRay = true>
void update_piece_threats(Piece pc,
Square s,
DirtyThreats* const dts,
Bitboard noRaysContaining = -1ULL) const;
void move_piece(Square from, Square to, DirtyThreats* const dts = nullptr);
template<bool Do>
void do_castling(Color us,
Square from,
Square& to,
Square& rfrom,
Square& rto,
DirtyPiece* const dp = nullptr);
template<bool AfterMove>
Key adjust_key50(Key k) const;
void do_castling(Color us,
Square from,
Square& to,
Square& rfrom,
Square& rto,
DirtyThreats* const dts = nullptr,
DirtyPiece* const dp = nullptr);
Key adjust_key50(Key k) const;
// Data members
Piece board[SQUARE_NB];
Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[COLOR_NB];
int pieceCount[PIECE_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
StateInfo* st;
int gamePly;
Color sideToMove;
bool chess960;
std::array<Piece, SQUARE_NB> board;
std::array<Bitboard, PIECE_TYPE_NB> byTypeBB;
std::array<Bitboard, COLOR_NB> byColorBB;
int pieceCount[PIECE_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
StateInfo* st;
int gamePly;
Color sideToMove;
bool chess960;
DirtyPiece scratch_dp;
DirtyThreats scratch_dts;
};
std::ostream& operator<<(std::ostream& os, const Position& pos);
@@ -212,15 +229,17 @@ inline Piece Position::piece_on(Square s) const {
return board[s];
}
inline const std::array<Piece, SQUARE_NB>& Position::piece_array() const { return board; }
inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; }
inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); }
inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; }
inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; }
template<typename... PieceTypes>
inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const {
return pieces(pt) | pieces(pts...);
inline Bitboard Position::pieces(PieceTypes... pts) const {
return (byTypeBB[pts] | ...);
}
inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; }
@@ -250,10 +269,6 @@ inline Square Position::ep_square() const { return st->epSquare; }
inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; }
inline CastlingRights Position::castling_rights(Color c) const {
return c & CastlingRights(st->castlingRights);
}
inline bool Position::castling_impeded(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return pieces() & castlingPath[cr];
@@ -290,11 +305,10 @@ inline Bitboard Position::pinners(Color c) const { return st->pinners[c]; }
inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; }
inline Key Position::key() const { return adjust_key50<false>(st->key); }
inline Key Position::key() const { return adjust_key50(st->key); }
template<bool AfterMove>
inline Key Position::adjust_key50(Key k) const {
return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
return st->rule50 < 14 ? k : k ^ make_key((st->rule50 - 14) / 8);
}
inline Key Position::pawn_key() const { return st->pawnKey; }
@@ -332,18 +346,23 @@ inline bool Position::capture_stage(Move m) const {
inline Piece Position::captured_piece() const { return st->capturedPiece; }
inline void Position::put_piece(Piece pc, Square s) {
inline void Position::put_piece(Piece pc, Square s, DirtyThreats* const dts) {
board[s] = pc;
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
byColorBB[color_of(pc)] |= s;
pieceCount[pc]++;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
if (dts)
update_piece_threats<true>(pc, s, dts);
}
inline void Position::remove_piece(Square s) {
inline void Position::remove_piece(Square s, DirtyThreats* const dts) {
Piece pc = board[s];
if (dts)
update_piece_threats<false>(pc, s, dts);
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[type_of(pc)] ^= s;
byColorBB[color_of(pc)] ^= s;
@@ -352,19 +371,40 @@ inline void Position::remove_piece(Square s) {
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
}
inline void Position::move_piece(Square from, Square to) {
inline void Position::move_piece(Square from, Square to, DirtyThreats* const dts) {
Piece pc = board[from];
Bitboard fromTo = from | to;
if (dts)
update_piece_threats<false>(pc, from, dts, fromTo);
byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo;
board[from] = NO_PIECE;
board[to] = pc;
if (dts)
update_piece_threats<true>(pc, to, dts, fromTo);
}
inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) {
Piece old = board[s];
remove_piece(s);
if (dts)
update_piece_threats<false, false>(old, s, dts);
put_piece(pc, s);
if (dts)
update_piece_threats<true, false>(pc, s, dts);
}
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
do_move(m, newSt, gives_check(m), tt);
new (&scratch_dts) DirtyThreats;
do_move(m, newSt, gives_check(m), scratch_dp, scratch_dts, tt, nullptr);
}
inline StateInfo* Position::state() const { return st; }
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+708 -589
View File
File diff suppressed because it is too large Load Diff
+70 -30
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,11 +26,14 @@
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <vector>
#include "cluster.h"
#include "history.h"
#include "misc.h"
#include "nnue/network.h"
@@ -75,7 +78,6 @@ struct Stack {
bool ttHit;
int cutoffCnt;
int reduction;
bool isTTMove;
};
@@ -121,7 +123,9 @@ struct LimitsType {
ponderMode = false;
}
bool use_time_management() const { return time[WHITE] || time[BLACK]; }
bool use_time_management() const {
return Distributed::is_root() && (time[WHITE] || time[BLACK]);
}
std::vector<std::string> searchmoves;
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
@@ -134,19 +138,22 @@ struct LimitsType {
// The UCI stores the uci options, thread pool, and transposition table.
// This struct is used to easily forward data to the Search::Worker class.
struct SharedState {
SharedState(const OptionsMap& optionsMap,
ThreadPool& threadPool,
TranspositionTable& transpositionTable,
const LazyNumaReplicated<Eval::NNUE::Networks>& nets) :
SharedState(const OptionsMap& optionsMap,
ThreadPool& threadPool,
TranspositionTable& transpositionTable,
std::map<NumaIndex, SharedHistories>& sharedHists,
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& nets) :
options(optionsMap),
threads(threadPool),
tt(transpositionTable),
sharedHistories(sharedHists),
networks(nets) {}
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
const LazyNumaReplicated<Eval::NNUE::Networks>& networks;
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
std::map<NumaIndex, SharedHistories>& sharedHistories;
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
};
class Worker;
@@ -175,6 +182,14 @@ struct InfoFull: InfoShort {
size_t tbHits;
std::string_view pv;
int hashfull;
std::vector<char> serialize() const;
// NOTE: The returned InfoFull will contain string_views that point
// into the given buffer, so it must live (and not be reallocated)
// (and not be reallocated) for at least as long as you want to
// access fields in the InfoFull.
static InfoFull unserialize(const std::vector<char>& buf);
};
struct InfoIteration {
@@ -228,7 +243,7 @@ class SearchManager: public ISearchManager {
};
SearchManager(const UpdateContext& updateContext) :
SearchManager(UpdateContext& updateContext) :
updates(updateContext) {}
void check_time(Search::Worker& worker) override;
@@ -251,7 +266,7 @@ class SearchManager: public ISearchManager {
size_t id;
const UpdateContext& updates;
UpdateContext& updates;
};
class NullSearchManager: public ISearchManager {
@@ -259,13 +274,17 @@ class NullSearchManager: public ISearchManager {
void check_time(Search::Worker&) override {}
};
// Search::Worker is the class that does the actual search.
// It is instantiated once per thread, and it is responsible for keeping track
// of the search history, and storing data required for the search.
class Worker {
public:
Worker(SharedState&, std::unique_ptr<ISearchManager>, size_t, NumaReplicatedAccessToken);
Worker(SharedState&,
std::unique_ptr<ISearchManager>,
size_t,
size_t,
size_t,
NumaReplicatedAccessToken);
// Called at instantiation to initialize reductions tables.
// Reset histories, usually before a new game.
@@ -283,21 +302,42 @@ class Worker {
ButterflyHistory mainHistory;
LowPlyHistory lowPlyHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
PawnHistory pawnHistory;
CorrectionHistory<Pawn> pawnCorrectionHistory;
CorrectionHistory<Minor> minorPieceCorrectionHistory;
CorrectionHistory<NonPawn> nonPawnCorrectionHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
CorrectionHistory<Continuation> continuationCorrectionHistory;
#ifdef USE_MPI
struct {
std::mutex mutex;
Distributed::TTCache<Distributed::TTCacheSize> buffer = {};
} ttCache;
#endif
std::atomic<uint64_t> TTsaves;
friend void Distributed::save(TranspositionTable&,
ThreadPool&,
Search::Worker*,
TTWriter ttWriter,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8);
TTMoveHistory ttMoveHistory;
SharedHistories& sharedHistory;
private:
void iterative_deepening();
void do_move(Position& pos, const Move move, StateInfo& st);
void do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck);
void do_null_move(Position& pos, StateInfo& st);
void do_move(Position& pos, const Move move, StateInfo& st, Stack* const ss);
void
do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss);
void do_null_move(Position& pos, StateInfo& st, Stack* const ss);
void undo_move(Position& pos, const Move move);
void undo_null_move(Position& pos);
@@ -336,7 +376,7 @@ class Worker {
Depth rootDepth, completedDepth;
Value rootDelta;
size_t threadIdx;
size_t threadIdx, numaThreadIdx, numaTotal;
NumaReplicatedAccessToken numaAccessToken;
// Reductions lookup table initialized at startup
@@ -347,10 +387,10 @@ class Worker {
Tablebases::Config tbConfig;
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
const LazyNumaReplicated<Eval::NNUE::Networks>& networks;
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
// Used by NNUE
Eval::NNUE::AccumulatorStack accumulatorStack;
+635
View File
@@ -0,0 +1,635 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHM_H_INCLUDED
#define SHM_H_INCLUDED
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <memory>
#include <new>
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#if !defined(_WIN32) && !defined(__ANDROID__)
#include "shm_linux.h"
#endif
#if defined(__ANDROID__)
#include <limits.h>
#define SF_MAX_SEM_NAME_LEN NAME_MAX
#endif
#include "types.h"
#include "memory.h"
#if defined(_WIN32)
#if _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
#endif
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <cstring>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#include <sys/syslimits.h>
#elif defined(__sun)
#include <stdlib.h>
#elif defined(__FreeBSD__)
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#elif defined(__NetBSD__) || defined(__DragonFly__) || defined(__linux__)
#include <limits.h>
#include <unistd.h>
#endif
namespace Stockfish {
// argv[0] CANNOT be used because we need to identify the executable.
// argv[0] contains the command used to invoke it, which does not involve the full path.
// Just using a path is not fully resilient either, as the executable could
// have changed if it wasn't locked by the OS. Ideally we would hash the executable
// but it's not really that important at this point.
// If the path is longer than 4095 bytes the hash will be computed from an unspecified
// amount of bytes of the path; in particular it can a hash of an empty string.
inline std::string getExecutablePathHash() {
char executable_path[4096] = {0};
std::size_t path_length = 0;
#if defined(_WIN32)
path_length = GetModuleFileNameA(NULL, executable_path, sizeof(executable_path));
#elif defined(__APPLE__)
uint32_t size = sizeof(executable_path);
if (_NSGetExecutablePath(executable_path, &size) == 0)
{
path_length = std::strlen(executable_path);
}
#elif defined(__sun) // Solaris
const char* path = getexecname();
if (path)
{
std::strncpy(executable_path, path, sizeof(executable_path) - 1);
path_length = std::strlen(executable_path);
}
#elif defined(__FreeBSD__)
size_t size = sizeof(executable_path);
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
if (sysctl(mib, 4, executable_path, &size, NULL, 0) == 0)
{
path_length = std::strlen(executable_path);
}
#elif defined(__NetBSD__) || defined(__DragonFly__)
ssize_t len = readlink("/proc/curproc/exe", executable_path, sizeof(executable_path) - 1);
if (len >= 0)
{
executable_path[len] = '\0';
path_length = len;
}
#elif defined(__linux__)
ssize_t len = readlink("/proc/self/exe", executable_path, sizeof(executable_path) - 1);
if (len >= 0)
{
executable_path[len] = '\0';
path_length = len;
}
#endif
// In case of any error the path will be empty.
return std::string(executable_path, path_length);
}
enum class SystemWideSharedConstantAllocationStatus {
NoAllocation,
LocalMemory,
SharedMemory
};
#if defined(_WIN32)
inline std::string GetLastErrorAsString(DWORD error) {
//Get the error message ID, if any.
DWORD errorMessageID = error;
if (errorMessageID == 0)
{
return std::string(); //No error message has been recorded
}
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return message;
}
// Utilizes shared memory to store the value. It is deduplicated system-wide (for the single user).
template<typename T>
class SharedMemoryBackend {
public:
enum class Status {
Success,
LargePageAllocationError,
FileMappingError,
MapViewError,
MutexCreateError,
MutexWaitError,
MutexReleaseError,
NotInitialized
};
static constexpr DWORD IS_INITIALIZED_VALUE = 1;
SharedMemoryBackend() :
status(Status::NotInitialized) {};
SharedMemoryBackend(const std::string& shm_name, const T& value) :
status(Status::NotInitialized) {
initialize(shm_name, value);
}
bool is_valid() const { return status == Status::Success; }
std::optional<std::string> get_error_message() const {
switch (status)
{
case Status::Success :
return std::nullopt;
case Status::LargePageAllocationError :
return "Failed to allocate large page memory";
case Status::FileMappingError :
return "Failed to create file mapping: " + last_error_message;
case Status::MapViewError :
return "Failed to map view: " + last_error_message;
case Status::MutexCreateError :
return "Failed to create mutex: " + last_error_message;
case Status::MutexWaitError :
return "Failed to wait on mutex: " + last_error_message;
case Status::MutexReleaseError :
return "Failed to release mutex: " + last_error_message;
case Status::NotInitialized :
return "Not initialized";
default :
return "Unknown error";
}
}
void* get() const { return is_valid() ? pMap : nullptr; }
~SharedMemoryBackend() { cleanup(); }
SharedMemoryBackend(const SharedMemoryBackend&) = delete;
SharedMemoryBackend& operator=(const SharedMemoryBackend&) = delete;
SharedMemoryBackend(SharedMemoryBackend&& other) noexcept :
pMap(other.pMap),
hMapFile(other.hMapFile),
status(other.status),
last_error_message(std::move(other.last_error_message)) {
other.pMap = nullptr;
other.hMapFile = 0;
other.status = Status::NotInitialized;
}
SharedMemoryBackend& operator=(SharedMemoryBackend&& other) noexcept {
if (this != &other)
{
cleanup();
pMap = other.pMap;
hMapFile = other.hMapFile;
status = other.status;
last_error_message = std::move(other.last_error_message);
other.pMap = nullptr;
other.hMapFile = 0;
other.status = Status::NotInitialized;
}
return *this;
}
SystemWideSharedConstantAllocationStatus get_status() const {
return status == Status::Success ? SystemWideSharedConstantAllocationStatus::SharedMemory
: SystemWideSharedConstantAllocationStatus::NoAllocation;
}
private:
void initialize(const std::string& shm_name, const T& value) {
const size_t total_size = sizeof(T) + sizeof(IS_INITIALIZED_VALUE);
// Try allocating with large pages first.
hMapFile = windows_try_with_large_page_priviliges(
[&](size_t largePageSize) {
const size_t total_size_aligned =
(total_size + largePageSize - 1) / largePageSize * largePageSize;
#if defined(_WIN64)
DWORD total_size_low = total_size_aligned & 0xFFFFFFFFu;
DWORD total_size_high = total_size_aligned >> 32u;
#else
DWORD total_size_low = total_size_aligned;
DWORD total_size_high = 0;
#endif
return CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE | SEC_COMMIT | SEC_LARGE_PAGES,
total_size_high, total_size_low, shm_name.c_str());
},
[]() { return (void*) nullptr; });
// Fallback to normal allocation if no large pages available.
if (!hMapFile)
{
hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
static_cast<DWORD>(total_size), shm_name.c_str());
}
if (!hMapFile)
{
const DWORD err = GetLastError();
last_error_message = GetLastErrorAsString(err);
status = Status::FileMappingError;
return;
}
pMap = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, total_size);
if (!pMap)
{
const DWORD err = GetLastError();
last_error_message = GetLastErrorAsString(err);
status = Status::MapViewError;
cleanup_partial();
return;
}
// Use named mutex to ensure only one initializer
std::string mutex_name = shm_name + "$mutex";
HANDLE hMutex = CreateMutexA(NULL, FALSE, mutex_name.c_str());
if (!hMutex)
{
const DWORD err = GetLastError();
last_error_message = GetLastErrorAsString(err);
status = Status::MutexCreateError;
cleanup_partial();
return;
}
DWORD wait_result = WaitForSingleObject(hMutex, INFINITE);
if (wait_result != WAIT_OBJECT_0)
{
const DWORD err = GetLastError();
last_error_message = GetLastErrorAsString(err);
status = Status::MutexWaitError;
CloseHandle(hMutex);
cleanup_partial();
return;
}
// Crucially, we place the object first to ensure alignment.
volatile DWORD* is_initialized =
std::launder(reinterpret_cast<DWORD*>(reinterpret_cast<char*>(pMap) + sizeof(T)));
T* object = std::launder(reinterpret_cast<T*>(pMap));
if (*is_initialized != IS_INITIALIZED_VALUE)
{
// First time initialization, message for debug purposes
new (object) T{value};
*is_initialized = IS_INITIALIZED_VALUE;
}
BOOL release_result = ReleaseMutex(hMutex);
CloseHandle(hMutex);
if (!release_result)
{
const DWORD err = GetLastError();
last_error_message = GetLastErrorAsString(err);
status = Status::MutexReleaseError;
cleanup_partial();
return;
}
status = Status::Success;
}
void cleanup_partial() {
if (pMap != nullptr)
{
UnmapViewOfFile(pMap);
pMap = nullptr;
}
if (hMapFile)
{
CloseHandle(hMapFile);
hMapFile = 0;
}
}
void cleanup() {
if (pMap != nullptr)
{
UnmapViewOfFile(pMap);
pMap = nullptr;
}
if (hMapFile)
{
CloseHandle(hMapFile);
hMapFile = 0;
}
}
void* pMap = nullptr;
HANDLE hMapFile = 0;
Status status = Status::NotInitialized;
std::string last_error_message;
};
#elif !defined(__ANDROID__)
template<typename T>
class SharedMemoryBackend {
public:
SharedMemoryBackend() = default;
SharedMemoryBackend(const std::string& shm_name, const T& value) :
shm1(shm::create_shared<T>(shm_name, value)) {}
void* get() const {
const T* ptr = &shm1->get();
return reinterpret_cast<void*>(const_cast<T*>(ptr));
}
bool is_valid() const { return shm1 && shm1->is_open() && shm1->is_initialized(); }
SystemWideSharedConstantAllocationStatus get_status() const {
return is_valid() ? SystemWideSharedConstantAllocationStatus::SharedMemory
: SystemWideSharedConstantAllocationStatus::NoAllocation;
}
std::optional<std::string> get_error_message() const {
if (!shm1)
return "Shared memory not initialized";
if (!shm1->is_open())
return "Shared memory is not open";
if (!shm1->is_initialized())
return "Not initialized";
return std::nullopt;
}
private:
std::optional<shm::SharedMemory<T>> shm1;
};
#else
// For systems that don't have shared memory, or support is troublesome.
// The way fallback is done is that we need a dummy backend.
template<typename T>
class SharedMemoryBackend {
public:
SharedMemoryBackend() = default;
SharedMemoryBackend([[maybe_unused]] const std::string& shm_name,
[[maybe_unused]] const T& value) {}
void* get() const { return nullptr; }
bool is_valid() const { return false; }
SystemWideSharedConstantAllocationStatus get_status() const {
return SystemWideSharedConstantAllocationStatus::NoAllocation;
}
std::optional<std::string> get_error_message() const { return "Dummy SharedMemoryBackend"; }
};
#endif
template<typename T>
struct SharedMemoryBackendFallback {
SharedMemoryBackendFallback() = default;
SharedMemoryBackendFallback(const std::string&, const T& value) :
fallback_object(make_unique_large_page<T>(value)) {}
void* get() const { return fallback_object.get(); }
SharedMemoryBackendFallback(const SharedMemoryBackendFallback&) = delete;
SharedMemoryBackendFallback& operator=(const SharedMemoryBackendFallback&) = delete;
SharedMemoryBackendFallback(SharedMemoryBackendFallback&& other) noexcept :
fallback_object(std::move(other.fallback_object)) {}
SharedMemoryBackendFallback& operator=(SharedMemoryBackendFallback&& other) noexcept {
fallback_object = std::move(other.fallback_object);
return *this;
}
SystemWideSharedConstantAllocationStatus get_status() const {
return fallback_object == nullptr ? SystemWideSharedConstantAllocationStatus::NoAllocation
: SystemWideSharedConstantAllocationStatus::LocalMemory;
}
std::optional<std::string> get_error_message() const {
if (fallback_object == nullptr)
return "Not initialized";
return "Shared memory not supported by the OS. Local allocation fallback.";
}
private:
LargePagePtr<T> fallback_object;
};
// Platform-independent wrapper
template<typename T>
struct SystemWideSharedConstant {
private:
static std::string createHashString(const std::string& input) {
size_t hash = std::hash<std::string>{}(input);
std::stringstream ss;
ss << std::hex << std::setfill('0') << hash;
return ss.str();
}
public:
// We can't run the destructor because it may be in a completely different process.
// The object stored must also be obviously in-line but we can't check for that, other than some basic checks that cover most cases.
static_assert(std::is_trivially_destructible_v<T>);
static_assert(std::is_trivially_move_constructible_v<T>);
static_assert(std::is_trivially_copy_constructible_v<T>);
SystemWideSharedConstant() = default;
// Content is addressed by its hash. An additional discriminator can be added to account for differences
// that are not present in the content, for example NUMA node allocation.
SystemWideSharedConstant(const T& value, std::size_t discriminator = 0) {
std::size_t content_hash = std::hash<T>{}(value);
std::size_t executable_hash = std::hash<std::string>{}(getExecutablePathHash());
std::string shm_name = std::string("Local\\sf_") + std::to_string(content_hash) + "$"
+ std::to_string(executable_hash) + "$"
+ std::to_string(discriminator);
#if !defined(_WIN32)
// POSIX shared memory names must start with a slash
shm_name = "/sf_" + createHashString(shm_name);
// hash name and make sure it is not longer than SF_MAX_SEM_NAME_LEN
if (shm_name.size() > SF_MAX_SEM_NAME_LEN)
{
shm_name = shm_name.substr(0, SF_MAX_SEM_NAME_LEN - 1);
}
#endif
SharedMemoryBackend<T> shm_backend(shm_name, value);
if (shm_backend.is_valid())
{
backend = std::move(shm_backend);
}
else
{
backend = SharedMemoryBackendFallback<T>(shm_name, value);
}
}
SystemWideSharedConstant(const SystemWideSharedConstant&) = delete;
SystemWideSharedConstant& operator=(const SystemWideSharedConstant&) = delete;
SystemWideSharedConstant(SystemWideSharedConstant&& other) noexcept :
backend(std::move(other.backend)) {}
SystemWideSharedConstant& operator=(SystemWideSharedConstant&& other) noexcept {
backend = std::move(other.backend);
return *this;
}
const T& operator*() const { return *std::launder(reinterpret_cast<const T*>(get_ptr())); }
bool operator==(std::nullptr_t) const noexcept { return get_ptr() == nullptr; }
bool operator!=(std::nullptr_t) const noexcept { return get_ptr() != nullptr; }
SystemWideSharedConstantAllocationStatus get_status() const {
return std::visit(
[](const auto& end) -> SystemWideSharedConstantAllocationStatus {
if constexpr (std::is_same_v<std::decay_t<decltype(end)>, std::monostate>)
{
return SystemWideSharedConstantAllocationStatus::NoAllocation;
}
else
{
return end.get_status();
}
},
backend);
}
std::optional<std::string> get_error_message() const {
return std::visit(
[](const auto& end) -> std::optional<std::string> {
if constexpr (std::is_same_v<std::decay_t<decltype(end)>, std::monostate>)
{
return std::nullopt;
}
else
{
return end.get_error_message();
}
},
backend);
}
private:
auto get_ptr() const {
return std::visit(
[](const auto& end) -> void* {
if constexpr (std::is_same_v<std::decay_t<decltype(end)>, std::monostate>)
{
return nullptr;
}
else
{
return end.get();
}
},
backend);
}
std::variant<std::monostate, SharedMemoryBackend<T>, SharedMemoryBackendFallback<T>> backend;
};
} // namespace Stockfish
#endif // #ifndef SHM_H_INCLUDED
+658
View File
@@ -0,0 +1,658 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHM_LINUX_H_INCLUDED
#define SHM_LINUX_H_INCLUDED
#include <atomic>
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <dirent.h>
#include <mutex>
#include <new>
#include <optional>
#include <pthread.h>
#include <string>
#include <inttypes.h>
#include <type_traits>
#include <unordered_set>
#include <fcntl.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__linux__)
#include <limits.h>
#define SF_MAX_SEM_NAME_LEN NAME_MAX
#elif defined(__APPLE__)
#define SF_MAX_SEM_NAME_LEN 31
#else
#define SF_MAX_SEM_NAME_LEN 255
#endif
namespace Stockfish::shm {
namespace detail {
struct ShmHeader {
static constexpr uint32_t SHM_MAGIC = 0xAD5F1A12;
pthread_mutex_t mutex;
std::atomic<uint32_t> ref_count{0};
std::atomic<bool> initialized{false};
uint32_t magic = SHM_MAGIC;
};
class SharedMemoryBase {
public:
virtual ~SharedMemoryBase() = default;
virtual void close() noexcept = 0;
virtual const std::string& name() const noexcept = 0;
};
class SharedMemoryRegistry {
private:
static std::mutex registry_mutex_;
static std::unordered_set<SharedMemoryBase*> active_instances_;
public:
static void register_instance(SharedMemoryBase* instance) {
std::scoped_lock lock(registry_mutex_);
active_instances_.insert(instance);
}
static void unregister_instance(SharedMemoryBase* instance) {
std::scoped_lock lock(registry_mutex_);
active_instances_.erase(instance);
}
static void cleanup_all() noexcept {
std::scoped_lock lock(registry_mutex_);
for (auto* instance : active_instances_)
instance->close();
active_instances_.clear();
}
};
inline std::mutex SharedMemoryRegistry::registry_mutex_;
inline std::unordered_set<SharedMemoryBase*> SharedMemoryRegistry::active_instances_;
class CleanupHooks {
private:
static std::once_flag register_once_;
static void handle_signal(int sig) noexcept {
SharedMemoryRegistry::cleanup_all();
_Exit(128 + sig);
}
static void register_signal_handlers() noexcept {
std::atexit([]() { SharedMemoryRegistry::cleanup_all(); });
constexpr int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE,
SIGSEGV, SIGTERM, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ};
struct sigaction sa;
sa.sa_handler = handle_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
for (int sig : signals)
sigaction(sig, &sa, nullptr);
}
public:
static void ensure_registered() noexcept {
std::call_once(register_once_, register_signal_handlers);
}
};
inline std::once_flag CleanupHooks::register_once_;
inline int portable_fallocate(int fd, off_t offset, off_t length) {
#ifdef __APPLE__
fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, offset, length, 0};
int ret = fcntl(fd, F_PREALLOCATE, &store);
if (ret == -1)
{
store.fst_flags = F_ALLOCATEALL;
ret = fcntl(fd, F_PREALLOCATE, &store);
}
if (ret != -1)
ret = ftruncate(fd, offset + length);
return ret;
#else
return posix_fallocate(fd, offset, length);
#endif
}
} // namespace detail
template<typename T>
class SharedMemory: public detail::SharedMemoryBase {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
static_assert(!std::is_pointer_v<T>, "T cannot be a pointer type");
private:
std::string name_;
int fd_ = -1;
void* mapped_ptr_ = nullptr;
T* data_ptr_ = nullptr;
detail::ShmHeader* header_ptr_ = nullptr;
size_t total_size_ = 0;
std::string sentinel_base_;
std::string sentinel_path_;
static constexpr size_t calculate_total_size() noexcept {
return sizeof(T) + sizeof(detail::ShmHeader);
}
static std::string make_sentinel_base(const std::string& name) {
uint64_t hash = std::hash<std::string>{}(name);
char buf[32];
std::snprintf(buf, sizeof(buf), "sfshm_%016" PRIx64, static_cast<uint64_t>(hash));
return buf;
}
public:
explicit SharedMemory(const std::string& name) noexcept :
name_(name),
total_size_(calculate_total_size()),
sentinel_base_(make_sentinel_base(name)) {}
~SharedMemory() noexcept override {
detail::SharedMemoryRegistry::unregister_instance(this);
close();
}
SharedMemory(const SharedMemory&) = delete;
SharedMemory& operator=(const SharedMemory&) = delete;
SharedMemory(SharedMemory&& other) noexcept :
name_(std::move(other.name_)),
fd_(other.fd_),
mapped_ptr_(other.mapped_ptr_),
data_ptr_(other.data_ptr_),
header_ptr_(other.header_ptr_),
total_size_(other.total_size_),
sentinel_base_(std::move(other.sentinel_base_)),
sentinel_path_(std::move(other.sentinel_path_)) {
detail::SharedMemoryRegistry::unregister_instance(&other);
detail::SharedMemoryRegistry::register_instance(this);
other.reset();
}
SharedMemory& operator=(SharedMemory&& other) noexcept {
if (this != &other)
{
detail::SharedMemoryRegistry::unregister_instance(this);
close();
name_ = std::move(other.name_);
fd_ = other.fd_;
mapped_ptr_ = other.mapped_ptr_;
data_ptr_ = other.data_ptr_;
header_ptr_ = other.header_ptr_;
total_size_ = other.total_size_;
sentinel_base_ = std::move(other.sentinel_base_);
sentinel_path_ = std::move(other.sentinel_path_);
detail::SharedMemoryRegistry::unregister_instance(&other);
detail::SharedMemoryRegistry::register_instance(this);
other.reset();
}
return *this;
}
[[nodiscard]] bool open(const T& initial_value) noexcept {
detail::CleanupHooks::ensure_registered();
bool retried_stale = false;
while (true)
{
if (is_open())
return false;
bool created_new = false;
fd_ = shm_open(name_.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666);
if (fd_ == -1)
{
fd_ = shm_open(name_.c_str(), O_RDWR, 0666);
if (fd_ == -1)
return false;
}
else
created_new = true;
if (!lock_file(LOCK_EX))
{
::close(fd_);
reset();
return false;
}
bool invalid_header = false;
bool success =
created_new ? setup_new_region(initial_value) : setup_existing_region(invalid_header);
if (!success)
{
if (created_new || invalid_header)
shm_unlink(name_.c_str());
if (mapped_ptr_)
unmap_region();
unlock_file();
::close(fd_);
reset();
if (!created_new && invalid_header && !retried_stale)
{
retried_stale = true;
continue;
}
return false;
}
if (!lock_shared_mutex())
{
if (created_new)
shm_unlink(name_.c_str());
if (mapped_ptr_)
unmap_region();
unlock_file();
::close(fd_);
reset();
if (!created_new && !retried_stale)
{
retried_stale = true;
continue;
}
return false;
}
if (!create_sentinel_file_locked())
{
unlock_shared_mutex();
unmap_region();
if (created_new)
shm_unlink(name_.c_str());
unlock_file();
::close(fd_);
reset();
return false;
}
header_ptr_->ref_count.fetch_add(1, std::memory_order_acq_rel);
unlock_shared_mutex();
unlock_file();
detail::SharedMemoryRegistry::register_instance(this);
return true;
}
}
void close() noexcept override {
if (fd_ == -1 && mapped_ptr_ == nullptr)
return;
bool remove_region = false;
bool file_locked = lock_file(LOCK_EX);
bool mutex_locked = false;
if (file_locked && header_ptr_ != nullptr)
mutex_locked = lock_shared_mutex();
if (mutex_locked)
{
if (header_ptr_)
{
header_ptr_->ref_count.fetch_sub(1, std::memory_order_acq_rel);
}
remove_sentinel_file();
remove_region = !has_other_live_sentinels_locked();
unlock_shared_mutex();
}
else
{
remove_sentinel_file();
decrement_refcount_relaxed();
}
unmap_region();
if (remove_region)
shm_unlink(name_.c_str());
if (file_locked)
unlock_file();
if (fd_ != -1)
{
::close(fd_);
fd_ = -1;
}
reset();
}
const std::string& name() const noexcept override { return name_; }
[[nodiscard]] bool is_open() const noexcept { return fd_ != -1 && mapped_ptr_ && data_ptr_; }
[[nodiscard]] const T& get() const noexcept { return *data_ptr_; }
[[nodiscard]] const T* operator->() const noexcept { return data_ptr_; }
[[nodiscard]] const T& operator*() const noexcept { return *data_ptr_; }
[[nodiscard]] uint32_t ref_count() const noexcept {
return header_ptr_ ? header_ptr_->ref_count.load(std::memory_order_acquire) : 0;
}
[[nodiscard]] bool is_initialized() const noexcept {
return header_ptr_ ? header_ptr_->initialized.load(std::memory_order_acquire) : false;
}
static void cleanup_all_instances() noexcept { detail::SharedMemoryRegistry::cleanup_all(); }
private:
void reset() noexcept {
fd_ = -1;
mapped_ptr_ = nullptr;
data_ptr_ = nullptr;
header_ptr_ = nullptr;
sentinel_path_.clear();
}
void unmap_region() noexcept {
if (mapped_ptr_)
{
munmap(mapped_ptr_, total_size_);
mapped_ptr_ = nullptr;
data_ptr_ = nullptr;
header_ptr_ = nullptr;
}
}
[[nodiscard]] bool lock_file(int operation) noexcept {
if (fd_ == -1)
return false;
while (flock(fd_, operation) == -1)
{
if (errno == EINTR)
continue;
return false;
}
return true;
}
void unlock_file() noexcept {
if (fd_ == -1)
return;
while (flock(fd_, LOCK_UN) == -1)
{
if (errno == EINTR)
continue;
break;
}
}
std::string sentinel_full_path(pid_t pid) const {
std::string path = "/dev/shm/";
path += sentinel_base_;
path.push_back('.');
path += std::to_string(pid);
return path;
}
void decrement_refcount_relaxed() noexcept {
if (!header_ptr_)
return;
uint32_t expected = header_ptr_->ref_count.load(std::memory_order_relaxed);
while (expected != 0
&& !header_ptr_->ref_count.compare_exchange_weak(
expected, expected - 1, std::memory_order_acq_rel, std::memory_order_relaxed))
{}
}
bool create_sentinel_file_locked() noexcept {
if (!header_ptr_)
return false;
const pid_t self_pid = getpid();
sentinel_path_ = sentinel_full_path(self_pid);
for (int attempt = 0; attempt < 2; ++attempt)
{
int fd = ::open(sentinel_path_.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600);
if (fd != -1)
{
::close(fd);
return true;
}
if (errno == EEXIST)
{
::unlink(sentinel_path_.c_str());
decrement_refcount_relaxed();
continue;
}
break;
}
sentinel_path_.clear();
return false;
}
void remove_sentinel_file() noexcept {
if (!sentinel_path_.empty())
{
::unlink(sentinel_path_.c_str());
sentinel_path_.clear();
}
}
static bool pid_is_alive(pid_t pid) noexcept {
if (pid <= 0)
return false;
if (kill(pid, 0) == 0)
return true;
return errno == EPERM;
}
[[nodiscard]] bool initialize_shared_mutex() noexcept {
if (!header_ptr_)
return false;
pthread_mutexattr_t attr;
if (pthread_mutexattr_init(&attr) != 0)
return false;
bool success = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) == 0;
#if _POSIX_C_SOURCE >= 200809L
if (success)
success = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) == 0;
#endif
if (success)
success = pthread_mutex_init(&header_ptr_->mutex, &attr) == 0;
pthread_mutexattr_destroy(&attr);
return success;
}
[[nodiscard]] bool lock_shared_mutex() noexcept {
if (!header_ptr_)
return false;
while (true)
{
int rc = pthread_mutex_lock(&header_ptr_->mutex);
if (rc == 0)
return true;
#if _POSIX_C_SOURCE >= 200809L
if (rc == EOWNERDEAD)
{
if (pthread_mutex_consistent(&header_ptr_->mutex) == 0)
return true;
return false;
}
#endif
if (rc == EINTR)
continue;
return false;
}
}
void unlock_shared_mutex() noexcept {
if (header_ptr_)
pthread_mutex_unlock(&header_ptr_->mutex);
}
bool has_other_live_sentinels_locked() const noexcept {
DIR* dir = opendir("/dev/shm");
if (!dir)
return false;
std::string prefix = sentinel_base_ + ".";
bool found = false;
while (dirent* entry = readdir(dir))
{
std::string name = entry->d_name;
if (name.rfind(prefix, 0) != 0)
continue;
auto pid_str = name.substr(prefix.size());
char* end = nullptr;
long value = std::strtol(pid_str.c_str(), &end, 10);
if (!end || *end != '\0')
continue;
pid_t pid = static_cast<pid_t>(value);
if (pid_is_alive(pid))
{
found = true;
break;
}
std::string stale_path = std::string("/dev/shm/") + name;
::unlink(stale_path.c_str());
const_cast<SharedMemory*>(this)->decrement_refcount_relaxed();
}
closedir(dir);
return found;
}
[[nodiscard]] bool setup_new_region(const T& initial_value) noexcept {
if (ftruncate(fd_, static_cast<off_t>(total_size_)) == -1)
return false;
if (detail::portable_fallocate(fd_, 0, static_cast<off_t>(total_size_)) != 0)
return false;
mapped_ptr_ = mmap(nullptr, total_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
if (mapped_ptr_ == MAP_FAILED)
{
mapped_ptr_ = nullptr;
return false;
}
data_ptr_ = static_cast<T*>(mapped_ptr_);
header_ptr_ =
reinterpret_cast<detail::ShmHeader*>(static_cast<char*>(mapped_ptr_) + sizeof(T));
new (header_ptr_) detail::ShmHeader{};
new (data_ptr_) T{initial_value};
if (!initialize_shared_mutex())
return false;
header_ptr_->ref_count.store(0, std::memory_order_release);
header_ptr_->initialized.store(true, std::memory_order_release);
return true;
}
[[nodiscard]] bool setup_existing_region(bool& invalid_header) noexcept {
invalid_header = false;
struct stat st;
fstat(fd_, &st);
if (static_cast<size_t>(st.st_size) < total_size_)
{
invalid_header = true;
return false;
}
mapped_ptr_ = mmap(nullptr, total_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
if (mapped_ptr_ == MAP_FAILED)
{
mapped_ptr_ = nullptr;
return false;
}
data_ptr_ = static_cast<T*>(mapped_ptr_);
header_ptr_ = std::launder(
reinterpret_cast<detail::ShmHeader*>(static_cast<char*>(mapped_ptr_) + sizeof(T)));
if (!header_ptr_->initialized.load(std::memory_order_acquire)
|| header_ptr_->magic != detail::ShmHeader::SHM_MAGIC)
{
invalid_header = true;
unmap_region();
return false;
}
return true;
}
};
template<typename T>
[[nodiscard]] std::optional<SharedMemory<T>> create_shared(const std::string& name,
const T& initial_value) noexcept {
SharedMemory<T> shm(name);
if (shm.open(initial_value))
return shm;
return std::nullopt;
}
} // namespace Stockfish::shm
#endif // #ifndef SHM_LINUX_H_INCLUDED
+25 -17
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,8 +35,10 @@
#include <type_traits>
#include <utility>
#include <vector>
#include <array>
#include "../bitboard.h"
#include "../cluster.h"
#include "../misc.h"
#include "../movegen.h"
#include "../position.h"
@@ -493,8 +495,9 @@ class TBTables {
}
void info() const {
sync_cout << "info string Found " << foundWDLFiles << " WDL and " << foundDTZFiles
<< " DTZ tablebase files (up to " << MaxCardinality << "-man)." << sync_endl;
if (Distributed::is_root())
sync_cout << "info string Found " << foundWDLFiles << " WDL and " << foundDTZFiles
<< " DTZ tablebase files (up to " << MaxCardinality << "-man)." << sync_endl;
}
void add(const std::vector<PieceType>& pieces);
@@ -584,7 +587,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
// idx = k * d->span + idx % d->span (2)
//
// So from (1) and (2) we can compute idx - I(K):
int diff = idx % d->span - d->span / 2;
int diff = int(idx % d->span - d->span / 2);
// Sum the above to offset to find the offset corresponding to our idx
offset += diff;
@@ -1092,7 +1095,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
// See https://web.archive.org/web/20201106232444/http://www.larsson.dogma.net/dcc99.pdf
std::vector<bool> visited(d->symlen.size());
for (std::size_t sym = 0; sym < d->symlen.size(); ++sym)
for (Sym sym = 0; sym < d->symlen.size(); ++sym)
if (!visited[sym])
d->symlen[sym] = set_symlen(d, sym, visited);
@@ -1214,6 +1217,8 @@ template<TBType Type>
void* mapped(TBTable<Type>& e, const Position& pos) {
static std::mutex mutex;
// Because TB is the only usage of materialKey, check it here in debug mode
assert(pos.material_key_is_ok());
// Use 'acquire' to avoid a thread reading 'ready' == true while
// another is still working. (compiler reordering may cause this).
@@ -1592,10 +1597,11 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
// Use the DTZ tables to rank root moves.
//
// A return value false indicates that not all probes were successful.
bool Tablebases::root_probe(Position& pos,
Search::RootMoves& rootMoves,
bool rule50,
bool rankDTZ) {
bool Tablebases::root_probe(Position& pos,
Search::RootMoves& rootMoves,
bool rule50,
bool rankDTZ,
const std::function<bool()>& time_abort) {
ProbeState result = OK;
StateInfo st;
@@ -1640,7 +1646,7 @@ bool Tablebases::root_probe(Position& pos,
pos.undo_move(m.pv[0]);
if (result == FAIL)
if (time_abort() || result == FAIL)
return false;
// Better moves are ranked higher. Certain wins are ranked equally.
@@ -1705,10 +1711,11 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, boo
return true;
}
Config Tablebases::rank_root_moves(const OptionsMap& options,
Position& pos,
Search::RootMoves& rootMoves,
bool rankDTZ) {
Config Tablebases::rank_root_moves(const OptionsMap& options,
Position& pos,
Search::RootMoves& rootMoves,
bool rankDTZ,
const std::function<bool()>& time_abort) {
Config config;
if (rootMoves.empty())
@@ -1731,10 +1738,11 @@ Config Tablebases::rank_root_moves(const OptionsMap& options,
if (config.cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
{
// Rank moves using DTZ tables
config.rootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"], rankDTZ);
// Rank moves using DTZ tables, bail out if time_abort flags zeitnot
config.rootInTB =
root_probe(pos, rootMoves, options["Syzygy50MoveRule"], rankDTZ, time_abort);
if (!config.rootInTB)
if (!config.rootInTB && !time_abort())
{
// DTZ tables are missing; try to rank moves using WDL tables
dtz_available = false;
+13 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#ifndef TBPROBE_H
#define TBPROBE_H
#include <functional>
#include <string>
#include <vector>
@@ -66,12 +67,18 @@ extern int MaxCardinality;
void init(const std::string& paths);
WDLScore probe_wdl(Position& pos, ProbeState* result);
int probe_dtz(Position& pos, ProbeState* result);
bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50, bool rankDTZ);
bool root_probe(Position& pos,
Search::RootMoves& rootMoves,
bool rule50,
bool rankDTZ,
const std::function<bool()>& time_abort);
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50);
Config rank_root_moves(const OptionsMap& options,
Position& pos,
Search::RootMoves& rootMoves,
bool rankDTZ = false);
Config rank_root_moves(
const OptionsMap& options,
Position& pos,
Search::RootMoves& rootMoves,
bool rankDTZ = false,
const std::function<bool()>& time_abort = []() { return false; });
} // namespace Stockfish::Tablebases
+52 -10
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,11 +21,17 @@
#include <algorithm>
#include <cassert>
#include <deque>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include "bitboard.h"
#include "cluster.h"
#include "history.h"
#include "memory.h"
#include "misc.h"
#include "movegen.h"
#include "search.h"
#include "syzygy/tbprobe.h"
@@ -41,8 +47,12 @@ namespace Stockfish {
Thread::Thread(Search::SharedState& sharedState,
std::unique_ptr<Search::ISearchManager> sm,
size_t n,
size_t numaN,
size_t totalNumaCount,
OptionalThreadToNumaNodeBinder binder) :
idx(n),
idxInNuma(numaN),
totalNuma(totalNumaCount),
nthreads(sharedState.options["Threads"]),
stdThread(&Thread::idle_loop, this) {
@@ -53,8 +63,8 @@ Thread::Thread(Search::SharedState& sharedState,
// the Worker allocation. Ideally we would also allocate the SearchManager
// here, but that's minor.
this->numaAccessToken = binder();
this->worker =
std::make_unique<Search::Worker>(sharedState, std::move(sm), n, this->numaAccessToken);
this->worker = make_unique_large_page<Search::Worker>(
sharedState, std::move(sm), n, idxInNuma, totalNuma, this->numaAccessToken);
});
wait_for_search_finished();
@@ -132,13 +142,16 @@ Search::SearchManager* ThreadPool::main_manager() { return main_thread()->worker
uint64_t ThreadPool::nodes_searched() const { return accumulate(&Search::Worker::nodes); }
uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits); }
uint64_t ThreadPool::TT_saves() const { return accumulate(&Search::Worker::TTsaves); }
static size_t next_power_of_two(uint64_t count) { return count > 1 ? (2ULL << msb(count - 1)) : 1; }
// Creates/destroys threads to match the requested number.
// Created and launched threads will immediately go to sleep in idle_loop.
// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(const NumaConfig& numaConfig,
Search::SharedState sharedState,
const Search::SearchManager::UpdateContext& updateContext) {
void ThreadPool::set(const NumaConfig& numaConfig,
Search::SharedState sharedState,
Search::SearchManager::UpdateContext& updateContext) {
if (threads.size() > 0) // destroy any existing thread(s)
{
@@ -171,10 +184,36 @@ void ThreadPool::set(const NumaConfig& numaConfig,
return true;
}();
std::map<NumaIndex, size_t> counts;
boundThreadToNumaNode = doBindThreads
? numaConfig.distribute_threads_among_numa_nodes(requested)
: std::vector<NumaIndex>{};
if (boundThreadToNumaNode.empty())
counts[0] = requested; // Pretend all threads are part of numa node 0
else
{
for (size_t i = 0; i < boundThreadToNumaNode.size(); ++i)
counts[boundThreadToNumaNode[i]]++;
}
sharedState.sharedHistories.clear();
for (auto pair : counts)
{
NumaIndex numaIndex = pair.first;
uint64_t count = pair.second;
auto f = [&]() {
sharedState.sharedHistories.try_emplace(numaIndex, next_power_of_two(count));
};
if (doBindThreads)
numaConfig.execute_on_numa_node(numaIndex, f);
else
f();
}
auto threadsPerNode = counts;
counts.clear();
while (threads.size() < requested)
{
const size_t threadId = threads.size();
@@ -190,8 +229,9 @@ void ThreadPool::set(const NumaConfig& numaConfig,
auto binder = doBindThreads ? OptionalThreadToNumaNodeBinder(numaConfig, numaId)
: OptionalThreadToNumaNodeBinder(numaId);
threads.emplace_back(
std::make_unique<Thread>(sharedState, std::move(manager), threadId, binder));
threads.emplace_back(std::make_unique<Thread>(sharedState, std::move(manager), threadId,
counts[numaId]++, threadsPerNode[numaId],
binder));
}
clear();
@@ -282,8 +322,8 @@ void ThreadPool::start_thinking(const OptionsMap& options,
{
th->run_custom_job([&]() {
th->worker->limits = limits;
th->worker->nodes = th->worker->tbHits = th->worker->nmpMinPly =
th->worker->bestMoveChanges = 0;
th->worker->nodes = th->worker->tbHits = th->worker->bestMoveChanges = 0;
th->worker->nmpMinPly = 0;
th->worker->rootDepth = th->worker->completedDepth = 0;
th->worker->rootMoves = rootMoves;
th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState);
@@ -295,6 +335,8 @@ void ThreadPool::start_thinking(const OptionsMap& options,
for (auto&& th : threads)
th->wait_for_search_finished();
Distributed::signals_init();
main_thread()->start_searching();
}
+12 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,8 @@
#include <mutex>
#include <vector>
#include "memory.h"
#include "movepick.h"
#include "numa.h"
#include "position.h"
#include "search.h"
@@ -75,6 +77,8 @@ class Thread {
Thread(Search::SharedState&,
std::unique_ptr<Search::ISearchManager>,
size_t,
size_t,
size_t,
OptionalThreadToNumaNodeBinder);
virtual ~Thread();
@@ -93,13 +97,13 @@ class Thread {
void wait_for_search_finished();
size_t id() const { return idx; }
std::unique_ptr<Search::Worker> worker;
std::function<void()> jobFunc;
LargePagePtr<Search::Worker> worker;
std::function<void()> jobFunc;
private:
std::mutex mutex;
std::condition_variable cv;
size_t idx, nthreads;
size_t idx, idxInNuma, totalNuma, nthreads;
bool exit = false, searching = true; // Set before starting std::thread
NativeThread stdThread;
NumaReplicatedAccessToken numaAccessToken;
@@ -134,14 +138,14 @@ class ThreadPool {
void wait_on_thread(size_t threadId);
size_t num_threads() const;
void clear();
void set(const NumaConfig& numaConfig,
Search::SharedState,
const Search::SearchManager::UpdateContext&);
void
set(const NumaConfig& numaConfig, Search::SharedState, Search::SearchManager::UpdateContext&);
Search::SearchManager* main_manager();
Thread* main_thread() const { return threads.front().get(); }
uint64_t nodes_searched() const;
uint64_t tb_hits() const;
uint64_t TT_saves() const;
Thread* get_best_thread() const;
void start_searching();
void wait_for_search_finished() const;
@@ -164,7 +168,7 @@ class ThreadPool {
std::vector<std::unique_ptr<Thread>> threads;
std::vector<NumaIndex> boundThreadToNumaNode;
uint64_t accumulate(std::atomic<uint64_t> Search::Worker::*member) const {
uint64_t accumulate(std::atomic<uint64_t> Search::Worker::* member) const {
uint64_t sum = 0;
for (auto&& th : threads)
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+3 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -85,16 +85,13 @@ void TimeManagement::init(Search::LimitsType& limits,
// with constants are involved.
const int64_t scaleFactor = useNodesTime ? npmsec : 1;
const TimePoint scaledTime = limits.time[us] / scaleFactor;
const TimePoint scaledInc = limits.inc[us] / scaleFactor;
// Maximum move horizon
int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051;
// If less than one second, gradually reduce mtg
if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051)
{
centiMTG = scaledTime * 5.051;
}
if (scaledTime < 1000)
centiMTG = int(scaledTime * 5.051);
// Make sure timeLeft is > 0 since we may use it as a divisor
TimePoint timeLeft =
+3 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,12 +21,13 @@
#include <cstdint>
#include "cluster.h"
#include "misc.h"
#include "types.h"
namespace Stockfish {
class OptionsMap;
enum Color : int8_t;
namespace Search {
struct LimitsType;
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -234,8 +234,8 @@ std::tuple<bool, TTData, TTWriter> TranspositionTable::probe(const Key key) cons
// Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i)
if (replace->depth8 - replace->relative_age(generation8) * 2
> tte[i].depth8 - tte[i].relative_age(generation8) * 2)
if (replace->depth8 - replace->relative_age(generation8)
> tte[i].depth8 - tte[i].relative_age(generation8))
replace = &tte[i];
return {false,
+14 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,10 @@
namespace Stockfish {
namespace Distributed {
void init();
}
class ThreadPool;
struct TTEntry;
struct Cluster;
@@ -52,7 +56,12 @@ struct TTData {
Bound bound;
bool is_pv;
#ifdef USE_MPI
// We need this for TTCache to be constructible.
TTData() = default;
#else
TTData() = delete;
#endif
// clang-format off
TTData(Move m, Value v, Value ev, Depth d, Bound b, bool pv) :
@@ -73,6 +82,8 @@ struct TTWriter {
private:
friend class TranspositionTable;
friend void Distributed::init();
TTEntry* entry;
TTWriter(TTEntry* tte);
};
@@ -80,6 +91,8 @@ struct TTWriter {
class TranspositionTable {
friend void Distributed::init();
public:
~TranspositionTable() { aligned_large_pages_free(table); }
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+11 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -170,11 +170,20 @@ class Tune {
static OptionsMap* options;
};
template<typename... Args>
constexpr void tune_check_args(Args&&...) {
static_assert((!std::is_fundamental_v<Args> && ...), "TUNE macro arguments wrong");
}
// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
#define STRINGIFY(x) #x
#define UNIQUE2(x, y) x##y
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
#define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__)
#define TUNE(...) \
int UNIQUE(p, __LINE__) = []() -> int { \
tune_check_args(__VA_ARGS__); \
return Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__); \
}();
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
+100 -48
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,7 +37,10 @@
// | only in 64-bit mode and requires hardware with pext support.
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include "misc.h"
#if defined(_MSC_VER)
// Disable some silly and noisy warnings from MSVC compiler
@@ -55,9 +58,15 @@
// _WIN32 Building on Windows (any)
// _WIN64 Building on Windows 64 bit
#if defined(__GNUC__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) \
&& defined(_WIN32) && !defined(__clang__)
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
// Enforce minimum GCC version
#if defined(__GNUC__) && !defined(__clang__) \
&& (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ < 3))
#error "Stockfish requires GCC 9.3 or later for correct compilation"
#endif
// Enforce minimum Clang version
#if defined(__clang__) && (__clang_major__ < 10)
#error "Stockfish requires Clang 10.0 or later for correct compilation"
#endif
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
@@ -108,13 +117,13 @@ using Bitboard = uint64_t;
constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 246;
enum Color {
enum Color : int8_t {
WHITE,
BLACK,
COLOR_NB = 2
};
enum CastlingRights {
enum CastlingRights : int8_t {
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
@@ -130,7 +139,7 @@ enum CastlingRights {
CASTLING_RIGHT_NB = 16
};
enum Bound {
enum Bound : int8_t {
BOUND_NONE,
BOUND_UPPER,
BOUND_LOWER,
@@ -181,13 +190,13 @@ constexpr Value QueenValue = 2538;
// clang-format off
enum PieceType {
enum PieceType : std::int8_t {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
ALL_PIECES = 0,
PIECE_TYPE_NB = 8
};
enum Piece {
enum Piece : std::int8_t {
NO_PIECE,
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
@@ -201,26 +210,24 @@ constexpr Value PieceValue[PIECE_NB] = {
using Depth = int;
enum : int {
// The following DEPTH_ constants are used for transposition table entries
// and quiescence search move generation stages. In regular search, the
// depth stored in the transposition table is literal: the search depth
// (effort) used to make the corresponding transposition table value. In
// quiescence search, however, the transposition table entries only store
// the current quiescence move generation stage (which should thus compare
// lower than any regular search depth).
DEPTH_QS = 0,
// For transposition table entries where no searching at all was done
// (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus
// compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET
// is used only for the transposition table entry occupancy check (see tt.cpp),
// and should thus be lower than DEPTH_UNSEARCHED.
DEPTH_UNSEARCHED = -2,
DEPTH_ENTRY_OFFSET = -3
};
// The following DEPTH_ constants are used for transposition table entries
// and quiescence search move generation stages. In regular search, the
// depth stored in the transposition table is literal: the search depth
// (effort) used to make the corresponding transposition table value. In
// quiescence search, however, the transposition table entries only store
// the current quiescence move generation stage (which should thus compare
// lower than any regular search depth).
constexpr Depth DEPTH_QS = 0;
// For transposition table entries where no searching at all was done
// (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus
// compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET
// is used only for the transposition table entry occupancy check (see tt.cpp),
// and should thus be lower than DEPTH_UNSEARCHED.
constexpr Depth DEPTH_UNSEARCHED = -2;
constexpr Depth DEPTH_ENTRY_OFFSET = -3;
// clang-format off
enum Square : int {
enum Square : int8_t {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
@@ -236,7 +243,7 @@ enum Square : int {
};
// clang-format on
enum Direction : int {
enum Direction : int8_t {
NORTH = 8,
EAST = 1,
SOUTH = -NORTH,
@@ -248,7 +255,7 @@ enum Direction : int {
NORTH_WEST = NORTH + WEST
};
enum File : int {
enum File : int8_t {
FILE_A,
FILE_B,
FILE_C,
@@ -260,7 +267,7 @@ enum File : int {
FILE_NB
};
enum Rank : int {
enum Rank : int8_t {
RANK_1,
RANK_2,
RANK_3,
@@ -274,23 +281,62 @@ enum Rank : int {
// Keep track of what a move changes on the board (used by NNUE)
struct DirtyPiece {
Piece pc; // this is never allowed to be NO_PIECE
Square from, to; // to should be SQ_NONE for promotions
// Number of changed pieces
int dirty_num;
// if {add,remove}_sq is SQ_NONE, {add,remove}_pc is allowed to be
// uninitialized
// castling uses add_sq and remove_sq to remove and add the rook
Square remove_sq, add_sq;
Piece remove_pc, add_pc;
};
// Max 3 pieces can change in one move. A promotion with capture moves
// both the pawn and the captured piece to SQ_NONE and the piece promoted
// to from SQ_NONE to the capture square.
Piece piece[3];
// Keep track of what threats change on the board (used by NNUE)
struct DirtyThreat {
static constexpr int PcSqOffset = 0;
static constexpr int ThreatenedSqOffset = 8;
static constexpr int ThreatenedPcOffset = 16;
static constexpr int PcOffset = 20;
// From and to squares, which may be SQ_NONE
Square from[3];
Square to[3];
DirtyThreat() { /* don't initialize data */ }
DirtyThreat(uint32_t raw) :
data(raw) {}
DirtyThreat(Piece pc, Piece threatened_pc, Square pc_sq, Square threatened_sq, bool add) {
data = (uint32_t(add) << 31) | (pc << PcOffset) | (threatened_pc << ThreatenedPcOffset)
| (threatened_sq << ThreatenedSqOffset) | (pc_sq << PcSqOffset);
}
Piece pc() const { return static_cast<Piece>(data >> PcOffset & 0xf); }
Piece threatened_pc() const { return static_cast<Piece>(data >> ThreatenedPcOffset & 0xf); }
Square threatened_sq() const { return static_cast<Square>(data >> ThreatenedSqOffset & 0xff); }
Square pc_sq() const { return static_cast<Square>(data >> PcSqOffset & 0xff); }
bool add() const { return data >> 31; }
uint32_t raw() const { return data; }
private:
uint32_t data;
};
// A piece can be involved in at most 8 outgoing attacks and 16 incoming attacks.
// Moving a piece also can reveal at most 8 discovered attacks.
// This implies that a non-castling move can change at most (8 + 16) * 3 + 8 = 80 features.
// By similar logic, a castling move can change at most (5 + 1 + 3 + 9) * 2 = 36 features.
// Thus, 80 should work as an upper bound. Finally, 16 entries are added to accommodate
// unmasked vector stores near the end of the list.
using DirtyThreatList = ValueList<DirtyThreat, 96>;
struct DirtyThreats {
DirtyThreatList list;
Color us;
Square prevKsq, ksq;
Bitboard threatenedSqs, threateningSqs;
};
#define ENABLE_INCR_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); }
constexpr T& operator++(T& d) { return d = T(int(d) + 1); } \
constexpr T& operator--(T& d) { return d = T(int(d) - 1); }
ENABLE_INCR_OPERATORS_ON(PieceType)
ENABLE_INCR_OPERATORS_ON(Square)
@@ -303,10 +349,10 @@ constexpr Direction operator+(Direction d1, Direction d2) { return Direction(int
constexpr Direction operator*(int i, Direction d) { return Direction(i * int(d)); }
// Additional operators to add a Direction to a Square
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
inline Square& operator+=(Square& s, Direction d) { return s = s + d; }
inline Square& operator-=(Square& s, Direction d) { return s = s - d; }
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
constexpr Square& operator+=(Square& s, Direction d) { return s = s + d; }
constexpr Square& operator-=(Square& s, Direction d) { return s = s - d; }
// Toggle color
constexpr Color operator~(Color c) { return Color(c ^ BLACK); }
@@ -334,7 +380,7 @@ constexpr Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt);
constexpr PieceType type_of(Piece pc) { return PieceType(pc & 7); }
inline Color color_of(Piece pc) {
constexpr Color color_of(Piece pc) {
assert(pc != NO_PIECE);
return Color(pc >> 3);
}
@@ -403,8 +449,6 @@ class Move {
return Square(data & 0x3F);
}
constexpr int from_to() const { return data & 0xFFF; }
constexpr MoveType type_of() const { return MoveType(data & (3 << 14)); }
constexpr PieceType promotion_type() const { return PieceType(((data >> 12) & 3) + KNIGHT); }
@@ -429,6 +473,14 @@ class Move {
std::uint16_t data;
};
template<typename T, typename... Ts>
struct is_all_same {
static constexpr bool value = (std::is_same_v<T, Ts> && ...);
};
template<typename... Ts>
constexpr auto is_all_same_v = is_all_same<Ts...>::value;
} // namespace Stockfish
#endif // #ifndef TYPES_H_INCLUDED
+36 -35
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
#include <vector>
#include "benchmark.h"
#include "cluster.h"
#include "engine.h"
#include "memory.h"
#include "movegen.h"
@@ -94,7 +95,8 @@ void UCIEngine::loop() {
do
{
if (cli.argc == 1
&& !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication
&& !Distributed::getline(std::cin,
cmd)) // Wait for an input or an end-of-file (EOF) indication
cmd = "quit";
std::istringstream is(cmd);
@@ -112,7 +114,7 @@ void UCIEngine::loop() {
else if (token == "ponderhit")
engine.set_ponderhit(false);
else if (token == "uci")
else if (token == "uci" && Distributed::is_root())
{
sync_cout << "id name " << engine_info(true) << "\n"
<< engine.get_options() << sync_endl;
@@ -133,7 +135,7 @@ void UCIEngine::loop() {
position(is);
else if (token == "ucinewgame")
engine.search_clear();
else if (token == "isready")
else if (token == "isready" && Distributed::is_root())
sync_cout << "readyok" << sync_endl;
// Add custom non-UCI commands, mainly for debugging purposes.
@@ -144,13 +146,13 @@ void UCIEngine::loop() {
bench(is);
else if (token == BenchmarkCommand)
benchmark(is);
else if (token == "d")
else if (token == "d" && Distributed::is_root())
sync_cout << engine.visualize() << sync_endl;
else if (token == "eval")
else if (token == "eval" && Distributed::is_root())
engine.trace_eval();
else if (token == "compiler")
else if (token == "compiler" && Distributed::is_root())
sync_cout << compiler_info() << sync_endl;
else if (token == "export_net")
else if (token == "export_net" && Distributed::is_root())
{
std::pair<std::optional<std::string>, std::string> files[2];
@@ -162,7 +164,9 @@ void UCIEngine::loop() {
engine.save_network(files);
}
else if (token == "--help" || token == "help" || token == "--license" || token == "license")
else if ((token == "--help" || token == "help" || token == "--license"
|| token == "license")
&& Distributed::is_root())
sync_cout
<< "\nStockfish is a powerful chess engine for playing and analyzing."
"\nIt is released as free software licensed under the GNU GPLv3 License."
@@ -171,7 +175,7 @@ void UCIEngine::loop() {
"\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme"
"\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n"
<< sync_endl;
else if (!token.empty() && token[0] != '#')
else if (!token.empty() && token[0] != '#' && Distributed::is_root())
sync_cout << "Unknown command: '" << cmd << "'. Type help for more information."
<< sync_endl;
@@ -252,8 +256,9 @@ void UCIEngine::bench(std::istream& args) {
if (token == "go" || token == "eval")
{
std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << engine.fen() << ")"
<< std::endl;
if (Distributed::is_root())
std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << engine.fen() << ")"
<< std::endl;
if (token == "go")
{
Search::LimitsType limits = parse_limits(is);
@@ -269,7 +274,7 @@ void UCIEngine::bench(std::istream& args) {
nodes += nodesSearched;
nodesSearched = 0;
}
else
else if (Distributed::is_root())
engine.trace_eval();
}
else if (token == "setoption")
@@ -287,10 +292,11 @@ void UCIEngine::bench(std::istream& args) {
dbg_print();
std::cerr << "\n===========================" //
<< "\nTotal time (ms) : " << elapsed //
<< "\nNodes searched : " << nodes //
<< "\nNodes/second : " << 1000 * nodes / elapsed << std::endl;
if (Distributed::is_root())
std::cerr << "\n===========================" //
<< "\nTotal time (ms) : " << elapsed //
<< "\nNodes searched : " << nodes //
<< "\nNodes/second : " << 1000 * nodes / elapsed << std::endl;
// reset callback, to not capture a dangling reference to nodesSearched
engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); });
@@ -313,8 +319,8 @@ void UCIEngine::benchmark(std::istream& args) {
Benchmark::BenchmarkSetup setup = Benchmark::setup_benchmark(args);
const int numGoCommands = count_if(setup.commands.begin(), setup.commands.end(),
[](const std::string& s) { return s.find("go ") == 0; });
const auto numGoCommands = count_if(setup.commands.begin(), setup.commands.end(),
[](const std::string& s) { return s.find("go ") == 0; });
TimePoint totalTime = 0;
@@ -339,16 +345,9 @@ void UCIEngine::benchmark(std::istream& args) {
Search::LimitsType limits = parse_limits(is);
TimePoint elapsed = now();
// Run with silenced network verification
engine.go(limits);
engine.wait_for_search_finished();
totalTime += now() - elapsed;
nodes += nodesSearched;
nodesSearched = 0;
}
else if (token == "position")
position(is);
@@ -368,13 +367,14 @@ void UCIEngine::benchmark(std::istream& args) {
int numHashfullReadings = 0;
constexpr int hashfullAges[] = {0, 999}; // Only normal hashfull and touched hash.
int totalHashfull[std::size(hashfullAges)] = {0};
int maxHashfull[std::size(hashfullAges)] = {0};
constexpr int hashfullAgeCount = std::size(hashfullAges);
int totalHashfull[hashfullAgeCount] = {0};
int maxHashfull[hashfullAgeCount] = {0};
auto updateHashfullReadings = [&]() {
numHashfullReadings += 1;
for (int i = 0; i < static_cast<int>(std::size(hashfullAges)); ++i)
for (int i = 0; i < hashfullAgeCount; ++i)
{
const int hashfull = engine.get_hashfull(hashfullAges[i]);
maxHashfull[i] = std::max(maxHashfull[i], hashfull);
@@ -396,6 +396,7 @@ void UCIEngine::benchmark(std::istream& args) {
Search::LimitsType limits = parse_limits(is);
nodesSearched = 0;
TimePoint elapsed = now();
// Run with silenced network verification
@@ -407,7 +408,6 @@ void UCIEngine::benchmark(std::istream& args) {
updateHashfullReadings();
nodes += nodesSearched;
nodesSearched = 0;
}
else if (token == "position")
position(is);
@@ -467,7 +467,8 @@ void UCIEngine::setoption(std::istringstream& is) {
std::uint64_t UCIEngine::perft(const Search::LimitsType& limits) {
auto nodes = engine.perft(engine.fen(), limits.perft, engine.get_options()["UCI_Chess960"]);
sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
if (Distributed::is_root())
sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
return nodes;
}
@@ -561,7 +562,7 @@ int UCIEngine::to_cp(Value v, const Position& pos) {
auto [a, b] = win_rate_params(pos);
return std::round(100 * int(v) / a);
return int(std::round(100 * int(v) / a));
}
std::string UCIEngine::wdl(Value v, const Position& pos) {
@@ -630,12 +631,12 @@ void UCIEngine::on_update_full(const Engine::InfoFull& info, bool showWDL) {
<< " multipv " << info.multiPV //
<< " score " << format_score(info.score); //
if (showWDL)
ss << " wdl " << info.wdl;
if (!info.bound.empty())
ss << " " << info.bound;
if (showWDL)
ss << " wdl " << info.wdl;
ss << " nodes " << info.nodes //
<< " nps " << info.nps //
<< " hashfull " << info.hashfull //
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ namespace Stockfish {
class Position;
class Move;
class Score;
enum Square : int;
enum Square : int8_t;
using Value = int;
class UCIEngine {
+6 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include <sstream>
#include <utility>
#include "cluster.h"
#include "misc.h"
namespace Stockfish {
@@ -54,7 +55,7 @@ void OptionsMap::setoption(std::istringstream& is) {
if (options_map.count(name))
options_map[name] = value;
else
else if (Distributed::is_root())
sync_cout << "No such option: " << name << sync_endl;
}
@@ -110,7 +111,7 @@ Option::Option(OnChange f) :
max(0),
on_change(std::move(f)) {}
Option::Option(double v, int minv, int maxv, OnChange f) :
Option::Option(int v, int minv, int maxv, OnChange f) :
type("spin"),
min(minv),
max(maxv),
@@ -154,7 +155,7 @@ Option& Option::operator=(const std::string& v) {
if ((type != "button" && type != "string" && v.empty())
|| (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (std::stof(v) < min || std::stof(v) > max)))
|| (type == "spin" && (std::stoi(v) < min || std::stoi(v) > max)))
return *this;
if (type == "combo")
@@ -202,7 +203,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
}
else if (o.type == "spin")
os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max "
os << " default " << stoi(o.defaultValue) << " min " << o.min << " max "
<< o.max;
break;
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -43,7 +43,7 @@ class Option {
Option(OnChange = nullptr);
Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr);
Option(double v, int minv, int maxv, OnChange = nullptr);
Option(int v, int minv, int maxv, OnChange = nullptr);
Option(const char* v, const char* cur, OnChange = nullptr);
Option& operator=(const std::string&);
+7 -8
View File
@@ -67,7 +67,6 @@ def Stockfish(*args, **kwargs):
class TestCLI(metaclass=OrderedClassMembers):
def beforeAll(self):
pass
@@ -141,7 +140,7 @@ class TestCLI(metaclass=OrderedClassMembers):
def test_bench_128_threads_3_bench_tmp_epd_depth(self):
self.stockfish = Stockfish(
f"bench 128 {get_threads()} 3 {os.path.join(PATH,'bench_tmp.epd')} depth".split(
f"bench 128 {get_threads()} 3 {os.path.join(PATH, 'bench_tmp.epd')} depth".split(
" "
),
True,
@@ -167,7 +166,7 @@ class TestCLI(metaclass=OrderedClassMembers):
def test_export_net_verify_nnue(self):
current_path = os.path.abspath(os.getcwd())
self.stockfish = Stockfish(
f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True
f"export_net {os.path.join(current_path, 'verify.nnue')}".split(" "), True
)
assert self.stockfish.process.returncode == 0
@@ -254,7 +253,7 @@ class TestInteractive(metaclass=OrderedClassMembers):
self.stockfish.send_command("go depth 5")
def callback(output):
regex = r"info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
regex = r"info depth \d+ seldepth \d+ multipv \d+ score cp -?\d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
if output.startswith("info depth") and not re.match(regex, output):
assert False
if output.startswith("bestmove"):
@@ -274,7 +273,7 @@ class TestInteractive(metaclass=OrderedClassMembers):
def callback(output):
nonlocal depth
regex = rf"info depth {depth} seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
regex = rf"info depth {depth} seldepth \d+ multipv \d+ score cp -?\d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
if output.startswith("info depth"):
if not re.match(regex, output):
@@ -390,7 +389,7 @@ class TestInteractive(metaclass=OrderedClassMembers):
def test_verify_nnue_network(self):
current_path = os.path.abspath(os.getcwd())
Stockfish(
f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True
f"export_net {os.path.join(current_path, 'verify.nnue')}".split(" "), True
)
self.stockfish.send_command("setoption name EvalFile value verify.nnue")
@@ -469,7 +468,7 @@ class TestSyzygy(metaclass=OrderedClassMembers):
self.stockfish.send_command("go depth 5")
def check_output(output):
if "score cp -20000" in output or "score mate" in output:
if "score cp -20000" in output or "score mate -" in output:
return True
self.stockfish.check_output(check_output)
@@ -508,7 +507,7 @@ if __name__ == "__main__":
framework = MiniTestFramework()
# Each test suite will be ran inside a temporary directory
# Each test suite will be run inside a temporary directory
framework.run([TestCLI, TestInteractive, TestSyzygy])
EPD.delete_bench_epd()
+1 -1
View File
@@ -16,7 +16,7 @@ EXPECT_SCRIPT=$(mktemp)
cat << 'EOF' > $EXPECT_SCRIPT
#!/usr/bin/expect -f
set timeout 30
set timeout 120
lassign [lrange $argv 0 4] pos depth result chess960 logfile
log_file -noappend $logfile
spawn ./stockfish
+12 -2
View File
@@ -2,16 +2,26 @@
# obtain and optionally verify Bench / signature
# if no reference is given, the output is deliberately limited to just the signature
STDOUT_FILE=$(mktemp)
STDERR_FILE=$(mktemp)
error()
{
echo "running bench for signature failed on line $1"
echo "===== STDOUT ====="
cat "$STDOUT_FILE"
echo "===== STDERR ====="
cat "$STDERR_FILE"
rm -f "$STDOUT_FILE" "$STDERR_FILE"
exit 1
}
trap 'error ${LINENO}' ERR
# obtain
eval "$RUN_PREFIX ./stockfish bench" > "$STDOUT_FILE" 2> "$STDERR_FILE" || error ${LINENO}
signature=$(grep "Nodes searched : " "$STDERR_FILE" | awk '{print $4}')
signature=`eval "$WINE_PATH ./stockfish bench 2>&1" | grep "Nodes searched : " | awk '{print $4}'`
rm -f "$STDOUT_FILE" "$STDERR_FILE"
if [ $# -gt 0 ]; then
# compare to given reference
@@ -28,4 +38,4 @@ if [ $# -gt 0 ]; then
else
# just report signature
echo $signature
fi
fi