diff --git a/.github/workflows/arm_compilation.yml b/.github/workflows/arm_compilation.yml index 781bd8070..86d222182 100644 --- a/.github/workflows/arm_compilation.yml +++ b/.github/workflows/arm_compilation.yml @@ -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 diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 7805b24d6..473665ec2 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -76,9 +76,9 @@ jobs: - 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 diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 4e598ecfc..f8134a199 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -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 diff --git a/src/Makefile b/src/Makefile index e12bd5c7a..a40612888 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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,7 +64,7 @@ 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 \ @@ -439,7 +454,7 @@ endif ifeq ($(COMP),gcc) comp=gcc CXX=g++ - CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations -Wstack-usage=128000 + CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64)) ifeq ($(OS),Android) @@ -610,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 diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 039e384c9..4e266db89 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -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 diff --git a/src/benchmark.h b/src/benchmark.h index d6bdc275d..a6606e78c 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -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 diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 6c97d7863..4decb8d66 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -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,7 +32,6 @@ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard RayPassBB[SQUARE_NB][SQUARE_NB]; -Bitboard PseudoAttacks[PIECE_TYPE_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) { - PseudoAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); - PseudoAttacks[BLACK][s1] = pawn_attacks_bb(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(s1, 0); - PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb(s1, 0); - for (PieceType pt : {BISHOP, ROOK}) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) { @@ -115,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 @@ -167,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 @@ -184,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]; diff --git a/src/bitboard.h b/src/bitboard.h index 1cd717b8f..f97ff3219 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -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 #include #include +#include +#include #include "types.h" @@ -62,8 +64,6 @@ extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB]; -extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; - // Magic holds all magic bitboards relevant data for a single square struct Magic { @@ -203,69 +203,12 @@ inline int distance(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 -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 -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(s, occupied) | attacks_bb(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(s, occupied); - case ROOK : - return attacks_bb(s, occupied); - case QUEEN : - return attacks_bb(s, occupied) | attacks_bb(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); +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((b * 0x0101010101010101ULL) >> 56); } // Counts the number of non-zero bits in a bitboard. @@ -373,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, PIECE_TYPE_NB> attacks{}; + + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + { + attacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); + attacks[BLACK][s1] = pawn_attacks_bb(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 +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 +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(s, occupied) | attacks_bb(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(s, occupied); + case ROOK : + return attacks_bb(s, occupied); + case QUEEN : + return attacks_bb(s, occupied) | attacks_bb(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 diff --git a/src/engine.cpp b/src/engine.cpp index 397cb5627..a315578b8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -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 @@ -240,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"]); diff --git a/src/engine.h b/src/engine.h index 7315b8881..10c92d759 100644 --- a/src/engine.h +++ b/src/engine.h @@ -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 #include #include +#include #include #include #include #include #include +#include "history.h" #include "nnue/network.h" #include "numa.h" #include "position.h" @@ -122,6 +124,7 @@ class Engine { Search::SearchManager::UpdateContext updateContext; std::function onVerifyNetworks; + std::map sharedHists; }; } // namespace Stockfish diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d20843e85..745bd3e4d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -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 @@ -42,8 +42,8 @@ namespace Stockfish { // an approximation of the material advantage on the board in terms of pawns. int Eval::simple_eval(const Position& pos) { Color c = pos.side_to_move(); - return PawnValue * (pos.count(c) - pos.count(~c)) - + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); + return PawnValue * (pos.count(c) - pos.count(~c)) + pos.non_pawn_material(c) + - pos.non_pawn_material(~c); } bool Eval::use_smallnet(const Position& pos) { return std::abs(simple_eval(pos)) > 962; } diff --git a/src/evaluate.h b/src/evaluate.h index 8ed2eb994..b4f54a381 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -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 diff --git a/src/history.h b/src/history.h index 87004ead9..c98a7ee22 100644 --- a/src/history.h +++ b/src/history.h @@ -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 #include +#include #include #include #include @@ -28,63 +29,58 @@ #include #include // IWYU pragma: keep +#include "memory.h" #include "misc.h" #include "position.h" namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 8192; // 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::max() + 1; +constexpr int CORRHIST_BASE_SIZE = UINT_16_HISTORY_SIZE; constexpr int CORRECTION_HISTORY_LIMIT = 1024; 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((UINT_16_HISTORY_SIZE & (UINT_16_HISTORY_SIZE - 1)) == 0, - "CORRECTION_HISTORY_SIZE has to be a power of 2"); - -inline int pawn_history_index(const Position& pos) { - return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); -} - -inline uint16_t pawn_correction_history_index(const Position& pos) { - return uint16_t(pos.pawn_key()); -} - -inline uint16_t minor_piece_index(const Position& pos) { return uint16_t(pos.minor_piece_key()); } - -template -inline uint16_t non_pawn_index(const Position& pos) { - return uint16_t(pos.non_pawn_key(c)); -} +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 -class StatsEntry { - +template +struct StatsEntry { static_assert(std::is_arithmetic_v, "Not an arithmetic type"); - static_assert(D <= std::numeric_limits::max(), "D overflows T"); - T entry; + private: + std::conditional_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); } }; @@ -96,6 +92,42 @@ enum StatsType { template using Stats = MultiArray, Sizes...>; +template +using AtomicStats = MultiArray, 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 +struct DynStats { + explicit DynStats(size_t s) { + size = s * SizeMultiplier; + data = make_unique_large_page(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 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, @@ -118,7 +150,8 @@ using PieceToHistory = Stats; using ContinuationHistory = MultiArray; // PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = Stats; +using PawnHistory = + DynStats, 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 @@ -132,11 +165,27 @@ enum CorrHistType { Continuation, // Combined history of move pairs }; +template +struct CorrectionBundle { + StatsEntry pawn; + StatsEntry minor; + StatsEntry nonPawnWhite; + StatsEntry nonPawnBlack; + + void operator=(T val) { + pawn = val; + minor = val; + nonPawnWhite = val; + nonPawnBlack = val; + } +}; + namespace Detail { template struct CorrHistTypedef { - using type = Stats; + using type = + DynStats, CORRHIST_BASE_SIZE>; }; template<> @@ -151,17 +200,74 @@ struct CorrHistTypedef { template<> struct CorrHistTypedef { - using type = - Stats; + using type = DynStats, + CORRHIST_BASE_SIZE>; }; } +using UnifiedCorrectionHistory = + DynStats, COLOR_NB>, + CORRHIST_BASE_SIZE>; + template using CorrectionHistory = typename Detail::CorrHistTypedef::type; using TTMoveHistory = StatsEntry; +// 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 + auto& nonpawn_correction_entry(const Position& pos) { + return correctionHistory[pos.non_pawn_key(c) & sizeMinus1]; + } + template + 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 diff --git a/src/main.cpp b/src/main.cpp index a943a6910..a1770ed81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 "bitboard.h" #include "misc.h" -#include "nnue/features/full_threats.h" #include "position.h" #include "tune.h" #include "uci.h" @@ -35,7 +34,6 @@ int main(int argc, char* argv[]) { Bitboards::init(); Position::init(); - Eval::NNUE::Features::init_threat_offsets(); auto uci = std::make_unique(argc, argv); diff --git a/src/memory.cpp b/src/memory.cpp index f4aa8fc26..94a599399 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -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 diff --git a/src/memory.h b/src/memory.h index dad07df1d..c307a1317 100644 --- a/src/memory.h +++ b/src/memory.h @@ -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 diff --git a/src/misc.cpp b/src/misc.cpp index 886544b6c..3ddae503c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -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 diff --git a/src/misc.h b/src/misc.h index c9951e555..f1016b496 100644 --- a/src/misc.h +++ b/src/misc.h @@ -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 diff --git a/src/movegen.cpp b/src/movegen.cpp index 697a83cdf..d22faad25 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -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 diff --git a/src/movegen.h b/src/movegen.h index 287fd8927..7f209f92a 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -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 diff --git a/src/movepick.cpp b/src/movepick.cpp index 7de11fa1f..415d252f3 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -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 @@ -87,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) { @@ -159,7 +159,7 @@ ExtMove* MovePicker::score(MoveList& ml) { { // histories m.value = 2 * (*mainHistory)[us][m.raw()]; - m.value += 2 * (*pawnHistory)[pawn_history_index(pos)][pc][to]; + 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]; @@ -184,11 +184,7 @@ ExtMove* MovePicker::score(MoveList& ml) { if (pos.capture_stage(m)) m.value = PieceValue[capturedPiece] + (1 << 28); else - { m.value = (*mainHistory)[us][m.raw()] + (*continuationHistory[0])[pc][to]; - if (ply < LOW_PLY_HISTORY_SIZE) - m.value += (*lowPlyHistory)[ply][m.raw()]; - } } } return it; diff --git a/src/movepick.h b/src/movepick.h index 5b3190594..08bd9a539 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -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(); @@ -64,7 +64,7 @@ class MovePicker { const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; - const PawnHistory* pawnHistory; + const SharedHistories* sharedHistory; Move ttMove; ExtMove * cur, *endCur, *endBadCaptures, *endCaptures, *endGenerated; int stage; diff --git a/src/nnue/features/full_threats.cpp b/src/nnue/features/full_threats.cpp index 645bb7090..4e3ba81cf 100644 --- a/src/nnue/features/full_threats.cpp +++ b/src/nnue/features/full_threats.cpp @@ -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,9 @@ #include "full_threats.h" #include +#include #include +#include #include "../../bitboard.h" #include "../../misc.h" @@ -31,26 +33,28 @@ namespace Stockfish::Eval::NNUE::Features { -// Lookup array for indexing threats -IndexType offsets[PIECE_NB][SQUARE_NB]; - struct HelperOffsets { int cumulativePieceOffset, cumulativeOffset; }; -std::array helper_offsets; // 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; - PiecePairData() {} - PiecePairData(bool excluded_pair, bool semi_excluded_pair, IndexType feature_index_base) { - data = - excluded_pair << 1 | (semi_excluded_pair && !excluded_pair) | feature_index_base << 8; - } + + 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 - uint8_t excluded_pair_info() const { return (uint8_t) data; } - IndexType feature_index_base() const { return data >> 8; } + constexpr uint8_t excluded_pair_info() const { return static_cast(data); } + + constexpr IndexType feature_index_base() const { return static_cast(data >> 8); } }; constexpr std::array AllPieces = { @@ -58,12 +62,119 @@ constexpr std::array AllPieces = { B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, }; -// The final index is calculated from summing data found in these two LUTs, as well -// as offsets[attacker][from] -PiecePairData index_lut1[PIECE_NB][PIECE_NB]; // [attacker][attacked] -uint8_t index_lut2[PIECE_NB][SQUARE_NB][SQUARE_NB]; // [attacker][from][to] +template +constexpr auto make_piece_indices_type() { + static_assert(PT != PieceType::PAWN); + + std::array, 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 +constexpr auto make_piece_indices_piece() { + static_assert(type_of(P) == PieceType::PAWN); + + std::array, 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(); + constexpr auto BISHOP_ATTACKS = make_piece_indices_type(); + constexpr auto ROOK_ATTACKS = make_piece_indices_type(); + constexpr auto QUEEN_ATTACKS = make_piece_indices_type(); + constexpr auto KING_ATTACKS = make_piece_indices_type(); + + std::array, SQUARE_NB>, PIECE_NB> indices{}; + + indices[W_PAWN] = make_piece_indices_piece(); + indices[B_PAWN] = make_piece_indices_piece(); + + 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 indices{}; + std::array, 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(square_bb(from)) + : pawn_attacks_bb(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, PIECE_NB> indices{}; -static void init_index_luts() { for (Piece attacker : AllPieces) { for (Piece attacked : AllPieces) @@ -78,56 +189,21 @@ static void init_index_luts() { + (color_of(attacked) * (numValidTargets[attacker] / 2) + map) * helper_offsets[attacker].cumulativePieceOffset; - bool excluded = map < 0; - index_lut1[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature); + bool excluded = map < 0; + indices[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature); } } - for (Piece attacker : AllPieces) - { - for (int from = 0; from < SQUARE_NB; ++from) - { - for (int to = 0; to < SQUARE_NB; ++to) - { - Bitboard attacks = attacks_bb(attacker, Square(from)); - index_lut2[attacker][from][to] = popcount((square_bb(Square(to)) - 1) & attacks); - } - } - } + return indices; } -void init_threat_offsets() { - int cumulativeOffset = 0; - for (Piece piece : AllPieces) - { - int pieceIdx = piece; - int cumulativePieceOffset = 0; +// The final index is calculated from summing data found in these two LUTs, as well +// as offsets[attacker][from] - for (Square from = SQ_A1; from <= SQ_H8; ++from) - { - offsets[pieceIdx][from] = cumulativePieceOffset; - - if (type_of(piece) != PAWN) - { - Bitboard attacks = attacks_bb(piece, from, 0ULL); - cumulativePieceOffset += popcount(attacks); - } - - else if (from >= SQ_A2 && from <= SQ_H7) - { - Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb(square_bb(from)) - : pawn_attacks_bb(square_bb(from)); - cumulativePieceOffset += popcount(attacks); - } - } - - helper_offsets[pieceIdx] = {cumulativePieceOffset, cumulativeOffset}; - - cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset; - } - - init_index_luts(); -} +// [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( @@ -247,11 +323,11 @@ void FullThreats::append_changed_indices(Color perspective, { if (first) { - fusedData->dp2removedOriginBoard |= square_bb(to); + fusedData->dp2removedOriginBoard |= to; continue; } } - else if (fusedData->dp2removedOriginBoard & square_bb(to)) + else if (fusedData->dp2removedOriginBoard & to) continue; } @@ -261,11 +337,11 @@ void FullThreats::append_changed_indices(Color perspective, { if (first) { - fusedData->dp2removedTargetBoard |= square_bb(from); + fusedData->dp2removedTargetBoard |= from; continue; } } - else if (fusedData->dp2removedTargetBoard & square_bb(from)) + else if (fusedData->dp2removedTargetBoard & from) continue; } } diff --git a/src/nnue/features/full_threats.h b/src/nnue/features/full_threats.h index 177e9fabe..5b2582954 100644 --- a/src/nnue/features/full_threats.h +++ b/src/nnue/features/full_threats.h @@ -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 the Free Software Foundation, either version 3 of the License, or @@ -32,7 +32,6 @@ 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}; -void init_threat_offsets(); class FullThreats { public: diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 56779ddce..a82e89de4 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -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 diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index c58a3246b..49b0a87a4 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -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 diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 7ead09327..a3d072f67 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -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 diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 85c0dbfec..5e0551f69 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -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 @@ -138,11 +138,12 @@ void find_nnz(const std::int32_t* RESTRICT input, using namespace SIMD; constexpr IndexType InputSimdWidth = sizeof(vec_uint_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(InputSimdWidth, 8); - constexpr IndexType NumChunks = InputDimensions / ChunkSize; - constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth; - constexpr IndexType OutputsPerChunk = ChunkSize / 8; + // 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(input); IndexType count = 0; @@ -157,15 +158,11 @@ void find_nnz(const std::int32_t* RESTRICT input, 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(&Lookup.offset_indices[lookup])); - vec128_storeu(reinterpret_cast(out + count), vec128_add(base, offsets)); - count += popcount(lookup); - base = vec128_add(base, increment); - } + const vec128_t offsets = + vec128_load(reinterpret_cast(&Lookup.offset_indices[nnz])); + vec128_storeu(reinterpret_cast(out + count), vec128_add(base, offsets)); + count += popcount(nnz); + base = vec128_add(base, increment); } count_out = count; #endif diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index a8679d14d..7284c1033 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -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 diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 4218c0fe2..53412d014 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -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 diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 815c81074..4d5b683fe 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -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 diff --git a/src/nnue/network.h b/src/nnue/network.h index ba8f469f7..d0e3218ca 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -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 diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index 763fb7a5e..16af8d5f6 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -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 @@ -645,6 +645,18 @@ Bitboard get_changed_pieces(const std::array& oldPieces, sameBB |= static_cast(equalMask) << i; } return ~sameBB; +#elif defined(USE_NEON) + uint8x16x4_t old_v = vld4q_u8(reinterpret_cast(oldPieces.data())); + uint8x16x4_t new_v = vld4q_u8(reinterpret_cast(newPieces.data())); + auto cmp = [=](const int i) { return vceqq_u8(old_v.val[i], new_v.val[i]); }; + + uint8x16_t cmp0_1 = vsriq_n_u8(cmp(1), cmp(0), 1); + uint8x16_t cmp2_3 = vsriq_n_u8(cmp(3), cmp(2), 1); + uint8x16_t merged = vsriq_n_u8(cmp2_3, cmp0_1, 2); + merged = vsriq_n_u8(merged, merged, 4); + uint8x8_t sameBB = vshrn_n_u16(vreinterpretq_u16_u8(merged), 4); + + return ~vget_lane_u64(vreinterpret_u64_u8(sameBB), 0); #else Bitboard changed = 0; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 1ccab5f2f..438074f43 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -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 diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 5093abdd7..71fce9bd5 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -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 diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 8a877ae2b..febe7ca70 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -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 diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index ce23bdf0e..99cda2a69 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -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 @@ -145,15 +145,10 @@ class FeatureTransformer { } 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 diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 220140e5e..66a6764a3 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -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 diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h index 7ecfd58a2..ecece5589 100644 --- a/src/nnue/nnue_misc.h +++ b/src/nnue/nnue_misc.h @@ -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 diff --git a/src/nnue/simd.h b/src/nnue/simd.h index 6160221b9..25891163e 100644 --- a/src/nnue/simd.h +++ b/src/nnue/simd.h @@ -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 diff --git a/src/numa.h b/src/numa.h index 76d265af2..99169c211 100644 --- a/src/numa.h +++ b/src/numa.h @@ -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 diff --git a/src/perft.h b/src/perft.h index 4a49201d5..4cb409cf4 100644 --- a/src/perft.h +++ b/src/perft.h @@ -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 diff --git a/src/position.cpp b/src/position.cpp index a4286b86a..d8b02e8ab 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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 #include "bitboard.h" +#include "history.h" #include "misc.h" #include "movegen.h" #include "syzygy/tbprobe.h" @@ -692,13 +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 +// 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 TranspositionTable* tt = nullptr, + const SharedHistories* history = nullptr) { assert(m.is_ok()); assert(&newSt != st); @@ -853,6 +855,7 @@ void Position::do_move(Move m, 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]; @@ -880,6 +883,15 @@ void Position::do_move(Move m, 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(*this)); + prefetch(&history->nonpawn_correction_entry(*this)); + } + // Set capture piece st->capturedPiece = captured; diff --git a/src/position.h b/src/position.h index e5d3f6d28..a136c0729 100644 --- a/src/position.h +++ b/src/position.h @@ -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,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 @@ -69,7 +70,6 @@ struct StateInfo { // elements are not invalidated upon list resizing. using StateListPtr = std::unique_ptr>; - // 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 @@ -140,7 +140,8 @@ class Position { bool givesCheck, DirtyPiece& dp, DirtyThreats& dts, - const TranspositionTable* tt); + const TranspositionTable* tt, + const SharedHistories* worker); void undo_move(Move m); void do_null_move(StateInfo& newSt, const TranspositionTable& tt); void undo_null_move(); @@ -403,7 +404,7 @@ inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) { inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) { new (&scratch_dts) DirtyThreats; - do_move(m, newSt, gives_check(m), scratch_dp, scratch_dts, tt); + do_move(m, newSt, gives_check(m), scratch_dp, scratch_dts, tt, nullptr); } inline StateInfo* Position::state() const { return st; } diff --git a/src/score.cpp b/src/score.cpp index 561bc23c4..ea62577b9 100644 --- a/src/score.cpp +++ b/src/score.cpp @@ -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 diff --git a/src/score.h b/src/score.h index eda90af35..cf89d3cdd 100644 --- a/src/score.h +++ b/src/score.h @@ -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 diff --git a/src/search.cpp b/src/search.cpp index 8a48f4e09..fd9151407 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -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 @@ -66,6 +66,7 @@ using namespace Search; namespace { constexpr int SEARCHEDLIST_CAPACITY = 32; +constexpr int mainHistoryDefault = 68; using SearchedList = ValueList; // (*Scalers): @@ -78,18 +79,19 @@ using SearchedList = ValueList; // optimized for require verifications at longer time controls int correction_value(const Worker& w, const Position& pos, const Stack* const ss) { - const Color us = pos.side_to_move(); - const auto m = (ss - 1)->currentMove; - const auto pcv = w.pawnCorrectionHistory[pawn_correction_history_index(pos)][us]; - const auto micv = w.minorPieceCorrectionHistory[minor_piece_index(pos)][us]; - const auto wnpcv = w.nonPawnCorrectionHistory[non_pawn_index(pos)][WHITE][us]; - const auto bnpcv = w.nonPawnCorrectionHistory[non_pawn_index(pos)][BLACK][us]; - const auto cntcv = + const Color us = pos.side_to_move(); + const auto m = (ss - 1)->currentMove; + const auto& shared = w.sharedHistory; + const int pcv = shared.pawn_correction_entry(pos).at(us).pawn; + const int micv = shared.minor_piece_correction_entry(pos).at(us).minor; + const int wnpcv = shared.nonpawn_correction_entry(pos).at(us).nonPawnWhite; + const int bnpcv = shared.nonpawn_correction_entry(pos).at(us).nonPawnBlack; + const int cntcv = m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] + (*(ss - 4)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] - : 8; + : 8; - return 10347 * pcv + 8821 * micv + 11168 * (wnpcv + bnpcv) + 7841 * cntcv; + return 10347 * pcv + 8821 * micv + 11665 * (wnpcv + bnpcv) + 7841 * cntcv; } // Add correctionHistory value to raw staticEval and guarantee evaluation @@ -106,13 +108,12 @@ void update_correction_history(const Position& pos, const Color us = pos.side_to_move(); constexpr int nonPawnWeight = 178; + auto& shared = workerThread.sharedHistory; - workerThread.pawnCorrectionHistory[pawn_correction_history_index(pos)][us] << bonus; - workerThread.minorPieceCorrectionHistory[minor_piece_index(pos)][us] << bonus * 156 / 128; - workerThread.nonPawnCorrectionHistory[non_pawn_index(pos)][WHITE][us] - << bonus * nonPawnWeight / 128; - workerThread.nonPawnCorrectionHistory[non_pawn_index(pos)][BLACK][us] - << bonus * nonPawnWeight / 128; + shared.pawn_correction_entry(pos).at(us).pawn << bonus; + shared.minor_piece_correction_entry(pos).at(us).minor << bonus * 156 / 128; + shared.nonpawn_correction_entry(pos).at(us).nonPawnWhite << bonus * nonPawnWeight / 128; + shared.nonpawn_correction_entry(pos).at(us).nonPawnBlack << bonus * nonPawnWeight / 128; if (m.is_ok()) { @@ -156,9 +157,14 @@ bool is_shuffling(Move move, Stack* const ss, const Position& pos) { Search::Worker::Worker(SharedState& sharedState, std::unique_ptr sm, size_t threadId, + size_t numaThreadId, + size_t numaTotalThreads, NumaReplicatedAccessToken token) : // Unpack the SharedState struct into member variables + sharedHistory(sharedState.sharedHistories.at(token.get_numa_index())), threadIdx(threadId), + numaThreadIdx(numaThreadId), + numaTotal(numaTotalThreads), numaAccessToken(token), manager(std::move(sm)), options(sharedState.options), @@ -349,6 +355,11 @@ void Search::Worker::iterative_deepening() { lowPlyHistory.fill(97); + for (Color c : {WHITE, BLACK}) + for (int i = 0; i < UINT_16_HISTORY_SIZE; i++) + mainHistory[c][i] = + (mainHistory[c][i] - mainHistoryDefault) * 3 / 4 + mainHistoryDefault; + // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop && !(limits.depth && mainThread && Distributed::is_root() && rootDepth > limits.depth)) @@ -592,7 +603,7 @@ void Search::Worker::do_move( nodes.store(nodes.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed); auto [dirtyPiece, dirtyThreats] = accumulatorStack.push(); - pos.do_move(move, st, givesCheck, dirtyPiece, dirtyThreats, &tt); + pos.do_move(move, st, givesCheck, dirtyPiece, dirtyThreats, &tt, &sharedHistory); if (ss != nullptr) { @@ -621,12 +632,12 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); } // Reset histories, usually before a new game void Search::Worker::clear() { - mainHistory.fill(68); + mainHistory.fill(mainHistoryDefault); captureHistory.fill(-689); - pawnHistory.fill(-1238); - pawnCorrectionHistory.fill(5); - minorPieceCorrectionHistory.fill(0); - nonPawnCorrectionHistory.fill(0); + + // Each thread is responsible for clearing their part of shared history + sharedHistory.correctionHistory.clear_range(0, numaThreadIdx, numaTotal); + sharedHistory.pawnHistory.clear_range(-1238, numaThreadIdx, numaTotal); ttMoveHistory = 0; @@ -900,7 +911,7 @@ Value Search::Worker::search( mainHistory[~us][((ss - 1)->currentMove).raw()] << evalDiff * 9; if (!ttHit && type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) - pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq] << evalDiff * 13; + sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << evalDiff * 13; } @@ -1032,7 +1043,7 @@ moves_loop: // When in check, search starts here MovePicker mp(pos, ttData.move, depth, &mainHistory, &lowPlyHistory, &captureHistory, contHist, - &pawnHistory, ss->ply); + &sharedHistory, ss->ply); value = bestValue; @@ -1121,7 +1132,7 @@ moves_loop: // When in check, search starts here { int history = (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + pawnHistory[pawn_history_index(pos)][movedPiece][move.to_sq()]; + + sharedHistory.pawn_entry(pos)[movedPiece][move.to_sq()]; // Continuation history based pruning if (history < -4083 * depth) @@ -1243,8 +1254,9 @@ moves_loop: // When in check, search starts here r += 1119; // Increase reduction if next ply has a lot of fail high - if ((ss + 1)->cutoffCnt > 2) - r += 991 + allNode * 923; + if ((ss + 1)->cutoffCnt > 1) + r += 120 + 1024 * ((ss + 1)->cutoffCnt > 2) + 100 * ((ss + 1)->cutoffCnt > 3) + + 1024 * allNode; // For first picked move (ttMove) reduce reduction if (move == ttData.move) @@ -1261,6 +1273,10 @@ moves_loop: // When in check, search starts here // Decrease/increase reduction for moves with a good/bad history r -= ss->statScore * 850 / 8192; + // Scale up reductions for expected ALL nodes + if (allNode) + r += r / (depth + 1); + // Step 17. Late moves reduction / extension (LMR) if (depth >= 2 && moveCount > 1) { @@ -1281,7 +1297,7 @@ moves_loop: // When in check, search starts here { // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = d < newDepth && value > (bestValue + 43 + 2 * newDepth); + const bool doDeeperSearch = d < newDepth && value > bestValue + 50; const bool doShallowerSearch = value < bestValue + 9; newDepth += doDeeperSearch - doShallowerSearch; @@ -1474,7 +1490,7 @@ moves_loop: // When in check, search starts here mainHistory[~us][((ss - 1)->currentMove).raw()] << scaledBonus * 243 / 32768; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) - pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq] + sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << scaledBonus * 1160 / 32768; } @@ -1649,7 +1665,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // the moves. We presently use two stages of move generator in quiescence search: // captures, or evasions only when in check. MovePicker mp(pos, ttData.move, DEPTH_QS, &mainHistory, &lowPlyHistory, &captureHistory, - contHist, &pawnHistory, ss->ply); + contHist, &sharedHistory, ss->ply); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta // cutoff occurs. @@ -1867,16 +1883,24 @@ void update_all_stats(const Position& pos, Piece movedPiece = pos.moved_piece(bestMove); PieceType capturedPiece; - int bonus = std::min(116 * depth - 81, 1515) + 347 * (bestMove == ttMove); + int bonus = + std::min(116 * depth - 81, 1515) + 347 * (bestMove == ttMove) + (ss - 1)->statScore / 32; int malus = std::min(848 * depth - 207, 2446) - 17 * moveCount; if (!pos.capture_stage(bestMove)) { update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 910 / 1024); + int i = 0; // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -malus * 1085 / 1024); + { + i++; + int actualMalus = malus * 1085 / 1024; + if (i > 5) + actualMalus -= actualMalus * (i - 5) / i; + update_quiet_histories(pos, ss, workerThread, move, -actualMalus); + } } else { @@ -1930,8 +1954,7 @@ void update_quiet_histories( update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 896 / 1024); - int pIndex = pawn_history_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] + workerThread.sharedHistory.pawn_entry(pos)[pos.moved_piece(move)][move.to_sq()] << bonus * (bonus > 0 ? 905 : 505) / 1024; } diff --git a/src/search.h b/src/search.h index 43826093d..3ceeb7d58 100644 --- a/src/search.h +++ b/src/search.h @@ -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 #include #include +#include #include #include #include @@ -140,15 +141,18 @@ struct SharedState { SharedState(const OptionsMap& optionsMap, ThreadPool& threadPool, TranspositionTable& transpositionTable, + std::map& sharedHists, const LazyNumaReplicatedSystemWide& nets) : options(optionsMap), threads(threadPool), tt(transpositionTable), + sharedHistories(sharedHists), networks(nets) {} const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; + std::map& sharedHistories; const LazyNumaReplicatedSystemWide& networks; }; @@ -270,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, size_t, NumaReplicatedAccessToken); + Worker(SharedState&, + std::unique_ptr, + size_t, + size_t, + size_t, + NumaReplicatedAccessToken); // Called at instantiation to initialize reductions tables. // Reset histories, usually before a new game. @@ -294,13 +302,8 @@ class Worker { ButterflyHistory mainHistory; LowPlyHistory lowPlyHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; - - CorrectionHistory pawnCorrectionHistory; - CorrectionHistory minorPieceCorrectionHistory; - CorrectionHistory nonPawnCorrectionHistory; + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; CorrectionHistory continuationCorrectionHistory; #ifdef USE_MPI @@ -325,7 +328,8 @@ class Worker { Value ev, uint8_t generation8); - TTMoveHistory ttMoveHistory; + TTMoveHistory ttMoveHistory; + SharedHistories& sharedHistory; private: void iterative_deepening(); @@ -372,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 diff --git a/src/shm.h b/src/shm.h index b870afc24..9bf13f234 100644 --- a/src/shm.h +++ b/src/shm.h @@ -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 diff --git a/src/shm_linux.h b/src/shm_linux.h index a8b5404b2..1e344e93f 100644 --- a/src/shm_linux.h +++ b/src/shm_linux.h @@ -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 @@ -502,7 +502,7 @@ class SharedMemory: public detail::SharedMemoryBase { return false; bool success = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) == 0; -#ifdef PTHREAD_MUTEX_ROBUST +#if _POSIX_C_SOURCE >= 200809L if (success) success = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) == 0; #endif @@ -524,7 +524,7 @@ class SharedMemory: public detail::SharedMemoryBase { if (rc == 0) return true; -#ifdef PTHREAD_MUTEX_ROBUST +#if _POSIX_C_SOURCE >= 200809L if (rc == EOWNERDEAD) { if (pthread_mutex_consistent(&header_ptr_->mutex) == 0) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 107a522ea..007a891e5 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -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,6 +35,7 @@ #include #include #include +#include #include "../bitboard.h" #include "../cluster.h" diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 4a6c3b763..7b60d6e20 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -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 diff --git a/src/thread.cpp b/src/thread.cpp index abddaa19b..94a151346 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -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,14 +21,17 @@ #include #include #include +#include #include #include #include #include +#include "bitboard.h" #include "cluster.h" -#include "misc.h" +#include "history.h" #include "memory.h" +#include "misc.h" #include "movegen.h" #include "search.h" #include "syzygy/tbprobe.h" @@ -44,8 +47,12 @@ namespace Stockfish { Thread::Thread(Search::SharedState& sharedState, std::unique_ptr 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) { @@ -56,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 = make_unique_large_page(sharedState, std::move(sm), n, - this->numaAccessToken); + this->worker = make_unique_large_page( + sharedState, std::move(sm), n, idxInNuma, totalNuma, this->numaAccessToken); }); wait_for_search_finished(); @@ -137,6 +144,8 @@ uint64_t ThreadPool::nodes_searched() const { return accumulate(&Search::Worker: 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. @@ -175,10 +184,36 @@ void ThreadPool::set(const NumaConfig& numaConfig, return true; }(); + std::map counts; boundThreadToNumaNode = doBindThreads ? numaConfig.distribute_threads_among_numa_nodes(requested) : std::vector{}; + 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(); @@ -194,8 +229,9 @@ void ThreadPool::set(const NumaConfig& numaConfig, auto binder = doBindThreads ? OptionalThreadToNumaNodeBinder(numaConfig, numaId) : OptionalThreadToNumaNodeBinder(numaId); - threads.emplace_back( - std::make_unique(sharedState, std::move(manager), threadId, binder)); + threads.emplace_back(std::make_unique(sharedState, std::move(manager), threadId, + counts[numaId]++, threadsPerNode[numaId], + binder)); } clear(); diff --git a/src/thread.h b/src/thread.h index 1ecd325b8..0330ef705 100644 --- a/src/thread.h +++ b/src/thread.h @@ -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 @@ -77,6 +77,8 @@ class Thread { Thread(Search::SharedState&, std::unique_ptr, size_t, + size_t, + size_t, OptionalThreadToNumaNodeBinder); virtual ~Thread(); @@ -101,7 +103,7 @@ class Thread { 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; diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index fb4b2ec97..5a8d43a2e 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -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 diff --git a/src/timeman.cpp b/src/timeman.cpp index e82a1f6bf..4e98081bc 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -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 diff --git a/src/timeman.h b/src/timeman.h index 483f3a4d8..249a71bec 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -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 diff --git a/src/tt.cpp b/src/tt.cpp index d7f7dbdef..ef602809f 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -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 diff --git a/src/tt.h b/src/tt.h index 6676193f9..d142a105a 100644 --- a/src/tt.h +++ b/src/tt.h @@ -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 diff --git a/src/tune.cpp b/src/tune.cpp index f53a0eb52..f930c267e 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -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 diff --git a/src/tune.h b/src/tune.h index d3c9ebaa3..4ce6e759f 100644 --- a/src/tune.h +++ b/src/tune.h @@ -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 diff --git a/src/types.h b/src/types.h index 1bb8bd3cc..b5a0498a8 100644 --- a/src/types.h +++ b/src/types.h @@ -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 diff --git a/src/uci.cpp b/src/uci.cpp index 6d4f5331a..0d943805f 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -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 diff --git a/src/uci.h b/src/uci.h index 1686b3a72..c9b594393 100644 --- a/src/uci.h +++ b/src/uci.h @@ -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 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 9f2dfc286..1e10850b1 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -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 diff --git a/src/ucioption.h b/src/ucioption.h index 0c957fda1..4f6d7541c 100644 --- a/src/ucioption.h +++ b/src/ucioption.h @@ -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 diff --git a/tests/signature.sh b/tests/signature.sh index 0f6dd7585..ef781a0f4 100755 --- a/tests/signature.sh +++ b/tests/signature.sh @@ -18,7 +18,7 @@ error() trap 'error ${LINENO}' ERR # obtain -eval "$WINE_PATH ./stockfish bench" > "$STDOUT_FILE" 2> "$STDERR_FILE" || error ${LINENO} +eval "$RUN_PREFIX ./stockfish bench" > "$STDOUT_FILE" 2> "$STDERR_FILE" || error ${LINENO} signature=$(grep "Nodes searched : " "$STDERR_FILE" | awk '{print $4}') rm -f "$STDOUT_FILE" "$STDERR_FILE"