Merge branch 'sf16branch' into clusterMergeMaster17

This commit is contained in:
Joost VandeVondele
2023-07-03 20:35:36 +02:00
67 changed files with 2423 additions and 1732 deletions
+31 -1
View File
@@ -1,6 +1,8 @@
name: Stockfish
on:
push:
tags:
- '*'
branches:
- master
- tools
@@ -10,6 +12,31 @@ on:
- master
- tools
jobs:
Prerelease:
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# returns null if no pre-release exists
- name: Get Commit SHA of Latest Pre-release
run: |
# Install required packages
sudo apt-get update
sudo apt-get install -y curl jq
echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV
# delete old previous pre-release and tag
- uses: dev-drprasad/delete-tag-and-release@v0.2.1
if: env.COMMIT_SHA != 'null'
with:
tag_name: ${{ env.COMMIT_SHA }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Sanitizers:
uses: ./.github/workflows/stockfish_sanitizers.yml
Tests:
@@ -17,5 +44,8 @@ jobs:
Compiles:
uses: ./.github/workflows/stockfish_compile_test.yml
Binaries:
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')
uses: ./.github/workflows/stockfish_binaries.yml
ARM_Binaries:
if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')
uses: ./.github/workflows/stockfish_arm_binaries.yml
@@ -0,0 +1,158 @@
name: Stockfish
on:
workflow_call:
jobs:
Stockfish:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EMU: ${{ matrix.config.emu }}
EXT: ${{ matrix.config.ext }}
OS: ${{ matrix.config.os }}
BINARY: ${{ matrix.binaries }}
strategy:
matrix:
config:
- name: Android NDK aarch64
os: ubuntu-22.04
compiler: aarch64-linux-android21-clang++
emu: qemu-aarch64
comp: ndk
shell: bash {0}
- name: Android NDK arm
os: ubuntu-22.04
compiler: armv7a-linux-androideabi21-clang++
emu: qemu-arm
comp: ndk
shell: bash {0}
binaries:
- armv8
- armv7
- armv7-neon
exclude:
- binaries: armv8
config: {compiler: armv7a-linux-androideabi21-clang++}
- binaries: armv7
config: {compiler: aarch64-linux-android21-clang++}
- binaries: armv7-neon
config: {compiler: aarch64-linux-android21-clang++}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Download required linux packages
if: runner.os == 'Linux'
run: |
sudo apt update
sudo apt install qemu-user
- name: Install NDK
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;$NDKV"
ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV
ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin
echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV
fi
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
fi
$COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
# Compile profile guided builds
- name: Compile ${{ matrix.binaries }} build
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU
make strip ARCH=$BINARY COMP=$COMP
mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT
- name: Remove non src files
run: rm -f *.o .depend *.nnue
- name: Download wiki
run: |
git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki
cd ../wiki
rm -rf .git
- name: Create tar archive.
run: |
cd ..
mkdir stockfish
cp -r wiki stockfish/
cp -r src stockfish/
cp stockfish-android-$BINARY$EXT stockfish/
cp "Top CPU Contributors.txt" stockfish/
cp Copying.txt stockfish/
cp AUTHORS stockfish/
cp CITATION.cff stockfish/
cp README.md stockfish/
tar -cvf stockfish-android-$BINARY.tar stockfish
- name: Upload binaries
uses: actions/upload-artifact@v3
with:
name: stockfish-android-${{ matrix.binaries }}
path: stockfish-android-${{ matrix.binaries }}.tar
- name: Release
if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'
uses: softprops/action-gh-release@v1
with:
files: stockfish-android-${{ matrix.binaries }}.tar
- name: Get last commit sha
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Make sure that an old ci which still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
continue-on-error: true
uses: softprops/action-gh-release@v1
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-android-${{ matrix.binaries }}.tar
+103 -45
View File
@@ -9,35 +9,35 @@ jobs:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }}
OS: ${{ matrix.config.os }}
NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }}
strategy:
matrix:
config:
- {
name: "Ubuntu 20.04 GCC",
os: ubuntu-20.04,
compiler: g++,
comp: gcc,
shell: 'bash {0}'
}
- {
name: "MacOS 12 Apple Clang",
os: macos-12,
compiler: clang++,
comp: clang,
shell: 'bash {0}'
}
- {
name: "Windows 2022 Mingw-w64 GCC x86_64",
os: windows-2022,
compiler: g++,
comp: mingw,
msys_sys: 'mingw64',
msys_env: 'x86_64-gcc',
shell: 'msys2 {0}',
ext: .exe
}
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
simple_name: ubuntu
compiler: g++
comp: gcc
shell: bash {0}
archive_ext: tar
- name: MacOS 12 Apple Clang
os: macos-12
simple_name: macos
compiler: clang++
comp: clang
shell: bash {0}
archive_ext: tar
- name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022
simple_name: windows
compiler: g++
comp: mingw
msys_sys: mingw64
msys_env: x86_64-gcc
shell: msys2 {0}
ext: .exe
archive_ext: zip
binaries:
- x86-64
- x86-64-modern
@@ -56,55 +56,113 @@ jobs:
- name: Download required linux packages
if: runner.os == 'Linux'
run: |
sudo apt update
run: sudo apt update
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.config.msys_sys}}
install: mingw-w64-${{matrix.config.msys_env}} make git expect
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Download the used network from the fishtest framework
run: |
make net
run: make net
- name: Check compiler
run: |
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
$COMPILER -v
run: $COMPILER -v
- name: Test help target
run: |
make help
run: make help
- name: Check git
run: git --version
# Compile profile guided builds
- name: Compile ${{ matrix.binaries }} build
run: |
make clean
make -j2 profile-build ARCH=$BINARY COMP=$COMP
strip ./stockfish$EXT
mv ./stockfish$EXT ../stockfish-$OS-$BINARY$EXT
make strip ARCH=$BINARY COMP=$COMP
mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT
- name: Remove non src files
run: rm -f *.o .depend *.nnue
run: git clean -fx
- name: Create tar archive.
- name: Download wiki
run: |
git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki
rm -rf ../wiki/.git
- name: Create directory.
run: |
cd ..
mkdir stockfish
cp -r wiki stockfish/
cp -r src stockfish/
cp stockfish-$OS-$BINARY$EXT stockfish/
cp stockfish-$NAME-$BINARY$EXT stockfish/
cp "Top CPU Contributors.txt" stockfish/
cp Copying.txt stockfish/
cp AUTHORS stockfish/
tar -cvf stockfish-$OS-$BINARY.tar stockfish
cp CITATION.cff stockfish/
cp README.md stockfish/
- name: Create tar
if: runner.os != 'Windows'
run: |
cd ..
tar -cvf stockfish-$NAME-$BINARY.tar stockfish
- name: Create zip
if: runner.os == 'Windows'
run: |
cd ..
zip -r stockfish-$NAME-$BINARY.zip stockfish
- name: Upload binaries
if: runner.os != 'Windows'
uses: actions/upload-artifact@v3
with:
name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}
path: |
stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}.tar
path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar
# Artifacts automatically get zipped
# to avoid double zipping, we use the unzipped directory
- name: Upload binaries
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}
path: stockfish
- name: Release
if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'
uses: softprops/action-gh-release@v1
with:
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
- name: Get last commit sha
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Make sure that an old ci which still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
continue-on-error: true
uses: softprops/action-gh-release@v1
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
+42 -55
View File
@@ -11,52 +11,40 @@ jobs:
strategy:
matrix:
config:
- {
name: "Ubuntu 20.04 GCC",
os: ubuntu-20.04,
compiler: g++,
comp: gcc,
shell: 'bash {0}'
}
- {
name: "Ubuntu 20.04 Clang",
os: ubuntu-20.04,
compiler: clang++,
comp: clang,
shell: 'bash {0}'
}
- {
name: "MacOS 12 Apple Clang",
os: macos-12,
compiler: clang++,
comp: clang,
shell: 'bash {0}'
}
- {
name: "MacOS 12 GCC 11",
os: macos-12,
compiler: g++-11,
comp: gcc,
shell: 'bash {0}'
}
- {
name: "Windows 2022 Mingw-w64 GCC x86_64",
os: windows-2022,
compiler: g++,
comp: mingw,
msys_sys: 'mingw64',
msys_env: 'x86_64-gcc',
shell: 'msys2 {0}'
}
- {
name: "Windows 2022 Mingw-w64 Clang x86_64",
os: windows-2022,
compiler: clang++,
comp: clang,
msys_sys: 'clang64',
msys_env: 'clang-x86_64-clang',
shell: 'msys2 {0}'
}
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
compiler: g++
comp: gcc
shell: bash {0}
- name: Ubuntu 20.04 Clang
os: ubuntu-20.04
compiler: clang++
comp: clang
shell: bash {0}
- name: MacOS 12 Apple Clang
os: macos-12
compiler: clang++
comp: clang
shell: bash {0}
- name: MacOS 12 GCC 11
os: macos-12
compiler: g++-11
comp: gcc
shell: bash {0}
- name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022
compiler: g++
comp: mingw
msys_sys: mingw64
msys_env: x86_64-gcc
shell: msys2 {0}
- name: Windows 2022 Mingw-w64 Clang x86_64
os: windows-2022
compiler: clang++
comp: clang
msys_sys: clang64
msys_env: clang-x86_64-clang
shell: msys2 {0}
defaults:
run:
@@ -72,20 +60,19 @@ jobs:
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.config.msys_sys}}
install: mingw-w64-${{matrix.config.msys_env}} make git expect
install: mingw-w64-${{matrix.config.msys_env}} make git
- name: Download the used network from the fishtest framework
run: |
make net
run: make net
- name: Check compiler
run: |
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
$COMPILER -v
run: $COMPILER -v
- name: Test help target
run: |
make help
run: make help
- name: Check git
run: git --version
# x86-64 with newer extensions tests
@@ -112,4 +99,4 @@ jobs:
- name: Compile x86-64-vnni256 build
run: |
make clean
make -j2 ARCH=x86-64-vnni256 build
make -j2 ARCH=x86-64-vnni256 build
+25 -36
View File
@@ -12,34 +12,24 @@ jobs:
strategy:
matrix:
config:
- {
name: "Ubuntu 20.04 GCC",
os: ubuntu-20.04,
compiler: g++,
comp: gcc,
shell: 'bash {0}'
}
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
compiler: g++
comp: gcc
shell: bash {0}
sanitizers:
- {
name: Run with thread sanitizer,
make_option: sanitize=thread,
instrumented_option: sanitizer-thread
}
- {
name: Run with UB sanitizer,
make_option: sanitize=undefined,
instrumented_option: sanitizer-undefined
}
- {
name: Run under valgrind,
make_option: "",
instrumented_option: valgrind
}
- {
name: Run under valgrind-thread,
make_option: "",
instrumented_option: valgrind-thread
}
- name: Run with thread sanitizer
make_option: sanitize=thread
instrumented_option: sanitizer-thread
- name: Run with UB sanitizer
make_option: sanitize=undefined
instrumented_option: sanitizer-undefined
- name: Run under valgrind
make_option: ""
instrumented_option: valgrind
- name: Run under valgrind-thread
make_option: ""
instrumented_option: valgrind-thread
defaults:
run:
working-directory: src
@@ -52,20 +42,19 @@ jobs:
- name: Download required linux packages
run: |
sudo apt update
sudo apt install expect valgrind g++-multilib qemu-user
sudo apt install expect valgrind g++-multilib
- name: Download the used network from the fishtest framework
run: |
make net
run: make net
- name: Check compiler
run: |
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
$COMPILER -v
run: $COMPILER -v
- name: Test help target
run: |
make help
run: make help
- name: Check git
run: git --version
# Sanitizers
@@ -74,4 +63,4 @@ jobs:
export CXXFLAGS="-O1 -fno-inline"
make clean
make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null
../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }}
../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }}
+91 -119
View File
@@ -12,95 +12,68 @@ jobs:
strategy:
matrix:
config:
- {
name: "Ubuntu 20.04 GCC",
os: ubuntu-20.04,
compiler: g++,
comp: gcc,
run_32bit_tests: true,
run_64bit_tests: true,
shell: 'bash {0}'
}
- {
name: "Ubuntu 20.04 Clang",
os: ubuntu-20.04,
compiler: clang++,
comp: clang,
run_32bit_tests: true,
run_64bit_tests: true,
shell: 'bash {0}'
}
- {
name: "Ubuntu 20.04 NDK armv8",
os: ubuntu-20.04,
compiler: aarch64-linux-android21-clang++,
comp: ndk,
run_armv8_tests: false,
shell: 'bash {0}'
}
- {
name: "Ubuntu 20.04 NDK armv7",
os: ubuntu-20.04,
compiler: armv7a-linux-androideabi21-clang++,
comp: ndk,
run_armv7_tests: false,
shell: 'bash {0}'
}
- {
name: "MacOS 12 Apple Clang",
os: macos-12,
compiler: clang++,
comp: clang,
run_64bit_tests: true,
shell: 'bash {0}'
}
- {
name: "MacOS 12 GCC 11",
os: macos-12,
compiler: g++-11,
comp: gcc,
run_64bit_tests: true,
shell: 'bash {0}'
}
- {
name: "Windows 2022 Mingw-w64 GCC x86_64",
os: windows-2022,
compiler: g++,
comp: mingw,
run_64bit_tests: true,
msys_sys: 'mingw64',
msys_env: 'x86_64-gcc',
shell: 'msys2 {0}'
}
- {
name: "Windows 2022 Mingw-w64 GCC i686",
os: windows-2022,
compiler: g++,
comp: mingw,
run_32bit_tests: true,
msys_sys: 'mingw32',
msys_env: 'i686-gcc',
shell: 'msys2 {0}'
}
- {
name: "Windows 2022 Mingw-w64 Clang x86_64",
os: windows-2022,
compiler: clang++,
comp: clang,
run_64bit_tests: true,
msys_sys: 'clang64',
msys_env: 'clang-x86_64-clang',
shell: 'msys2 {0}'
}
exclude:
- config:
{
name: "Ubuntu 20.04 NDK armv7"
}
- config:
{
name: "Ubuntu 20.04 NDK armv8"
}
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
compiler: g++
comp: gcc
run_32bit_tests: true
run_64bit_tests: true
shell: bash {0}
- name: Ubuntu 20.04 Clang
os: ubuntu-20.04
compiler: clang++
comp: clang
run_32bit_tests: true
run_64bit_tests: true
shell: bash {0}
- name: Android NDK aarch64
os: ubuntu-22.04
compiler: aarch64-linux-android21-clang++
comp: ndk
run_armv8_tests: true
shell: bash {0}
- name: Android NDK arm
os: ubuntu-22.04
compiler: armv7a-linux-androideabi21-clang++
comp: ndk
run_armv7_tests: true
shell: bash {0}
- name: MacOS 12 Apple Clang
os: macos-12
compiler: clang++
comp: clang
run_64bit_tests: true
shell: bash {0}
- name: MacOS 12 GCC 11
os: macos-12
compiler: g++-11
comp: gcc
run_64bit_tests: true
shell: bash {0}
- name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022
compiler: g++
comp: mingw
run_64bit_tests: true
msys_sys: mingw64
msys_env: x86_64-gcc
shell: msys2 {0}
- name: Windows 2022 Mingw-w64 GCC i686
os: windows-2022
compiler: g++
comp: mingw
run_32bit_tests: true
msys_sys: mingw32
msys_env: i686-gcc
shell: msys2 {0}
- name: Windows 2022 Mingw-w64 Clang x86_64
os: windows-2022
compiler: clang++
comp: clang
run_64bit_tests: true
msys_sys: clang64
msys_env: clang-x86_64-clang
shell: msys2 {0}
defaults:
run:
working-directory: src
@@ -116,30 +89,47 @@ jobs:
sudo apt update
sudo apt install expect valgrind g++-multilib qemu-user
- name: Install NDK
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;$NDKV"
ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV
ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin
echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV
fi
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.config.msys_sys}}
install: mingw-w64-${{matrix.config.msys_env}} make git expect
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git expect
- name: Download the used network from the fishtest framework
run: |
make net
run: make net
- name: Extract the bench number from the commit history
run: |
git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
git log HEAD | grep -o "\b[Bb]ench[ :]\+[1-9][0-9]\{5,9\}\b" | head -n 1 | sed "s/[^0-9]//g" > git_sig
[ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found"
- name: Check compiler
run: |
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
fi
$COMPILER -v
- name: Test help target
run: |
make help
run: make help
- name: Check git
run: git --version
# x86-32 tests
@@ -229,13 +219,7 @@ jobs:
- name: Test armv8 build
if: ${{ matrix.config.run_armv8_tests }}
run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean
make -j2 ARCH=armv8 build
@@ -246,13 +230,7 @@ jobs:
- name: Test armv7 build
if: ${{ matrix.config.run_armv7_tests }}
run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean
make -j2 ARCH=armv7 build
@@ -261,13 +239,7 @@ jobs:
- name: Test armv7-neon build
if: ${{ matrix.config.run_armv7_tests }}
run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean
make -j2 ARCH=armv7-neon build
@@ -281,4 +253,4 @@ jobs:
make clean
make -j2 ARCH=x86-64-modern build
../tests/perft.sh
../tests/reprosearch.sh
../tests/reprosearch.sh
+29 -19
View File
@@ -1,17 +1,15 @@
# List of authors for Stockfish
# Founders of the Stockfish project and fishtest infrastructure
# Founders of the Stockfish project and Fishtest infrastructure
Tord Romstad (romstad)
Marco Costalba (mcostalba)
Joona Kiiski (zamar)
Gary Linscott (glinscott)
# Authors and inventors of NNUE, training, NNUE port
# Authors and inventors of NNUE, training, and NNUE port
Yu Nasu (ynasu87)
Motohiro Isozaki (yaneurao)
Hisayori Noda (nodchip)
# all other authors of the code in alphabetical order
# All other authors of Stockfish code (in alphabetical order)
Aditya (absimaldata)
Adrian Petrescu (apetresc)
Ajith Chandy Jose (ajithcj)
@@ -21,6 +19,7 @@ Alexander Kure
Alexander Pagel (Lolligerhans)
Alfredo Menezes (lonfom169)
Ali AlZhrani (Cooffe)
Andreas Matthies (Matthies)
Andrei Vetrov (proukornew)
Andrew Grant (AndyGrant)
Andrey Neporada (nepal)
@@ -35,6 +34,7 @@ Ben Chaney (Chaneybenjamini)
Ben Koshy (BKSpurgeon)
Bill Henry (VoyagerOne)
Bojun Guo (noobpwnftw, Nooby)
borg323
Boštjan Mejak (PedanticHacker)
braich
Brian Sheppard (SapphireBrand, briansheppard-toast)
@@ -46,12 +46,12 @@ Chess13234
Chris Cain (ceebo)
clefrks
Dale Weiler (graphitemaster)
Dan Schmidt (dfannius)
Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic)
Dan Schmidt (dfannius)
Dariusz Orzechowski (dorzechowski)
David Zar
David (dav1312)
David Zar
Daylen Yang (daylen)
Deshawn Mohan-Smith (GoldenRare)
Dieter Dobbelaere (ddobbelaere)
@@ -65,7 +65,6 @@ Eelco de Groot (KingDefender)
Elvin Liu (solarlight2)
erbsenzaehler
Ernesto Gatti
Linmiao Xu (linrock)
Fabian Beuke (madnight)
Fabian Fichter (ianfab)
Fanael Linithien (Fanael)
@@ -78,32 +77,35 @@ George Sobala (gsobala)
gguliash
Giacomo Lorenzetti (G-Lorenz)
Gian-Carlo Pascutto (gcp)
Goh CJ (cj5716)
Gontran Lemaire (gonlem)
Goodkov Vasiliy Aleksandrovich (goodkov)
Gregor Cramer
GuardianRM
Günther Demetz (pb00067, pb00068)
Guy Vreuls (gvreuls)
Günther Demetz (pb00067, pb00068)
Henri Wiechers
Hiraoka Takuya (HiraokaTakuya)
homoSapiensSapiens
Hongzhi Cheng
Ivan Ivec (IIvec)
Jacques B. (Timshel)
Jake Senne (w1wwwwww)
Jan Ondruš (hxim)
Jared Kish (Kurtbusch, kurt22i)
Jarrod Torriero (DU-jdto)
Jean Gauthier (OuaisBla)
Jean-Francois Romang (jromang)
Jean Gauthier (OuaisBla)
Jekaa
Jerry Donald Watson (jerrydonaldwatson)
jjoshua2
Jonathan Calovski (Mysseno)
Jonathan Buladas Dumale (SFisGOD)
Jonathan Calovski (Mysseno)
Jonathan McDermid (jonathanmcdermid)
Joost VandeVondele (vondele)
Jörg Oster (joergoster)
Joseph Ellis (jhellis3)
Joseph R. Prostko
Jörg Oster (joergoster)
Julian Willemer (NightlyKing)
jundery
Justin Blanchard (UncombedCoconut)
@@ -117,6 +119,7 @@ Krystian Kuzniarek (kuzkry)
Leonardo Ljubičić (ICCF World Champion)
Leonid Pechenik (lp--)
Liam Keegan (lkeegan)
Linmiao Xu (linrock)
Linus Arver (listx)
loco-loco
Lub van den Berg (ElbertoOne)
@@ -131,6 +134,7 @@ Matt Ginsberg (mattginsberg)
Matthew Lai (matthewlai)
Matthew Sullivan (Matt14916)
Max A. (Disservin)
Maxim Masiutin (maximmasiutin)
Maxim Molchanov (Maxim)
Michael An (man)
Michael Byrne (MichaelB7)
@@ -145,16 +149,18 @@ Mira
Miroslav Fontán (Hexik)
Moez Jellouli (MJZ1977)
Mohammed Li (tthsqe12)
Muzhen J (XInTheDark)
Nathan Rugg (nmrugg)
Nick Pelling (nickpelling)
Nguyen Pham (nguyenpham)
Nicklas Persson (NicklasPersson)
Nick Pelling (nickpelling)
Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT)
Nguyen Pham (nguyenpham)
Norman Schmidt (FireFather)
notruck
Ofek Shochat (OfekShochat, ghostway)
Ondrej Mosnáček (WOnder93)
Ondřej Mišina (AndrovT)
Oskar Werkelin Ahlin
Pablo Vazquez
Panthee
@@ -163,7 +169,9 @@ Pasquale Pigazzini (ppigazzini)
Patrick Jansen (mibere)
Peter Schneider (pschneider1968)
Peter Zsifkovits (CoffeeOne)
PikaCat
Praveen Kumar Tummala (praveentml)
Prokop Randáček (ProkopRandacek)
Rahul Dsilva (silversolver1)
Ralph Stößer (Ralph Stoesser)
Raminder Singh
@@ -172,8 +180,8 @@ Reuven Peleg (R-Peleg)
Richard Lloyd (Richard-Lloyd)
Rodrigo Exterckötter Tjäder
Rodrigo Roim (roim)
Ron Britvich (Britvich)
Ronald de Man (syzygy1, syzygy)
Ron Britvich (Britvich)
rqs
Rui Coelho (ruicoelhopedro)
Ryan Schmitt
@@ -184,21 +192,22 @@ Sergei Antonov (saproj)
Sergei Ivanov (svivanov72)
Sergio Vieri (sergiovieri)
sf-x
Shahin M. Shahin (peregrine)
Shane Booth (shane31)
Shawn Varghese (xXH4CKST3RXx)
Siad Daboul (Topologist)
Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80)
Stefano Di Martino (StefanoD)
Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet)
Syine Mineta (MinetaS)
Prokop Randáček (ProkopRandacek)
Thanar2
thaspel
theo77186
Tomasz Sobczyk (Sopel97)
Tom Truscott
Tom Vijlbrief (tomtor)
Tomasz Sobczyk (Sopel97)
Torsten Franz (torfranz, tfranzer)
Torsten Hellwig (Torom)
Tracey Emery (basepr1me)
@@ -206,11 +215,12 @@ tttak
Unai Corzo (unaiic)
Uri Blass (uriblass)
Vince Negri (cuddlestmonkey)
Viren
windfishballad
xefoci7612
zz4032
# Additionally, we acknowledge the authors and maintainers of fishtest,
# an amazing and essential framework for the development of Stockfish!
# an amazing and essential framework for Stockfish development!
#
# https://github.com/glinscott/fishtest/blob/master/AUTHORS
+23
View File
@@ -0,0 +1,23 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: Stockfish
message: >-
Please cite this software using the metadata from this
file.
type: software
authors:
- name: The Stockfish developers (see AUTHORS file)
repository-code: 'https://github.com/official-stockfish/Stockfish'
url: 'https://stockfishchess.org/'
repository-artifact: 'https://stockfishchess.org/download/'
abstract: Stockfish is a free and strong UCI chess engine.
keywords:
- chess
- artificial intelligence (AI)
- tree search
- alpha-beta search
- neural networks (NN)
- efficiently updatable neural networks (NNUE)
license: GPL-3.0
+83 -297
View File
@@ -2,6 +2,21 @@
[![Stockfish][stockfish128-logo]][website-link]
<h3>Stockfish</h3>
A free and strong UCI chess engine.
<br>
<strong>[Explore Stockfish docs »][wiki-link]</strong>
<br>
<br>
[Report bug][issue-link]
·
[Open a discussion][discussions-link]
·
[Discord][discord-link]
·
[Blog][website-blog-link]
[![Build][build-badge]][build-link]
[![License][license-badge]][license-link]
<br>
@@ -16,19 +31,16 @@
## Overview
[Stockfish][website-link] is a free, powerful UCI chess engine derived from
Glaurung 2.1. Stockfish is not a complete chess program and requires a UCI-compatible
graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard,
Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
Read the documentation for your GUI of choice for information about how to use
[Stockfish][website-link] is a **free and strong UCI chess engine** derived from
Glaurung 2.1 that analyzes chess positions and computes the optimal moves.
Stockfish **does not include a graphical user interface** (GUI) that is required
to display a chessboard and to make it easy to input moves. These GUIs are
developed independently from Stockfish and are available online. **Read the
documentation for your GUI** of choice for information about how to use
Stockfish with it.
The Stockfish engine features two evaluation functions for chess. The efficiently
updatable neural network (NNUE) based evaluation is the default and by far the strongest.
The classical evaluation based on handcrafted terms remains available. The strongest
network is integrated in the binary and downloaded automatically during the build process.
The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2,
avx2, neon, or similar).
See also the Stockfish [documentation][wiki-usage-link] for further usage help.
## Files
@@ -36,210 +48,47 @@ This distribution of Stockfish consists of the following files:
* [README.md][readme-link], the file you are currently reading.
* [Copying.txt][license-link], a text file containing the GNU General Public License
version 3.
* [Copying.txt][license-link], a text file containing the GNU General Public
License version 3.
* [AUTHORS][authors-link], a text file with the list of authors for the project.
* [src][src-link], a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems.
* [src][src-link], a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like systems.
* a file with the .nnue extension, storing the neural network for the NNUE evaluation.
Binary distributions will have this file embedded.
* a file with the .nnue extension, storing the neural network for the NNUE
evaluation. Binary distributions will have this file embedded.
## The UCI protocol and available options
## The UCI protocol
The Universal Chess Interface (UCI) is a standard protocol used to communicate with
a chess engine, and is the recommended way to do so for typical graphical user interfaces
(GUI) or chess tools. Stockfish implements the majority of its options as described
in [the UCI protocol][uci-link].
The [Universal Chess Interface][uci-link] (UCI) is a standard text-based protocol
used to communicate with a chess engine and is the recommended way to do so for
typical graphical user interfaces (GUI) or chess tools. Stockfish implements the
majority of its options.
Developers can see the default values for UCI options available in Stockfish by typing
`./stockfish uci` in a terminal, but the majority of users will typically see them and
change them via a chess GUI. This is a list of available UCI options in Stockfish:
Developers can see the default values for the UCI options available in Stockfish
by typing `./stockfish uci` in a terminal, but most users should typically use a
chess GUI to interact with Stockfish.
* #### Threads
The number of CPU threads used for searching a position. For best performance, set
this equal to the number of CPU cores available.
For more information on UCI or debug commands, see our [documentation][wiki-commands-link].
* #### Hash
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
## Compiling Stockfish
* #### Clear Hash
Clear the hash table.
Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions,
big-endian machines such as Power PC, and other platforms.
* #### Ponder
Let Stockfish ponder its next move while the opponent is thinking.
On Unix-like systems, it should be easy to compile Stockfish directly from the
source code with the included Makefile in the folder `src`. In general, it is
recommended to run `make help` to see a list of make targets with corresponding
descriptions.
* #### MultiPV
Output the N best lines (principal variations, PVs) when searching.
Leave at 1 for best performance.
* #### Use NNUE
Toggle between the NNUE and classical evaluation functions. If set to "true",
the network parameters must be available to load from file (see also EvalFile),
if they are not embedded in the binary.
* #### EvalFile
The name of the file of the NNUE evaluation parameters. Depending on the GUI the
filename might have to include the full path to the folder/directory that contains
the file. Other locations, such as the directory that contains the binary and the
working directory, are also searched.
* #### UCI_AnalyseMode
An option handled by your GUI.
* #### UCI_Chess960
An option handled by your GUI. If true, Stockfish will play Chess960.
* #### UCI_ShowWDL
If enabled, show approximate WDL statistics as part of the engine output.
These WDL numbers model expected game outcomes for a given evaluation and
game ply for engine self-play at fishtest LTC conditions (60+0.6s per game).
* #### UCI_LimitStrength
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
* #### UCI_Elo
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
* #### Skill Level
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
weaker move will be played.
* #### SyzygyPath
Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based
operating systems. Do not use spaces around the ";" or ":".
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
It is recommended to store .rtbw files on an SSD. There is no loss in storing
the .rtbz files on a regular HDD. It is recommended to verify all md5 checksums
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
lead to engine crashes.
* #### SyzygyProbeDepth
Minimum remaining search depth for which a position is probed. Set this option
to a higher value to probe less aggressively if you experience too much slowdown
(in terms of nps) due to tablebase probing.
* #### Syzygy50MoveRule
Disable to let fifty-move rule draws detected by Syzygy tablebase probes count
as wins or losses. This is useful for ICCF correspondence games.
* #### SyzygyProbeLimit
Limit Syzygy tablebase probing to positions with at most this many pieces left
(including kings and pawns).
* #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases.
* #### Slow Mover
Lower values will make Stockfish take less time in games, higher values will
make it think longer.
* #### nodestime
Tells the engine to use nodes searched instead of wall time to account for
elapsed time. Useful for engine testing.
* #### Debug Log File
Write all communication to and from the engine into a text file.
For developers the following non-standard commands might be of interest, mainly useful for debugging:
* #### bench *ttSize threads limit fenFile limitType evalType*
Performs a standard benchmark using various options. The signature of a version
(standard node count) is obtained using all defaults. `bench` is currently
`bench 16 1 13 default depth mixed`.
* #### compiler
Give information about the compiler and environment used for building a binary.
* #### d
Display the current position, with ascii art and fen.
* #### eval
Return the evaluation of the current position.
* #### export_net [filename]
Exports the currently loaded network to a file.
If the currently loaded network is the embedded network and the filename
is not specified then the network is saved to the file matching the name
of the embedded network, as defined in evaluate.h.
If the currently loaded network is not the embedded network (some net set
through the UCI setoption) then the filename parameter is required and the
network is saved into that file.
* #### flip
Flips the side to move.
## A note on classical evaluation versus NNUE evaluation
Both approaches assign a value to a position that is used in alpha-beta (PVS) search
to find the best move. The classical evaluation computes this value as a function
of various chess concepts, handcrafted by experts, tested and tuned using fishtest.
The NNUE evaluation computes this value with a neural network based on basic
inputs (e.g. piece positions only). The network is optimized and trained
on the evaluations of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository][nodchip-link] provided the first version of the needed tools
to train and develop the NNUE networks. Today, more advanced training tools are
available in [the nnue-pytorch repository][pytorch-link], while data generation tools
are available in [a dedicated branch][tools-link].
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation
results in much stronger playing strength, even if the nodes per second computed by
the engine is somewhat lower (roughly 80% of nps is typical).
Notes:
1) the NNUE evaluation depends on the Stockfish binary and the network parameter file
(see the EvalFile UCI option). Not every parameter file is compatible with a given
Stockfish binary, but the default value of the EvalFile UCI option is the name of a
network that is guaranteed to be compatible with that binary.
2) to use the NNUE evaluation, the additional data file with neural network parameters
needs to be available. Normally, this file is already embedded in the binary or it can
be downloaded. The filename for the default (recommended) net can be found as the default
value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue`
(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from
```
https://tests.stockfishchess.org/api/nn/[filename]
cd src
make -j build ARCH=x86-64-modern
```
replacing `[filename]` as needed.
## What to expect from the Syzygy tablebases?
If the engine is searching a position that is not in the tablebases (e.g.
a position with 8 pieces), it will access the tablebases during the search.
If the engine reports a very large score (typically 153.xx), this means
it has found a winning line into a tablebase position.
If the engine is given a position to search that is in the tablebases, it
will use the tablebases at the beginning of the search to preselect all
good moves, i.e. all moves that preserve the win or preserve the draw while
taking into account the 50-move rule.
It will then perform a search only on those moves. **The engine will not move
immediately**, unless there is only a single good move. **The engine likely
will not report a mate score, even if the position is known to be won.**
It is therefore clear that this behaviour is not identical to what one might
be used to with Nalimov tablebases. There are technical reasons for this
difference, the main technical reason being that Nalimov tablebases use the
DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the
DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
counter). This special metric is one of the reasons that the Syzygy tablebases are
more compact than Nalimov tablebases, while still storing all information
needed for optimal play and in addition being able to take into account
the 50-move rule.
Detailed compilation instructions for all platforms can be found in our
[documentation][wiki-compile-link].
## Stockfish on distributed memory systems
@@ -266,137 +115,74 @@ implementation that efficiently supports this. Some MPI implentations might bene
from leaving 1 core/thread free for these asynchronous communications, and might require
setting additional environment variables. ```mpirun``` should forward stdin/stdout
to ```rank 0``` only (e.g. ```srun --input=0 --output=0```).
Refer to your MPI documentation for more info.
Refer to your MPI documentation for more info.
## Large Pages
Stockfish supports large pages on Linux and Windows. Large pages make
the hash access more efficient, improving the engine speed, especially
on large hash sizes. Typical increases are 5..10% in terms of nodes per
second, but speed increases up to 30% have been measured. The support is
automatic. Stockfish attempts to use large pages when available and
will fall back to regular memory allocation when this is not the case.
### Support on Linux
Large page support on Linux is obtained by the Linux kernel
transparent huge pages functionality. Typically, transparent huge pages
are already enabled, and no configuration is needed.
### Support on Windows
The use of large pages requires "Lock Pages in Memory" privilege. See
[Enable the Lock Pages in Memory Option (Windows)][lockpages-link]
on how to enable this privilege, then run [RAMMap][rammap-link]
to double-check that large pages are used. We suggest that you reboot
your computer after you have enabled large pages, because long Windows
sessions suffer from memory fragmentation, which may prevent Stockfish
from getting large pages: a fresh session is better in this regard.
## Compiling Stockfish yourself from the sources
Stockfish has support for 32 or 64-bit CPUs, certain hardware
instructions, big-endian machines such as Power PC, and other platforms.
On Unix-like systems, it should be easy to compile Stockfish
directly from the source code with the included Makefile in the folder
`src`. In general it is recommended to run `make help` to see a list of make
targets with corresponding descriptions.
```
cd src
make help
make net
make build ARCH=x86-64-modern
```
When not using the Makefile to compile (for instance, with Microsoft MSVC) you
need to manually set/unset some switches in the compiler command line; see
file *types.h* for a quick reference.
When reporting an issue or a bug, please tell us which Stockfish version
and which compiler you used to create your executable. This information
can be found by typing the following command in a console:
```
./stockfish compiler
```
## Understanding the code base and participating in the project
Stockfish's improvement over the last decade has been a great community
effort. There are a few ways to help contribute to its growth.
## Contributing
### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate
your hardware resources by installing the [Fishtest Worker][worker-link]
and view the current tests on [Fishtest][fishtest-link].
Improving Stockfish requires a massive amount of testing. You can donate your
hardware resources by installing the [Fishtest Worker][worker-link] and viewing
the current tests on [Fishtest][fishtest-link].
### Improving the code
If you want to help improve the code, there are several valuable resources:
* [In this wiki,][programming-link] many techniques used in
In the [chessprogramming wiki][programming-link], many techniques used in
Stockfish are explained with a lot of background information.
The [section on Stockfish][programmingsf-link] describes many features
and techniques used by Stockfish. However, it is generic rather than
focused on Stockfish's precise implementation.
* [The section on Stockfish][programmingsf-link]
describes many features and techniques used by Stockfish. However, it is
generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub][github-link].
Discussions about Stockfish take place these days mainly in the [FishCooking][fishcooking-link]
group and on the [Stockfish Discord channel][discord-link].
The engine testing is done on [Fishtest][fishtest-link].
If you want to help improve Stockfish, please read this [guideline][guideline-link]
first, where the basics of Stockfish development are explained.
Discussions about Stockfish take place these days mainly in the Stockfish
[Discord server][discord-link]. This is also the best place to ask questions
about the codebase and how to improve it.
## Terms of use
Stockfish is free, and distributed under the **GNU General Public License version 3**
(GPL v3). Essentially, this means you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your website, selling
it (either by itself or as part of some bigger software package), or
using it as the starting point for a software project of your own.
Stockfish is free and distributed under the
[**GNU General Public License version 3**][license-link] (GPL v3). Essentially,
this means you are free to do almost exactly what you want with the program,
including distributing it among your friends, making it available for download
from your website, selling it (either by itself or as part of some bigger
software package), or using it as the starting point for a software project of
your own.
The only real limitation is that whenever you distribute Stockfish in
some way, you MUST always include the license and the full source code
(or a pointer to where the source code can be found) to generate the
exact binary you are distributing. If you make any changes to the
source code, these changes must also be made available under the GPL v3.
For full details, read the copy of the GPL v3 found in the file named
[*Copying.txt*][license-link].
The only real limitation is that whenever you distribute Stockfish in some way,
you MUST always include the license and the full source code (or a pointer to
where the source code can be found) to generate the exact binary you are
distributing. If you make any changes to the source code, these changes must
also be made available under GPL v3.
[authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS
[build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml
[commits-link]: https://github.com/official-stockfish/Stockfish/commits/master
[discord-link]: https://discord.gg/GWDRS3kU6R
[fishcooking-link]: https://groups.google.com/g/fishcooking
[issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml
[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new
[fishtest-link]: https://tests.stockfishchess.org/tests
[github-link]: https://github.com/official-stockfish/Stockfish
[guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test
[license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
[lockpages-link]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows
[nodchip-link]: https://github.com/nodchip/Stockfish
[programming-link]: https://www.chessprogramming.org/Main_Page
[programmingsf-link]: https://www.chessprogramming.org/Stockfish
[pytorch-link]: https://github.com/glinscott/nnue-pytorch
[rammap-link]: https://docs.microsoft.com/en-us/sysinternals/downloads/rammap
[readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md
[release-link]: https://github.com/official-stockfish/Stockfish/releases/latest
[src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src
[stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png
[tools-link]: https://github.com/official-stockfish/Stockfish/tree/tools
[uci-link]: https://www.shredderchess.com/download/div/uci.zip
[uci-link]: https://backscattering.de/chess/uci/
[website-link]: https://stockfishchess.org
[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview
[website-blog-link]: https://stockfishchess.org/blog/
[wiki-link]: https://github.com/official-stockfish/Stockfish/wiki
[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage
[wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source
[wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands
[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker
[build-badge]: https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github
[build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github
[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge
[discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord
[fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests%2Ffinished
+103 -96
View File
@@ -1,160 +1,167 @@
Contributors to Fishtest with >10,000 CPU hours, as of 2022-11-19.
Contributors to Fishtest with >10,000 CPU hours, as of 2023-06-20.
Thank you!
Username CPU Hours Games played
------------------------------------------------------------------
noobpwnftw 36475307 2748033975
technologov 14570711 760073590
noobpwnftw 37457426 2850540907
technologov 14135647 742892808
linrock 4423514 303254809
mlang 3026000 200065824
dew 1689222 100034318
grandphish2 1442171 86798057
okrout 1439985 133471766
pemo 1405374 44189811
linrock 1299003 28382783
TueRens 1163420 71159522
JojoM 897158 55177114
dew 1689162 100033738
okrout 1578136 148855886
pemo 1508508 48814305
grandphish2 1461406 91540343
TueRens 1194790 70400852
JojoM 947612 61773190
tvijlbrief 796125 51897690
sebastronomy 742434 38218524
mibere 703840 46867607
gvreuls 635982 40652394
oz 590763 41201352
sebastronomy 581517 23307132
cw 517915 34865769
fastgm 504266 30264740
CSU_Dynasty 479901 31846710
ctoks 433503 28180725
gvreuls 651026 42988582
oz 543438 39314736
cw 517858 34869755
fastgm 503862 30260818
leszek 467278 33514883
CSU_Dynasty 464940 31177118
ctoks 434416 28506889
crunchy 427035 27344275
leszek 416883 27493447
bcross 409982 28062127
velislav 345954 22232274
maximmasiutin 424795 26577722
bcross 415722 29060963
olafm 395922 32268020
rpngn 348378 24560289
velislav 342567 22138992
Fisherman 327231 21829379
mgrabiak 300612 20608380
Dantist 296386 18031762
mgrabiak 288928 18869896
rpngn 259965 16281463
robal 237653 15148350
ncfish1 231764 15275003
nordlandia 226923 14624832
nordlandia 246201 16189678
robal 241300 15656382
marrco 234581 17714473
ncfish1 227517 15233777
glinscott 208125 13277240
drabel 204167 13930674
mhoram 202894 12601997
bking_US 198894 11876016
thirdlife 198844 5453268
Thanar 179852 12365359
vdv 175544 9904472
armo9494 168201 11136452
spams 157128 10319326
marrco 151599 9551115
sqrt2 147963 9724586
vdbergh 137690 8971569
DesolatedDodo 146350 9536172
Calis007 143165 9478764
vdbergh 138650 9064413
CoffeeOne 137100 5024116
armo9494 136191 9460264
malala 136182 8002293
DesolatedDodo 135276 8657464
xoto 133759 9159372
davar 129023 8376525
DMBK 122960 8980062
dsmith 122059 7570238
amicic 119661 7938029
Data 113305 8220352
BrunoBanani 112960 7436849
CypressChess 108331 7759788
skiminki 106518 7062598
skiminki 107583 7218170
jcAEie 105675 8238962
MaZePallas 102823 6633619
sterni1971 100532 5880772
sunu 100167 7040199
zeryl 99331 6221261
thirdlife 99124 2242380
ElbertoOne 99028 7023771
DMBK 97572 6950312
Calis007 96779 5611552
cuistot 93111 5536500
cuistot 98853 6069816
bigpen0r 94809 6529203
brabos 92118 6186135
Wolfgang 91769 5720158
Wolfgang 91939 6105872
psk 89957 5984901
sschnee 88235 5268000
racerschmacer 85805 6122790
jcAEie 85527 5630616
Fifis 85722 5709729
Dubslow 84986 6042456
Vizvezdenec 83761 5344740
sschnee 83557 4853690
0x3C33 82614 5271253
BRAVONE 81239 5054681
Dubslow 78461 5042980
nssy 76497 5259388
jromang 76106 5236025
teddybaer 75125 5407666
yurikvelo 73933 5031096
tolkki963 73885 4721430
tolkki963 74762 5149662
megaman7de 74351 4940352
Wencey 74181 4711488
Pking_cda 73776 5293873
Bobo1239 71675 4860987
yurikvelo 73150 5004382
markkulix 72607 5304642
Bobo1239 70579 4794999
solarlight 70517 5028306
dv8silencer 70287 3883992
Gelma 69304 3980932
manap 66273 4121774
megaman7de 65419 4120200
markkulix 65331 4114860
bigpen0r 64932 4683883
tinker 64333 4268790
qurashee 61208 3429862
AGI 58325 4258646
Mineta 59357 4418202
Spprtr 58723 3911011
AGI 58147 4325994
robnjr 57262 4053117
Freja 56938 3733019
MaxKlaxxMiner 56879 3423958
MarcusTullius 56746 3762951
ttruscott 56010 3680085
rkl 55132 4164467
renouve 53811 3501516
Spprtr 52736 3410019
javran 53785 4627608
finfish 51360 3370515
eva42 51272 3599691
eastorwest 51117 3454811
rap 49985 3219146
unixwizard 49734 2536230
pb00067 49727 3298270
pb00067 49733 3298934
OuaisBla 48626 3445134
ronaldjerum 47654 3240695
biffhero 46564 3111352
GPUex 45861 2926502
Fifis 45843 3088497
oryx 45578 3493978
VoyagerOne 45476 3452465
Wencey 44943 2654490
jmdana 44893 3065205
maposora 44597 4039578
oryx 44570 3454238
speedycpu 43842 3003273
jbwiebe 43305 2805433
GPUex 42378 3133332
Antihistamine 41788 2761312
mhunt 41735 2691355
olafm 41277 3284344
homyur 39893 2850481
gri 39871 2515779
MarcusTullius 38303 2251097
Garf 37741 2999686
kdave 37424 2557406
SC 37299 2731694
csnodgrass 36207 2688994
jmdana 36157 2210661
strelock 34716 2074055
szupaw 34102 2880346
EthanOConnor 33370 2090311
slakovv 32915 2021889
gopeto 31669 2060958
Gelma 31771 1551204
gopeto 31671 2060990
kdave 31157 2198362
manapbk 30987 1810399
Prcuvu 30377 2170122
anst 30301 2190091
jkiiski 30136 1904470
spcc 30135 1903728
spcc 29925 1901692
hyperbolic.tom 29840 2017394
xwziegtm 29763 2347412
chuckstablers 29659 2093438
Pyafue 29650 1902349
belzedar94 28846 1811530
OuaisBla 27636 1578800
chriswk 26902 1868317
xwziegtm 26897 2124586
achambord 26582 1767323
Patrick_G 26276 1801617
yorkman 26193 1992080
Ulysses 25289 1674274
Ulysses 25288 1689730
SFTUser 25182 1675689
nabildanial 24942 1519409
Sharaf_DG 24765 1786697
Maxim 24705 1502062
rodneyc 24376 1416402
agg177 23890 1395014
Ente 23747 1674582
Karpovbot 23629 1313186
Goatminola 23763 1956036
Ente 23639 1671638
Jopo12321 23467 1483172
JanErik 23408 1703875
Isidor 23388 1680691
Norabor 23371 1603244
cisco2015 22934 1763773
cisco2015 22920 1763301
jsys14 22824 1591906
Zirie 22542 1472937
team-oh 22272 1636708
Roady 22220 1465606
@@ -165,96 +172,96 @@ xor12 21628 1680365
dex 21612 1467203
nesoneg 21494 1463031
user213718 21454 1404128
AndreasKrug 21227 1577833
sphinx 21211 1384728
AndreasKrug 21097 1634811
jjoshua2 21001 1423089
Zake9298 20938 1565848
horst.prack 20878 1465656
jsys14 20729 1221010
0xB00B1ES 20590 1208666
j3corre 20405 941444
Adrian.Schmidt123 20316 1281436
bonsi 20022 1300682
wei 19973 1745989
dapper 19754 1167758
Zake9298 19745 1458416
notchris 19958 1800128
Serpensin 19840 1697528
Gaster319 19712 1677310
fishtester 19617 1257388
rstoesser 19569 1293588
eudhan 19274 1283717
votoanthuan 19108 1609992
vulcan 18871 1729392
Jopo12321 18803 1036284
Karpovbot 18766 1053178
qoo_charly_cai 18543 1284937
jundery 18445 1115855
ville 17883 1384026
5t0ckf15hTr4in3r 17809 1105858
chris 17698 1487385
dju 17697 994333
purplefishies 17595 1092533
dju 17414 981289
iisiraider 17275 1049015
DragonLord 17014 1162790
Karby 16457 1010138
Goatminola 16278 1145026
redstone59 16842 1461780
Alb11747 16787 1213926
IgorLeMasson 16064 1147232
Gaster319 16056 1109070
redstone59 15953 1161664
Karby 15982 979610
scuzzi 15757 968735
ako027ako 15671 1173203
Nikolay.IT 15154 1068349
Andrew Grant 15114 895539
Naven94 15054 834762
OssumOpossum 14857 1007129
qoo_charly_cai 14490 847865
ZacHFX 14783 1021842
enedene 14476 905279
szupaw 14252 929130
bpfliegel 14233 882523
mpx86 14019 759568
jpulman 13982 870599
Skiff84 13826 721996
crocogoat 13803 1117422
Nesa92 13786 1114691
joster 13710 946160
mbeier 13650 1044928
Hjax 13535 915487
Nullvalue 13468 1140498
Dark_wizzie 13422 1007152
Rudolphous 13244 883140
pirt 13100 1009897
Machariel 13010 863104
infinigon 12991 943216
pirt 12925 985437
Skiff84 12923 649994
mabichito 12903 749391
thijsk 12886 722107
AdrianSA 12860 804972
Flopzee 12698 894821
korposzczur 12606 838168
fatmurphy 12547 853210
woutboat 12419 836696
SapphireBrand 12416 969604
Oakwen 12406 840961
Oakwen 12399 844109
deflectooor 12386 579392
modolief 12386 896470
Farseer 12249 694108
Jackfish 12213 805008
pgontarz 12151 848794
dbernier 12103 860824
getraideBFF 12072 1024966
stocky 11954 699440
mschmidt 11941 803401
MooTheCow 11871 773654
Jackfish 11867 773550
dbernier 11705 821780
MooTheCow 11870 773598
FormazChar 11766 885707
whelanh 11557 245188
Maxim 11543 836024
Nullvalue 11534 731410
icewulf 11528 650470
FormazChar 11523 861599
3cho 11494 1031076
infinity 11470 727027
aga 11412 695127
torbjo 11395 729145
Thomas A. Anderson 11372 732094
savage84 11358 670860
ali-al-zhrani 11272 781310
d64 11263 789184
Bourbaki 11108 709144
ali-al-zhrani 11245 779246
snicolet 11106 869170
Alb11747 10855 696920
dapper 11032 771402
ols 10947 624903
Karmatron 10828 677458
basepi 10637 744851
Cubox 10621 826448
Karmatron 10616 674818
michaelrpg 10509 739239
OIVAS7572 10420 995586
Garruk 10348 704905
jojo2357 10419 929708
WoodMan777 10380 873720
Garruk 10365 706465
dzjp 10343 732529
ols 10259 570669
+110 -76
View File
@@ -1,5 +1,5 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
# Copyright (C) 2004-2023 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
@@ -69,33 +69,34 @@ VPATH = syzygy:nnue:nnue/features
### Section 2. High-level Configuration
### ==========================================================================
#
# flag --- Comp switch --- Description
# flag --- Comp switch --- Description
# ----------------------------------------------------------------------------
#
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# sanitize = none/<sanitizer> ... (-fsanitize )
# --- ( undefined ) --- enable undefined behavior checks
# --- ( thread ) --- enable threading error checks
# --- ( address ) --- enable memory access checks
# --- ...etc... --- see compiler documentation for supported sanitizers
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
# mmx = yes/no --- -mmmx --- Use Intel MMX instructions
# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# --- ( undefined ) --- enable undefined behavior checks
# --- ( thread ) --- enable threading error checks
# --- ( address ) --- enable memory access checks
# --- ...etc... --- see compiler documentation for supported sanitizers
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
# mmx = yes/no --- -mmmx --- Use Intel MMX instructions
# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# mpi = yes/no --- -DUSE_MPI --- Use Message Passing Interface
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
@@ -117,7 +118,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \
armv7 armv7-neon armv8 apple-silicon general-64 general-32 riscv64))
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64))
SUPPORTED_ARCH=true
else
SUPPORTED_ARCH=false
@@ -142,6 +143,7 @@ vnni256 = no
vnni512 = no
neon = no
mpi = no
dotprod = no
arm_version = 0
STRIP = strip
@@ -310,11 +312,21 @@ ifeq ($(ARCH),armv8)
arm_version = 8
endif
ifeq ($(ARCH),armv8-dotprod)
arch = armv8
prefetch = yes
popcnt = yes
neon = yes
dotprod = yes
arm_version = 8
endif
ifeq ($(ARCH),apple-silicon)
arch = arm64
prefetch = yes
popcnt = yes
neon = yes
dotprod = yes
arm_version = 8
endif
@@ -368,7 +380,7 @@ endif
ifeq ($(COMP),gcc)
comp=gcc
CXX=g++
CXXFLAGS += -pedantic -Wextra -Wshadow
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations
ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
ifeq ($(OS),Android)
@@ -412,13 +424,14 @@ ifeq ($(COMP),mingw)
CXX=i686-w64-mingw32-c++-posix
endif
endif
CXXFLAGS += -pedantic -Wextra -Wshadow
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations
endif
ifeq ($(COMP),icc)
comp=icc
CXX=icpc
CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi
ifeq ($(COMP),icx)
comp=icx
CXX=icpx
CXXFLAGS += --intel -pedantic -Wextra -Wshadow -Wmissing-prototypes \
-Wconditional-uninitialized -Wabi -Wdeprecated
endif
ifeq ($(COMP),clang)
@@ -428,7 +441,8 @@ ifeq ($(COMP),clang)
CXX=x86_64-w64-mingw32-clang++
endif
CXXFLAGS += -pedantic -Wextra -Wshadow
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes \
-Wconditional-uninitialized
ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),)
ifeq ($(target_windows),)
@@ -488,9 +502,9 @@ ifeq ($(COMP),ndk)
LDFLAGS += -static-libstdc++ -pie -lm -latomic
endif
ifeq ($(comp),icc)
profile_make = icc-profile-make
profile_use = icc-profile-use
ifeq ($(comp),icx)
profile_make = icx-profile-make
profile_use = icx-profile-use
else ifeq ($(comp),clang)
profile_make = clang-profile-make
profile_use = clang-profile-use
@@ -514,7 +528,7 @@ endif
### Sometimes gcc is really clang
ifeq ($(COMP),gcc)
gccversion = $(shell $(CXX) --version)
gccversion = $(shell $(CXX) --version 2>/dev/null)
gccisclang = $(findstring clang,$(gccversion))
ifneq ($(gccisclang),)
profile_make = clang-profile-make
@@ -561,7 +575,7 @@ ifeq ($(optimize),yes)
endif
ifeq ($(KERNEL),Darwin)
ifeq ($(comp),$(filter $(comp),clang icc))
ifeq ($(comp),$(filter $(comp),clang icx))
CXXFLAGS += -mdynamic-no-pic
endif
@@ -573,7 +587,10 @@ ifeq ($(optimize),yes)
endif
ifeq ($(comp),clang)
CXXFLAGS += -fexperimental-new-pass-manager
clangmajorversion = $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.)
ifeq ($(shell expr $(clangmajorversion) \< 16),1)
CXXFLAGS += -fexperimental-new-pass-manager
endif
endif
endif
@@ -594,8 +611,6 @@ endif
ifeq ($(popcnt),yes)
ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64))
CXXFLAGS += -DUSE_POPCNT
else ifeq ($(comp),icc)
CXXFLAGS += -msse3 -DUSE_POPCNT
else
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
endif
@@ -604,63 +619,63 @@ endif
### 3.6 SIMD architectures
ifeq ($(avx2),yes)
CXXFLAGS += -DUSE_AVX2
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx2 -mbmi
endif
endif
ifeq ($(avxvnni),yes)
CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavxvnni
endif
endif
ifeq ($(avx512),yes)
CXXFLAGS += -DUSE_AVX512
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw
endif
endif
ifeq ($(vnni256),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256
endif
endif
ifeq ($(vnni512),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=512
endif
endif
ifeq ($(sse41),yes)
CXXFLAGS += -DUSE_SSE41
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -msse4.1
endif
endif
ifeq ($(ssse3),yes)
CXXFLAGS += -DUSE_SSSE3
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mssse3
endif
endif
ifeq ($(sse2),yes)
CXXFLAGS += -DUSE_SSE2
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -msse2
endif
endif
ifeq ($(mmx),yes)
CXXFLAGS += -DUSE_MMX
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mmmx
endif
endif
@@ -676,24 +691,28 @@ ifeq ($(neon),yes)
endif
endif
ifeq ($(dotprod),yes)
CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD
endif
### 3.7 pext
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mbmi2
endif
endif
### 3.7.1 Try to include git commit sha for versioning
GIT_SHA = $(shell git rev-parse --short HEAD 2>/dev/null)
GIT_SHA = $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8)
ifneq ($(GIT_SHA), )
CXXFLAGS += -DGIT_SHA=\"$(GIT_SHA)\"
CXXFLAGS += -DGIT_SHA=$(GIT_SHA)
endif
### 3.7.2 Try to include git commit date for versioning
GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null)
ifneq ($(GIT_DATE), )
CXXFLAGS += -DGIT_DATE=\"$(GIT_DATE)\"
CXXFLAGS += -DGIT_DATE=$(GIT_DATE)
endif
### 3.8 Link Time Optimization
@@ -701,8 +720,11 @@ endif
### needs access to the optimization flags.
ifeq ($(optimize),yes)
ifeq ($(debug), no)
ifeq ($(comp),clang)
ifeq ($(comp),$(filter $(comp),clang icx))
CXXFLAGS += -flto=full
ifeq ($(comp),icx)
CXXFLAGS += -fwhole-program-vtables
endif
ifeq ($(target_windows),yes)
CXXFLAGS += -fuse-ld=lld
endif
@@ -758,19 +780,19 @@ help:
@echo "Supported targets:"
@echo ""
@echo "help > Display architecture details"
@echo "build > Standard build"
@echo "profile-build > standard build with profile-guided optimization"
@echo "build > skip profile-guided optimization"
@echo "net > Download the default nnue net"
@echo "profile-build > Faster build (with profile-guided optimization)"
@echo "strip > Strip executable"
@echo "install > Install executable"
@echo "clean > Clean up"
@echo ""
@echo "Supported archs:"
@echo ""
@echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide"
@echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide"
@echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support"
@echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide"
@echo "x86-64-avx512 > x86 64-bit with avx512 support"
@echo "x86-64-avxvnni > x86 64-bit with avxvnni support"
@echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support"
@echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
@echo "x86-64-avx2 > x86 64-bit with avx2 support"
@echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support"
@@ -786,6 +808,7 @@ help:
@echo "armv7 > ARMv7 32-bit"
@echo "armv7-neon > ARMv7 32-bit with popcnt and neon"
@echo "armv8 > ARMv8 64-bit with popcnt and neon"
@echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support"
@echo "e2k > Elbrus 2000"
@echo "apple-silicon > Apple silicon ARM64"
@echo "general-64 > unspecified 64-bit"
@@ -797,17 +820,18 @@ help:
@echo "gcc > Gnu compiler (default)"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@echo "icc > Intel compiler"
@echo "icx > Intel oneAPI DPC++/C++ Compiler"
@echo "ndk > Google NDK to cross-compile for Android"
@echo ""
@echo "Simple examples. If you don't know what to do, you likely want to run: "
@echo "Simple examples. If you don't know what to do, you likely want to run one of: "
@echo ""
@echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)"
@echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)"
@echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems "
@echo "make -j profile-build ARCH=x86-64-modern # A more portable compile for 64-bit systems "
@echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems "
@echo ""
@echo "Advanced examples, for experienced users looking for performance: "
@echo "Advanced examples, for experienced users: "
@echo ""
@echo "make help ARCH=x86-64-bmi2"
@echo "make -j profile-build ARCH=x86-64-bmi2"
@echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0"
@echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
@echo ""
@@ -822,8 +846,10 @@ endif
.PHONY: help build profile-build strip install clean net objclean profileclean \
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make FORCE
config-sanity \
icx-profile-use icx-profile-make \
gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make FORCE
build: net config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
@@ -863,7 +889,7 @@ net:
$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet))
$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
@if [ "x$(curl_or_wget)" = "x" ]; then \
echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \
echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \
fi
$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi))
@if [ "x$(shasum_command)" = "x" ]; then \
@@ -874,7 +900,9 @@ net:
echo "$(nnuenet) available."; \
else \
if [ "x$(curl_or_wget)" != "x" ]; then \
echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
else \
echo "No net found and download not possible"; exit 1;\
fi; \
fi; \
if [ "x$(shasum_command)" != "x" ]; then \
@@ -937,7 +965,9 @@ config-sanity: net
@echo "vnni512: '$(vnni512)'"
@echo "neon: '$(neon)'"
@echo "mpi: '$(mpi)'"
@echo "dotprod: '$(dotprod)'"
@echo "arm_version: '$(arm_version)'"
@echo "target_windows: '$(target_windows)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@@ -966,7 +996,7 @@ config-sanity: net
@test "$(vnni256)" = "yes" || test "$(vnni256)" = "no"
@test "$(vnni512)" = "yes" || test "$(vnni512)" = "no"
@test "$(neon)" = "yes" || test "$(neon)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
@test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
|| test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang"
$(EXE): $(OBJS)
@@ -1004,18 +1034,22 @@ gcc-profile-use:
EXTRALDFLAGS='-lgcov' \
all
icc-profile-make:
@mkdir -p profdir
icx-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
EXTRACXXFLAGS='-fprofile-instr-generate ' \
EXTRALDFLAGS=' -fprofile-instr-generate' \
all
icc-profile-use:
icx-profile-use:
$(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
EXTRALDFLAGS='-fprofile-use ' \
all
.depend: $(SRCS)
-@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null
ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean config-sanity))
-include .depend
endif
+3 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "benchmark.h"
#include <fstream>
#include <iostream>
#include <istream>
+34
View File
@@ -0,0 +1,34 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BENCHMARK_H_INCLUDED
#define BENCHMARK_H_INCLUDED
#include <iosfwd>
#include <string>
#include <vector>
namespace Stockfish {
class Position;
std::vector<std::string> setup_bench(const Position&, std::istream&);
} // namespace Stockfish
#endif // #ifndef BENCHMARK_H_INCLUDED
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -27,7 +27,6 @@ namespace Stockfish {
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
@@ -82,9 +81,6 @@ void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i)
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = (1ULL << s);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
+2 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -74,7 +74,6 @@ constexpr Bitboard KingFlank[FILE_NB] = {
extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
@@ -108,7 +107,7 @@ extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(is_ok(s));
return SquareBB[s];
return (1ULL << s);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+27 -40
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@
#include "timeman.h"
#include "uci.h"
#include "incbin/incbin.h"
#include "nnue/evaluate_nnue.h"
// Macro to embed the default efficiently updatable neural network (NNUE) file
// data in the engine binary (using incbin.h, by Dale Weiler).
@@ -83,20 +83,18 @@ namespace Eval {
eval_file = EvalFileDefaultName;
#if defined(DEFAULT_NNUE_DIRECTORY)
#define stringify2(x) #x
#define stringify(x) stringify2(x)
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
#else
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory };
#endif
for (string directory : dirs)
for (const string& directory : dirs)
if (currentEvalFileName != eval_file)
{
if (directory != "<internal>")
{
ifstream stream(directory + eval_file, ios::binary);
if (load_eval(eval_file, stream))
if (NNUE::load_eval(eval_file, stream))
currentEvalFileName = eval_file;
}
@@ -112,7 +110,7 @@ namespace Eval {
(void) gEmbeddedNNUEEnd; // Silence warning on unused variable
istream stream(&buffer);
if (load_eval(eval_file, stream))
if (NNUE::load_eval(eval_file, stream))
currentEvalFileName = eval_file;
}
}
@@ -163,24 +161,24 @@ namespace Trace {
Score scores[TERM_NB][COLOR_NB];
double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; }
static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; }
void add(int idx, Color c, Score s) {
static void add(int idx, Color c, Score s) {
scores[idx][c] = s;
}
void add(int idx, Score w, Score b = SCORE_ZERO) {
static void add(int idx, Score w, Score b = SCORE_ZERO) {
scores[idx][WHITE] = w;
scores[idx][BLACK] = b;
}
std::ostream& operator<<(std::ostream& os, Score s) {
static std::ostream& operator<<(std::ostream& os, Score s) {
os << std::setw(5) << to_cp(mg_value(s)) << " "
<< std::setw(5) << to_cp(eg_value(s));
return os;
}
std::ostream& operator<<(std::ostream& os, Term t) {
static std::ostream& operator<<(std::ostream& os, Term t) {
if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL)
os << " ---- ----" << " | " << " ---- ----";
@@ -197,8 +195,8 @@ using namespace Trace;
namespace {
// Threshold for lazy and space evaluation
constexpr Value LazyThreshold1 = Value(3631);
constexpr Value LazyThreshold2 = Value(2084);
constexpr Value LazyThreshold1 = Value(3622);
constexpr Value LazyThreshold2 = Value(1962);
constexpr Value SpaceThreshold = Value(11551);
// KingAttackWeights[PieceType] contains king attack weights by piece type
@@ -392,10 +390,10 @@ namespace {
template<Tracing T> template<Color Us, PieceType Pt>
Score Evaluation<T>::pieces() {
constexpr Color Them = ~Us;
constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
constexpr Color Them = ~Us;
[[maybe_unused]] constexpr Direction Down = -pawn_push(Us);
[[maybe_unused]] constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
Bitboard b1 = pos.pieces(Us, Pt);
Bitboard b, bb;
Score score = SCORE_ZERO;
@@ -434,7 +432,7 @@ namespace {
int mob = popcount(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt - 2][mob];
if (Pt == BISHOP || Pt == KNIGHT)
if constexpr (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if the piece is on an outpost square or can reach one
// Bonus for knights (UncontestedOutpost) if few relevant targets
@@ -1052,52 +1050,41 @@ make_v:
/// evaluate() is the evaluator for the outer world. It returns a static
/// evaluation of the position from the point of view of the side to move.
Value Eval::evaluate(const Position& pos, int* complexity) {
Value Eval::evaluate(const Position& pos) {
assert(!pos.checkers());
Value v;
Value psq = pos.psq_eg_stm();
// We use the much less accurate but faster Classical eval when the NNUE
// option is set to false. Otherwise we use the NNUE eval unless the
// PSQ advantage is decisive and several pieces remain. (~3 Elo)
bool useClassical = !useNNUE || (pos.count<ALL_PIECES>() > 7 && abs(psq) > 1760);
// PSQ advantage is decisive. (~4 Elo at STC, 1 Elo at LTC)
bool useClassical = !useNNUE || abs(psq) > 2048;
if (useClassical)
v = Evaluation<NO_TRACE>(pos).value();
else
{
int nnueComplexity;
int scale = 1064 + 106 * pos.non_pawn_material() / 5120;
int npm = pos.non_pawn_material() / 64;
Color stm = pos.side_to_move();
Value optimism = pos.this_thread()->optimism[stm];
Value nnue = NNUE::evaluate(pos, true, &nnueComplexity);
// Blend nnue complexity with (semi)classical complexity
nnueComplexity = ( 416 * nnueComplexity
+ 424 * abs(psq - nnue)
+ (optimism > 0 ? int(optimism) * int(psq - nnue) : 0)
) / 1024;
// Return hybrid NNUE complexity to caller
if (complexity)
*complexity = nnueComplexity;
optimism = optimism * (269 + nnueComplexity) / 256;
v = (nnue * scale + optimism * (scale - 754)) / 1024;
// Blend optimism with nnue complexity and (semi)classical complexity
optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512;
v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024;
}
// Damp down the evaluation linearly when shuffling
v = v * (195 - pos.rule50_count()) / 211;
v = v * (200 - pos.rule50_count()) / 214;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
// When not using NNUE, return classical complexity to caller
if (complexity && (!useNNUE || useClassical))
*complexity = abs(v - psq);
return v;
}
+3 -10
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@ class Position;
namespace Eval {
std::string trace(Position& pos);
Value evaluate(const Position& pos, int* complexity = nullptr);
Value evaluate(const Position& pos);
extern bool useNNUE;
extern std::string currentEvalFileName;
@@ -39,20 +39,13 @@ namespace Eval {
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-ad9b42354671.nnue"
#define EvalFileDefaultName "nn-5af11540bbfe.nnue"
namespace NNUE {
std::string trace(Position& pos);
Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr);
void init();
void verify();
bool load_eval(std::string name, std::istream& stream);
bool save_eval(std::ostream& stream);
bool save_eval(const std::optional<std::string>& filename);
} // namespace NNUE
} // namespace Eval
Executable → Regular
View File
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ namespace Stockfish::Material {
/// Material::Entry contains various information about a material configuration.
/// It contains a material imbalance evaluation, a function pointer to a special
/// endgame evaluation function (which in most cases is NULL, meaning that the
/// endgame evaluation function (which in most cases is nullptr, meaning that the
/// standard evaluation function will be used), and scale factors.
///
/// The scale factors are used to scale the evaluation score up or down. For
@@ -62,7 +62,7 @@ struct Entry {
uint8_t factor[COLOR_NB];
};
typedef HashTable<Entry, 8192> Table;
using Table = HashTable<Entry, 8192>;
Entry* probe(const Position& pos);
+137 -37
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,21 +32,26 @@
// the calls at compile time), try to load them at runtime. To do this we need
// first to define the corresponding function pointers.
extern "C" {
typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
typedef WORD(*fun5_t)();
using fun1_t = bool(*)(LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
using fun2_t = bool(*)(USHORT, PGROUP_AFFINITY);
using fun3_t = bool(*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
using fun4_t = bool(*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
using fun5_t = WORD(*)();
using fun6_t = bool(*)(HANDLE, DWORD, PHANDLE);
using fun7_t = bool(*)(LPCSTR, LPCSTR, PLUID);
using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD);
}
#endif
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string_view>
#include <vector>
#include <cstdlib>
#if defined(__linux__) && !defined(__ANDROID__)
#include <stdlib.h>
@@ -68,7 +73,7 @@ namespace Stockfish {
namespace {
/// Version number or dev.
const string version = "15.1";
constexpr string_view version = "16";
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
@@ -151,13 +156,13 @@ string engine_info(bool to_uci) {
stringstream ss;
ss << "Stockfish " << version << setfill('0');
if (version == "dev")
if constexpr (version == "dev")
{
ss << "-";
#ifdef GIT_DATE
ss << GIT_DATE;
ss << stringify(GIT_DATE);
#else
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
constexpr string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year;
stringstream date(__DATE__); // From compiler, format is "Sep 21 2008"
@@ -168,7 +173,7 @@ string engine_info(bool to_uci) {
ss << "-";
#ifdef GIT_SHA
ss << GIT_SHA;
ss << stringify(GIT_SHA);
#else
ss << "nogit";
#endif
@@ -185,8 +190,6 @@ string engine_info(bool to_uci) {
std::string compiler_info() {
#define stringify2(x) #x
#define stringify(x) stringify2(x)
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
/// Predefined macros hell:
@@ -298,21 +301,94 @@ std::string compiler_info() {
/// Debug functions used mainly to collect run-time statistics
static std::atomic<int64_t> hits[2], means[2];
constexpr int MaxDebugSlots = 32;
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
namespace {
template<size_t N>
struct DebugInfo {
std::atomic<int64_t> data[N] = { 0 };
constexpr inline std::atomic<int64_t>& operator[](int index) { return data[index]; }
};
DebugInfo<2> hit[MaxDebugSlots];
DebugInfo<2> mean[MaxDebugSlots];
DebugInfo<3> stdev[MaxDebugSlots];
DebugInfo<6> correl[MaxDebugSlots];
} // namespace
void dbg_hit_on(bool cond, int slot) {
++hit[slot][0];
if (cond)
++hit[slot][1];
}
void dbg_mean_of(int64_t value, int slot) {
++mean[slot][0];
mean[slot][1] += value;
}
void dbg_stdev_of(int64_t value, int slot) {
++stdev[slot][0];
stdev[slot][1] += value;
stdev[slot][2] += value * value;
}
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
++correl[slot][0];
correl[slot][1] += value1;
correl[slot][2] += value1 * value1;
correl[slot][3] += value2;
correl[slot][4] += value2 * value2;
correl[slot][5] += value1 * value2;
}
void dbg_print() {
if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
int64_t n;
auto E = [&n](int64_t x) { return double(x) / n; };
auto sqr = [](double x) { return x * x; };
if (means[0])
cerr << "Total " << means[0] << " Mean "
<< (double)means[1] / means[0] << endl;
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = hit[i][0]))
std::cerr << "Hit #" << i
<< ": Total " << n << " Hits " << hit[i][1]
<< " Hit Rate (%) " << 100.0 * E(hit[i][1])
<< std::endl;
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = mean[i][0]))
{
std::cerr << "Mean #" << i
<< ": Total " << n << " Mean " << E(mean[i][1])
<< std::endl;
}
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = stdev[i][0]))
{
double r = sqrtl(E(stdev[i][2]) - sqr(E(stdev[i][1])));
std::cerr << "Stdev #" << i
<< ": Total " << n << " Stdev " << r
<< std::endl;
}
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = correl[i][0]))
{
double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3]))
/ ( sqrtl(E(correl[i][2]) - sqr(E(correl[i][1])))
* sqrtl(E(correl[i][4]) - sqr(E(correl[i][3]))));
std::cerr << "Correl. #" << i
<< ": Total " << n << " Coefficient " << r
<< std::endl;
}
}
@@ -373,8 +449,10 @@ void* std_aligned_alloc(size_t alignment, size_t size) {
#if defined(POSIXALIGNEDALLOC)
void *mem;
return posix_memalign(&mem, alignment, size) ? nullptr : mem;
#elif defined(_WIN32)
#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64)
return _mm_malloc(size, alignment);
#elif defined(_WIN32)
return _aligned_malloc(size, alignment);
#else
return std::aligned_alloc(alignment, size);
#endif
@@ -384,8 +462,10 @@ void std_aligned_free(void* ptr) {
#if defined(POSIXALIGNEDALLOC)
free(ptr);
#elif defined(_WIN32)
#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64)
_mm_free(ptr);
#elif defined(_WIN32)
_aligned_free(ptr);
#else
free(ptr);
#endif
@@ -409,11 +489,30 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize
if (!largePageSize)
return nullptr;
// We need SeLockMemoryPrivilege, so try to enable it for the process
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
// Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges
HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll"));
if (!hAdvapi32)
hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
auto fun6 = (fun6_t)(void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken");
if (!fun6)
return nullptr;
auto fun7 = (fun7_t)(void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA");
if (!fun7)
return nullptr;
auto fun8 = (fun8_t)(void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges");
if (!fun8)
return nullptr;
if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
// We need SeLockMemoryPrivilege, so try to enable it for the process
if (!fun6( // OpenProcessToken()
GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
return nullptr;
if (fun7( // LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid)
nullptr, "SeLockMemoryPrivilege", &luid))
{
TOKEN_PRIVILEGES tp { };
TOKEN_PRIVILEGES prevTp { };
@@ -425,17 +524,18 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
// we still need to query GetLastError() to ensure that the privileges were actually obtained.
if (AdjustTokenPrivileges(
if (fun8( // AdjustTokenPrivileges()
hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
GetLastError() == ERROR_SUCCESS)
{
// Round up size to full pages and allocate
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
mem = VirtualAlloc(
NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
// Privilege no longer needed, restore previous state
AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
fun8( // AdjustTokenPrivileges ()
hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr);
}
}
@@ -453,7 +553,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
// Fall back to regular, page aligned, allocation if necessary
if (!mem)
mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
return mem;
}
@@ -517,7 +617,7 @@ void bindThisThread(size_t) {}
/// API and returns the best node id for the thread with index idx. Original
/// code from Texel by Peter Österlund.
int best_node(size_t idx) {
static int best_node(size_t idx) {
int threads = 0;
int nodes = 0;
@@ -526,7 +626,7 @@ int best_node(size_t idx) {
DWORD byteOffset = 0;
// Early exit if the needed API is not available at runtime
HMODULE k32 = GetModuleHandle("Kernel32.dll");
HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll"));
auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx");
if (!fun1)
return -1;
@@ -596,7 +696,7 @@ void bindThisThread(size_t idx) {
return;
// Early exit if the needed API are not available at runtime
HMODULE k32 = GetModuleHandle("Kernel32.dll");
HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll"));
auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity");
auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2");
+10 -32
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,9 @@
#include "types.h"
#define stringify2(x) #x
#define stringify(x) stringify2(x)
namespace Stockfish {
std::string engine_info(bool to_uci = false);
@@ -39,12 +42,13 @@ void std_aligned_free(void* ptr);
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
void aligned_large_pages_free(void* mem); // nop if mem == nullptr
void dbg_hit_on(bool b);
void dbg_hit_on(bool c, bool b);
void dbg_mean_of(int v);
void dbg_hit_on(bool cond, int slot = 0);
void dbg_mean_of(int64_t value, int slot = 0);
void dbg_stdev_of(int64_t value, int slot = 0);
void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0);
void dbg_print();
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
inline TimePoint now() {
return std::chrono::duration_cast<std::chrono::milliseconds>
@@ -85,32 +89,6 @@ static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
static inline const bool IsLittleEndian = (Le.c[0] == 4);
// RunningAverage : a class to calculate a running average of a series of values.
// For efficiency, all computations are done with integers.
class RunningAverage {
public:
// Reset the running average to rational value p / q
void set(int64_t p, int64_t q)
{ average = p * PERIOD * RESOLUTION / q; }
// Update average with value v
void update(int64_t v)
{ average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; }
// Test if average is strictly greater than rational a / b
bool is_greater(int64_t a, int64_t b) const
{ return b * average > a * (PERIOD * RESOLUTION); }
int64_t value() const
{ return average / (PERIOD * RESOLUTION); }
private :
static constexpr int64_t PERIOD = 4096;
static constexpr int64_t RESOLUTION = 1024;
int64_t average;
};
template <typename T, std::size_t MaxSize>
class ValueList {
@@ -164,7 +142,7 @@ public:
inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
#if defined(__GNUC__) && defined(IS_64BIT)
__extension__ typedef unsigned __int128 uint128;
__extension__ using uint128 = unsigned __int128;
return ((uint128)a * (uint128)b) >> 64;
#else
uint64_t aL = (uint32_t)a, aH = a >> 32;
+22 -14
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,13 +25,21 @@ namespace Stockfish {
namespace {
template<GenType Type, Direction D>
ExtMove* make_promotions(ExtMove* moveList, Square to) {
template<GenType Type, Direction D, bool Enemy>
ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
if constexpr (Enemy && Type == CAPTURES)
{
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
}
}
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
if constexpr ((Type == QUIETS && !Enemy) || Type == EVASIONS || Type == NON_EVASIONS)
{
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
@@ -60,18 +68,18 @@ namespace {
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
// Single and double pawn pushes, no promotions
if (Type != CAPTURES)
if constexpr (Type != CAPTURES)
{
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
if constexpr (Type == EVASIONS) // Consider only blocking squares
{
b1 &= target;
b2 &= target;
}
if (Type == QUIET_CHECKS)
if constexpr (Type == QUIET_CHECKS)
{
// To make a quiet check, you either make a direct check by pushing a pawn
// or push a blocker pawn that is not on the same file as the enemy king.
@@ -102,21 +110,21 @@ namespace {
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
if (Type == EVASIONS)
if constexpr (Type == EVASIONS)
b3 &= target;
while (b1)
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(b1));
moveList = make_promotions<Type, UpRight, true>(moveList, pop_lsb(b1));
while (b2)
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(b2));
moveList = make_promotions<Type, UpLeft, true>(moveList, pop_lsb(b2));
while (b3)
moveList = make_promotions<Type, Up >(moveList, pop_lsb(b3));
moveList = make_promotions<Type, Up, false>(moveList, pop_lsb(b3));
}
// Standard and en passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
Bitboard b2 = shift<UpLeft >(pawnsNotOn7) & enemies;
@@ -264,7 +272,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
: generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList)
if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
if ( ((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
&& !pos.legal(*cur))
*cur = (--moveList)->move;
else
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+10 -11
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -69,7 +69,6 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
!(ttm && pos.pseudo_legal(ttm));
threatenedPieces = 0;
}
/// MovePicker constructor for quiescence search
@@ -93,20 +92,20 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
{
assert(!pos.checkers());
stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm)
&& pos.pseudo_legal(ttm)
&& pos.see_ge(ttm, threshold));
}
/// MovePicker::score() assigns a numerical value to each move in a list, used
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
/// captures with a good history. Quiets moves are ordered using the histories.
/// captures with a good history. Quiets moves are ordered using the history tables.
template<GenType Type>
void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
[[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook;
[[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, threatenedPieces;
if constexpr (Type == QUIETS)
{
Color us = pos.side_to_move();
@@ -123,8 +122,8 @@ void MovePicker::score() {
for (auto& m : *this)
if constexpr (Type == CAPTURES)
m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))])
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
m.value = (7 * int(PieceValue[MG][pos.piece_on(to_sq(m))])
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16;
else if constexpr (Type == QUIETS)
m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]
@@ -141,7 +140,7 @@ void MovePicker::score() {
+ bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384;
else // Type == EVASIONS
{
if (pos.capture(m))
if (pos.capture_stage(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m)))
+ (1 << 28);
@@ -158,7 +157,7 @@ Move MovePicker::select(Pred filter) {
while (cur < endMoves)
{
if (T == Best)
if constexpr (T == Best)
std::swap(*cur, *std::max_element(cur, endMoves));
if (*cur != ttMove && filter())
@@ -197,7 +196,7 @@ top:
case GOOD_CAPTURE:
if (select<Next>([&](){
return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ?
return pos.see_ge(*cur, Value(-cur->value)) ?
// Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = *cur, false); }))
return *(cur - 1);
@@ -216,7 +215,7 @@ top:
case REFUTATION:
if (select<Next>([&](){ return *cur != MOVE_NONE
&& !pos.capture(*cur)
&& !pos.capture_stage(*cur)
&& pos.pseudo_legal(*cur); }))
return *(cur - 1);
++stage;
+8 -10
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -62,14 +62,14 @@ public:
template <typename T, int D, int Size, int... Sizes>
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
{
typedef Stats<T, D, Size, Sizes...> stats;
using stats = Stats<T, D, Size, Sizes...>;
void fill(const T& v) {
// For standard-layout 'this' points to first struct member
assert(std::is_standard_layout<stats>::value);
typedef StatsEntry<T, D> entry;
using entry = StatsEntry<T, D>;
entry* p = reinterpret_cast<entry*>(this);
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
}
@@ -87,23 +87,23 @@ enum StatsType { NoCaptures, Captures };
/// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
/// (~11 elo)
typedef Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
using CounterMoveHistory = Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB>;
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
typedef Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToHistory;
using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
/// ContinuationHistory is the combined history of a given pair of moves, usually
/// the current one given a previous one. The nested history table is based on
/// PieceToHistory instead of ButterflyBoards.
/// (~63 elo)
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
/// MovePicker class is used to pick one pseudo-legal move at a time from the
@@ -131,8 +131,6 @@ public:
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
Move next_move(bool skipQuiets = false);
Bitboard threatenedPieces;
private:
template<PickType T, typename Pred> Move select(Pred);
template<GenType> void score();
+28 -29
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -18,15 +18,15 @@
// Code for calculating NNUE evaluation function
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <string_view>
#include "../evaluate.h"
#include "../position.h"
#include "../misc.h"
#include "../uci.h"
#include "../types.h"
@@ -83,7 +83,7 @@ namespace Stockfish::Eval::NNUE {
} // namespace Detail
// Initialize the evaluation function parameters
void initialize() {
static void initialize() {
Detail::initialize(featureTransformer);
for (std::size_t i = 0; i < LayerStacks; ++i)
@@ -91,7 +91,7 @@ namespace Stockfish::Eval::NNUE {
}
// Read network header
bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
{
std::uint32_t version, size;
@@ -105,7 +105,7 @@ namespace Stockfish::Eval::NNUE {
}
// Write network header
bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
{
write_little_endian<std::uint32_t>(stream, Version);
write_little_endian<std::uint32_t>(stream, hashValue);
@@ -115,7 +115,7 @@ namespace Stockfish::Eval::NNUE {
}
// Read network parameters
bool read_parameters(std::istream& stream) {
static bool read_parameters(std::istream& stream) {
std::uint32_t hashValue;
if (!read_header(stream, &hashValue, &netDescription)) return false;
@@ -127,7 +127,7 @@ namespace Stockfish::Eval::NNUE {
}
// Write network parameters
bool write_parameters(std::ostream& stream) {
static bool write_parameters(std::ostream& stream) {
if (!write_header(stream, HashValue, netDescription)) return false;
if (!Detail::write_parameters(stream, *featureTransformer)) return false;
@@ -136,6 +136,11 @@ namespace Stockfish::Eval::NNUE {
return (bool)stream;
}
void hint_common_parent_position(const Position& pos) {
if (Eval::useNNUE)
featureTransformer->hint_common_access(pos);
}
// Evaluation function. Perform differential calculation.
Value evaluate(const Position& pos, bool adjusted, int* complexity) {
@@ -143,7 +148,7 @@ namespace Stockfish::Eval::NNUE {
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize;
int delta = 24 - pos.non_pawn_material() / 9560;
constexpr int delta = 24;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned[
@@ -211,7 +216,7 @@ namespace Stockfish::Eval::NNUE {
return t;
}
static const std::string PieceToChar(" PNBRQK pnbrqk");
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer.
@@ -245,14 +250,15 @@ namespace Stockfish::Eval::NNUE {
}
// format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer,
// always keeping two decimals. The buffer must have capacity for at least 7 chars.
static void format_cp_aligned_dot(Value v, char* buffer) {
// format_cp_aligned_dot() converts a Value into (centi)pawns, always keeping two decimals.
static void format_cp_aligned_dot(Value v, std::stringstream &stream) {
const double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue;
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue;
sprintf(&buffer[1], "%6.2f", cp);
stream << (v < 0 ? '-' : v > 0 ? '+' : ' ')
<< std::setiosflags(std::ios::fixed)
<< std::setw(6)
<< std::setprecision(2)
<< cp;
}
@@ -332,17 +338,10 @@ namespace Stockfish::Eval::NNUE {
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
{
char buffer[3][8];
std::memset(buffer, '\0', sizeof(buffer));
format_cp_aligned_dot(t.psqt[bucket], buffer[0]);
format_cp_aligned_dot(t.positional[bucket], buffer[1]);
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]);
ss << "| " << bucket << " "
<< " | " << buffer[0] << " "
<< " | " << buffer[1] << " "
<< " | " << buffer[2] << " "
ss << "| " << bucket << " ";
ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss); ss << " "
<< " | "; format_cp_aligned_dot(t.positional[bucket], ss); ss << " "
<< " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); ss << " "
<< " |";
if (bucket == t.correctBucket)
ss << " <-- this bucket is used";
+10 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@ namespace Stockfish::Eval::NNUE {
constexpr std::uint32_t HashValue =
FeatureTransformer::get_hash_value() ^ Network::get_hash_value();
// Deleter for automating release of memory area
template <typename T>
struct AlignedDeleter {
@@ -54,6 +55,14 @@ namespace Stockfish::Eval::NNUE {
template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
std::string trace(Position& pos);
Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr);
void hint_common_parent_position(const Position& pos);
bool load_eval(std::string name, std::istream& stream);
bool save_eval(std::ostream& stream);
bool save_eval(const std::optional<std::string>& filename);
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+61 -43
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@
This file contains the definition for a fully connected layer (aka affine transform).
Two approaches are employed, depending on the sizes of the transform.
Approach 1:
Approach 1 (a specialization for large inputs):
- used when the PaddedInputDimensions >= 128
- uses AVX512 if possible
- processes inputs in batches of 2*InputSimdWidth
@@ -42,9 +42,8 @@
depends on the architecture (the amount of registers)
- accumulate + hadd is used
Approach 2:
Approach 2 (a specialization for small inputs):
- used when the PaddedInputDimensions < 128
- does not use AVX512
- expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32.
- that's why AVX512 is hard to implement
- expected use-case is small layers
@@ -72,6 +71,10 @@ namespace Stockfish::Eval::NNUE::Layers {
const __m64 Zeros = _mm_setzero_si64();
const auto inputVector = reinterpret_cast<const __m64*>(input);
# elif defined(USE_NEON_DOTPROD)
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
const auto inputVector = reinterpret_cast<const int8x16_t*>(input);
# elif defined(USE_NEON)
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
const auto inputVector = reinterpret_cast<const int8x8_t*>(input);
@@ -123,6 +126,14 @@ namespace Stockfish::Eval::NNUE::Layers {
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
output[i] = _mm_cvtsi64_si32(sum);
# elif defined(USE_NEON_DOTPROD)
int32x4_t sum = {biases[i]};
const auto row = reinterpret_cast<const int8x16_t*>(&weights[offset]);
for (IndexType j = 0; j < NumChunks; ++j) {
sum = vdotq_s32(sum, inputVector[j], row[j]);
}
output[i] = vaddvq_s32(sum);
# elif defined(USE_NEON)
int32x4_t sum = {biases[i]};
const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]);
@@ -157,7 +168,7 @@ namespace Stockfish::Eval::NNUE::Layers {
constexpr IndexType LargeInputSize = std::numeric_limits<IndexType>::max();
#endif
// A specialization for large inputs.
// A specialization for large inputs
template <IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= LargeInputSize)>> {
public:
@@ -176,36 +187,39 @@ namespace Stockfish::Eval::NNUE::Layers {
using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization (for large inputs) should not have been chosen.");
#if defined (USE_AVX512)
static constexpr const IndexType InputSimdWidth = 64;
static constexpr const IndexType MaxNumOutputRegs = 16;
static constexpr IndexType InputSimdWidth = 64;
static constexpr IndexType MaxNumOutputRegs = 16;
#elif defined (USE_AVX2)
static constexpr const IndexType InputSimdWidth = 32;
static constexpr const IndexType MaxNumOutputRegs = 8;
static constexpr IndexType InputSimdWidth = 32;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_SSSE3)
static constexpr const IndexType InputSimdWidth = 16;
static constexpr const IndexType MaxNumOutputRegs = 8;
static constexpr IndexType InputSimdWidth = 16;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_NEON_DOTPROD)
static constexpr IndexType InputSimdWidth = 16;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_NEON)
static constexpr const IndexType InputSimdWidth = 8;
static constexpr const IndexType MaxNumOutputRegs = 8;
static constexpr IndexType InputSimdWidth = 8;
static constexpr IndexType MaxNumOutputRegs = 8;
#else
// The fallback implementation will not have permuted weights.
// We define these to avoid a lot of ifdefs later.
static constexpr const IndexType InputSimdWidth = 1;
static constexpr const IndexType MaxNumOutputRegs = 1;
static constexpr IndexType InputSimdWidth = 1;
static constexpr IndexType MaxNumOutputRegs = 1;
#endif
// A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs].
// A small block is a region of size [InputSimdWidth, 1]
static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
static constexpr const IndexType SmallBlockSize = InputSimdWidth;
static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
static constexpr IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
static constexpr IndexType SmallBlockSize = InputSimdWidth;
static constexpr IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
static constexpr IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
static constexpr IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
static constexpr IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
static_assert(OutputDimensions % NumOutputRegs == 0);
@@ -241,8 +255,7 @@ namespace Stockfish::Eval::NNUE::Layers {
// Read network parameters
bool read_parameters(std::istream& stream) {
for (IndexType i = 0; i < OutputDimensions; ++i)
biases[i] = read_little_endian<BiasType>(stream);
read_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
@@ -252,8 +265,7 @@ namespace Stockfish::Eval::NNUE::Layers {
// Write network parameters
bool write_parameters(std::ostream& stream) const {
for (IndexType i = 0; i < OutputDimensions; ++i)
write_little_endian<BiasType>(stream, biases[i]);
write_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
@@ -292,6 +304,15 @@ namespace Stockfish::Eval::NNUE::Layers {
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
#define vec_hadd Simd::m128_hadd
#define vec_haddx4 Simd::m128_haddx4
#elif defined (USE_NEON_DOTPROD)
using acc_vec_t = int32x4_t;
using bias_vec_t = int32x4_t;
using weight_vec_t = int8x16_t;
using in_vec_t = int8x16_t;
#define vec_zero {0}
#define vec_add_dpbusd_32x2 Simd::dotprod_m128_add_dpbusd_epi32x2
#define vec_hadd Simd::neon_m128_hadd
#define vec_haddx4 Simd::neon_m128_haddx4
#elif defined (USE_NEON)
using acc_vec_t = int32x4_t;
using bias_vec_t = int32x4_t;
@@ -374,6 +395,7 @@ namespace Stockfish::Eval::NNUE::Layers {
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
};
// A specialization for small inputs
template <IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < LargeInputSize)>> {
public:
@@ -393,12 +415,7 @@ namespace Stockfish::Eval::NNUE::Layers {
using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
#if defined (USE_SSSE3)
static constexpr const IndexType OutputSimdWidth = SimdWidth / 4;
static constexpr const IndexType InputSimdWidth = SimdWidth;
#endif
static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization (for small inputs) should not have been chosen.");
// Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
@@ -428,8 +445,7 @@ namespace Stockfish::Eval::NNUE::Layers {
// Read network parameters
bool read_parameters(std::istream& stream) {
for (IndexType i = 0; i < OutputDimensions; ++i)
biases[i] = read_little_endian<BiasType>(stream);
read_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
@@ -438,8 +454,7 @@ namespace Stockfish::Eval::NNUE::Layers {
// Write network parameters
bool write_parameters(std::ostream& stream) const {
for (IndexType i = 0; i < OutputDimensions; ++i)
write_little_endian<BiasType>(stream, biases[i]);
write_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
@@ -450,29 +465,34 @@ namespace Stockfish::Eval::NNUE::Layers {
const OutputType* propagate(
const InputType* input, OutputType* output) const {
#if defined (USE_AVX2)
#if defined (USE_AVX512)
using vec_t = __m512i;
#define vec_setzero _mm512_setzero_si512
#define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
#define vec_hadd Simd::m512_hadd
#elif defined (USE_AVX2)
using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
#define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4
#define vec_hadd Simd::m256_hadd
#define vec_haddx4 Simd::m256_haddx4
#elif defined (USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
#define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4
#define vec_hadd Simd::m128_hadd
#define vec_haddx4 Simd::m128_haddx4
#endif
#if defined (USE_SSSE3)
const auto inputVector = reinterpret_cast<const vec_t*>(input);
static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType);
static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1);
if constexpr (OutputDimensions % OutputSimdWidth == 0)
@@ -518,9 +538,7 @@ namespace Stockfish::Eval::NNUE::Layers {
# undef vec_set_32
# undef vec_add_dpbusd_32
# undef vec_add_dpbusd_32x2
# undef vec_add_dpbusd_32x4
# undef vec_hadd
# undef vec_haddx4
#else
// Use old implementation for the other architectures.
affine_transform_non_ssse3<
@@ -0,0 +1,286 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Definition of layer AffineTransformSparseInput of NNUE evaluation function
#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#include <iostream>
#include <algorithm>
#include <array>
#include <type_traits>
#include "../nnue_common.h"
#include "affine_transform.h"
#include "simd.h"
/*
This file contains the definition for a fully connected layer (aka affine transform) with block sparse input.
*/
namespace Stockfish::Eval::NNUE::Layers {
#if defined(__GNUC__) // GCC, Clang, ICC
static inline IndexType lsb_(std::uint32_t b) {
assert(b);
return IndexType(__builtin_ctzl(b));
}
#elif defined(_MSC_VER) // MSVC
static inline IndexType lsb_(std::uint32_t b) {
assert(b);
unsigned long idx;
_BitScanForward(&idx, b);
return (IndexType) idx;
}
#else // Compiler is neither GCC nor MSVC compatible
#error "Compiler not supported."
#endif
#if defined(USE_SSSE3)
alignas(CacheLineSize) static inline const std::array<std::array<std::uint16_t, 8>, 256> lookup_indices = [](){
std::array<std::array<std::uint16_t, 8>, 256> v{};
for (int i = 0; i < 256; ++i)
{
int j = i;
int k = 0;
while(j)
{
const IndexType lsbIndex = lsb_(std::uint32_t(j));
j &= j - 1;
v[i][k] = lsbIndex;
++k;
}
}
return v;
}();
alignas(CacheLineSize) static inline const std::array<unsigned, 256> lookup_count = [](){
std::array<unsigned, 256> v;
for (int i = 0; i < 256; ++i)
{
int j = i;
int k = 0;
while(j)
{
j &= j - 1;
++k;
}
v[i] = k;
}
return v;
}();
// Find indices of nonzero numbers in an int32_t array
template<const IndexType InputDimensions>
void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) {
#if defined (USE_AVX512)
using vec_t = __m512i;
#define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512())
#elif defined (USE_AVX2)
using vec_t = __m256i;
#define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256())))
#elif defined (USE_SSSE3)
using vec_t = __m128i;
#define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
#endif
constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t);
// Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8)
constexpr IndexType ChunkSize = std::max<IndexType>(InputSimdWidth, 8);
constexpr IndexType NumChunks = InputDimensions / ChunkSize;
constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth;
constexpr IndexType OutputsPerChunk = ChunkSize / 8;
const auto inputVector = reinterpret_cast<const vec_t*>(input);
IndexType count = 0;
__m128i base = _mm_set1_epi16(0);
__m128i increment = _mm_set1_epi16(8);
for (IndexType i = 0; i < NumChunks; ++i)
{
// bitmask of nonzero values in this chunk
unsigned nnz = 0;
for (IndexType j = 0; j < InputsPerChunk; ++j)
{
const vec_t inputChunk = inputVector[i * InputsPerChunk + j];
nnz |= (unsigned)vec_nnz(inputChunk) << (j * InputSimdWidth);
}
for (IndexType j = 0; j < OutputsPerChunk; ++j)
{
const auto lookup = (nnz >> (j * 8)) & 0xFF;
const auto offsets = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&lookup_indices[lookup]));
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + count), _mm_add_epi16(base, offsets));
count += lookup_count[lookup];
base = _mm_add_epi16(base, increment);
}
}
count_out = count;
}
# undef vec_nnz
#endif
// Sparse input implementation
template <IndexType InDims, IndexType OutDims>
class AffineTransformSparseInput {
public:
// Input/output type
// Input/output type
using InputType = std::uint8_t;
using OutputType = std::int32_t;
// Number of input/output dimensions
static constexpr IndexType InputDimensions = InDims;
static constexpr IndexType OutputDimensions = OutDims;
static_assert(OutputDimensions % 16 == 0, "Only implemented for OutputDimensions divisible by 16.");
static constexpr IndexType PaddedInputDimensions =
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
static constexpr IndexType PaddedOutputDimensions =
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
#if defined (USE_SSSE3)
static constexpr IndexType ChunkSize = 4;
#else
static constexpr IndexType ChunkSize = 1;
#endif
using OutputBuffer = OutputType[PaddedOutputDimensions];
// Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
std::uint32_t hashValue = 0xCC03DAE4u;
hashValue += OutputDimensions;
hashValue ^= prevHash >> 1;
hashValue ^= prevHash << 31;
return hashValue;
}
static IndexType get_weight_index_scrambled(IndexType i)
{
return
(i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize +
i / PaddedInputDimensions * ChunkSize +
i % ChunkSize;
}
static IndexType get_weight_index(IndexType i)
{
#if defined (USE_SSSE3)
return get_weight_index_scrambled(i);
#else
return i;
#endif
}
// Read network parameters
bool read_parameters(std::istream& stream) {
read_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
return !stream.fail();
}
// Write network parameters
bool write_parameters(std::ostream& stream) const {
write_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
return !stream.fail();
}
// Forward propagation
const OutputType* propagate(
const InputType* input, OutputType* output) const {
#if defined (USE_SSSE3)
#if defined (USE_AVX512)
using vec_t = __m512i;
#define vec_setzero _mm512_setzero_si512
#define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#elif defined (USE_AVX2)
using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#elif defined (USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#endif
static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType);
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize;
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
std::uint16_t nnz[NumChunks];
IndexType count;
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
// Find indices of nonzero 32bit blocks
find_nnz<NumChunks>(input32, nnz, count);
const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases);
vec_t acc[NumRegs];
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasvec[k];
for (IndexType j = 0; j < count; ++j)
{
const auto i = nnz[j];
const vec_t in = vec_set_32(input32[i]);
const auto col = reinterpret_cast<const vec_t*>(&weights[i * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumRegs; ++k)
vec_add_dpbusd_32(acc[k], in, col[k]);
}
vec_t* outptr = reinterpret_cast<vec_t*>(output);
for (IndexType k = 0; k < NumRegs; ++k)
outptr[k] = acc[k];
# undef vec_setzero
# undef vec_set_32
# undef vec_add_dpbusd_32
#else
// Use dense implementation for the other architectures.
affine_transform_non_ssse3<
InputDimensions,
PaddedInputDimensions,
OutputDimensions>(output, weights, biases, input);
#endif
return output;
}
private:
using BiasType = OutputType;
using WeightType = std::int8_t;
alignas(CacheLineSize) BiasType biases[OutputDimensions];
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
};
} // namespace Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+34 -18
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -153,7 +153,7 @@ namespace Stockfish::Simd {
asm(
"vpdpbusd %[b0], %[a0], %[acc]\n\t"
"vpdpbusd %[b1], %[a1], %[acc]\n\t"
: [acc]"+v"(acc)
: [acc]"+&v"(acc)
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
);
# else
@@ -165,18 +165,19 @@ namespace Stockfish::Simd {
__m512i tmp0 = _mm512_maddubs_epi16(a0, b0);
__m512i tmp1 = _mm512_maddubs_epi16(a1, b1);
asm(
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
"vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t"
"vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
: [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1))
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm512_set1_epi16(1))
);
# else
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
product0 = _mm512_adds_epi16(product0, product1);
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, product0);
product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1));
# endif
# endif
}
@@ -249,7 +250,7 @@ namespace Stockfish::Simd {
asm(
VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t"
VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t"
: [acc]"+v"(acc)
: [acc]"+&v"(acc)
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
);
# else
@@ -261,18 +262,19 @@ namespace Stockfish::Simd {
__m256i tmp0 = _mm256_maddubs_epi16(a0, b0);
__m256i tmp1 = _mm256_maddubs_epi16(a1, b1);
asm(
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
"vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t"
"vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
: [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1))
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm256_set1_epi16(1))
);
# else
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
product0 = _mm256_adds_epi16(product0, product1);
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, product0);
product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1));
# endif
# endif
}
@@ -326,23 +328,37 @@ namespace Stockfish::Simd {
__m128i tmp0 = _mm_maddubs_epi16(a0, b0);
__m128i tmp1 = _mm_maddubs_epi16(a1, b1);
asm(
"paddsw %[tmp1], %[tmp0]\n\t"
"pmaddwd %[ones], %[tmp0]\n\t"
"pmaddwd %[ones], %[tmp1]\n\t"
"paddd %[tmp1], %[tmp0]\n\t"
"paddd %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
: [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1))
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm_set1_epi16(1))
);
# else
__m128i product0 = _mm_maddubs_epi16(a0, b0);
__m128i product1 = _mm_maddubs_epi16(a1, b1);
product0 = _mm_adds_epi16(product0, product1);
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, product0);
product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1));
# endif
}
#endif
#if defined (USE_NEON_DOTPROD)
[[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2(
int32x4_t& acc,
int8x16_t a0, int8x16_t b0,
int8x16_t a1, int8x16_t b1) {
acc = vdotq_s32(acc, a0, b0);
acc = vdotq_s32(acc, a1, b1);
}
#endif
#if defined (USE_NEON)
[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -106,7 +106,7 @@ namespace Stockfish::Eval::NNUE::Layers {
for (IndexType i = Start; i < InputDimensions; ++i) {
output[i] = static_cast<OutputType>(
// realy should be /127 but we need to make it fast
// really should be /127 but we need to make it fast
// needs to be accounted for in the trainer
std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128)));
}
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+15 -16
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -27,6 +27,7 @@
#include "features/half_ka_v2_hm.h"
#include "layers/affine_transform_sparse_input.h"
#include "layers/affine_transform.h"
#include "layers/clipped_relu.h"
#include "layers/sqr_clipped_relu.h"
@@ -39,7 +40,7 @@ namespace Stockfish::Eval::NNUE {
using FeatureSet = Features::HalfKAv2_hm;
// Number of input feature dimensions after conversion
constexpr IndexType TransformedFeatureDimensions = 1024;
constexpr IndexType TransformedFeatureDimensions = 1536;
constexpr IndexType PSQTBuckets = 8;
constexpr IndexType LayerStacks = 8;
@@ -48,7 +49,7 @@ struct Network
static constexpr int FC_0_OUTPUTS = 15;
static constexpr int FC_1_OUTPUTS = 32;
Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
Layers::AffineTransformSparseInput<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
Layers::SqrClippedReLU<FC_0_OUTPUTS + 1> ac_sqr_0;
Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0;
Layers::AffineTransform<FC_0_OUTPUTS * 2, FC_1_OUTPUTS> fc_1;
@@ -72,22 +73,20 @@ struct Network
// Read network parameters
bool read_parameters(std::istream& stream) {
if (!fc_0.read_parameters(stream)) return false;
if (!ac_0.read_parameters(stream)) return false;
if (!fc_1.read_parameters(stream)) return false;
if (!ac_1.read_parameters(stream)) return false;
if (!fc_2.read_parameters(stream)) return false;
return true;
return fc_0.read_parameters(stream)
&& ac_0.read_parameters(stream)
&& fc_1.read_parameters(stream)
&& ac_1.read_parameters(stream)
&& fc_2.read_parameters(stream);
}
// Read network parameters
// Write network parameters
bool write_parameters(std::ostream& stream) const {
if (!fc_0.write_parameters(stream)) return false;
if (!ac_0.write_parameters(stream)) return false;
if (!fc_1.write_parameters(stream)) return false;
if (!ac_1.write_parameters(stream)) return false;
if (!fc_2.write_parameters(stream)) return false;
return true;
return fc_0.write_parameters(stream)
&& ac_0.write_parameters(stream)
&& fc_1.write_parameters(stream)
&& ac_1.write_parameters(stream)
&& fc_2.write_parameters(stream);
}
std::int32_t propagate(const TransformedFeatureType* transformedFeatures)
+78 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -57,6 +57,9 @@ namespace Stockfish::Eval::NNUE {
// Size of cache line (in bytes)
constexpr std::size_t CacheLineSize = 64;
constexpr const char Leb128MagicString[] = "COMPRESSED_LEB128";
constexpr const std::size_t Leb128MagicStringSize = sizeof(Leb128MagicString) - 1;
// SIMD width (in bytes)
#if defined(USE_AVX2)
constexpr std::size_t SimdWidth = 32;
@@ -159,6 +162,80 @@ namespace Stockfish::Eval::NNUE {
write_little_endian<IntType>(stream, values[i]);
}
template <typename IntType>
inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) {
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
char leb128MagicString[Leb128MagicStringSize];
stream.read(leb128MagicString, Leb128MagicStringSize);
assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0);
const std::uint32_t BUF_SIZE = 4096;
std::uint8_t buf[BUF_SIZE];
auto bytes_left = read_little_endian<std::uint32_t>(stream);
std::uint32_t buf_pos = BUF_SIZE;
for (std::size_t i = 0; i < count; ++i) {
IntType result = 0;
size_t shift = 0;
do {
if (buf_pos == BUF_SIZE) {
stream.read(reinterpret_cast<char*>(buf), std::min(bytes_left, BUF_SIZE));
buf_pos = 0;
}
std::uint8_t byte = buf[buf_pos++];
--bytes_left;
result |= (byte & 0x7f) << shift;
shift += 7;
if ((byte & 0x80) == 0) {
out[i] = sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0 ? result : result | ~((1 << shift) - 1);
break;
}
} while (shift < sizeof(IntType) * 8);
}
assert(bytes_left == 0);
}
template <typename IntType>
inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) {
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
stream.write(Leb128MagicString, Leb128MagicStringSize);
std::uint32_t byte_count = 0;
for (std::size_t i = 0; i < count; ++i) {
IntType value = values[i];
std::uint8_t byte;
do {
byte = value & 0x7f;
value >>= 7;
++byte_count;
} while ((byte & 0x40) == 0 ? value != 0 : value != -1);
}
write_little_endian(stream, byte_count);
const std::uint32_t BUF_SIZE = 4096;
std::uint8_t buf[BUF_SIZE];
std::uint32_t buf_pos = 0;
auto flush = [&]() {
if (buf_pos > 0) {
stream.write(reinterpret_cast<char*>(buf), buf_pos);
buf_pos = 0;
}
};
auto write = [&](std::uint8_t byte) {
buf[buf_pos++] = byte;
if (buf_pos == BUF_SIZE) flush();
};
for (std::size_t i = 0; i < count; ++i) {
IntType value = values[i];
while (true) {
std::uint8_t byte = value & 0x7f;
value >>= 7;
if ((byte & 0x40) == 0 ? value == 0 : value == -1) {
write(byte);
break;
}
write(byte | 0x80);
}
}
flush();
}
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED
+272 -187
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,7 @@
#include "nnue_architecture.h"
#include <cstring> // std::memset()
#include <utility> // std::pair
namespace Stockfish::Eval::NNUE {
@@ -41,8 +42,8 @@ namespace Stockfish::Eval::NNUE {
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
#ifdef USE_AVX512
typedef __m512i vec_t;
typedef __m256i psqt_vec_t;
using vec_t = __m512i;
using psqt_vec_t = __m256i;
#define vec_load(a) _mm512_load_si512(a)
#define vec_store(a,b) _mm512_store_si512(a,b)
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
@@ -65,8 +66,8 @@ namespace Stockfish::Eval::NNUE {
#define MaxChunkSize 64
#elif USE_AVX2
typedef __m256i vec_t;
typedef __m256i psqt_vec_t;
using vec_t = __m256i;
using psqt_vec_t = __m256i;
#define vec_load(a) _mm256_load_si256(a)
#define vec_store(a,b) _mm256_store_si256(a,b)
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
@@ -89,8 +90,8 @@ namespace Stockfish::Eval::NNUE {
#define MaxChunkSize 32
#elif USE_SSE2
typedef __m128i vec_t;
typedef __m128i psqt_vec_t;
using vec_t = __m128i;
using psqt_vec_t = __m128i;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) _mm_add_epi16(a,b)
@@ -110,8 +111,8 @@ namespace Stockfish::Eval::NNUE {
#define MaxChunkSize 16
#elif USE_MMX
typedef __m64 vec_t;
typedef __m64 psqt_vec_t;
using vec_t = __m64;
using psqt_vec_t = __m64;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) _mm_add_pi16(a,b)
@@ -138,8 +139,8 @@ namespace Stockfish::Eval::NNUE {
#define MaxChunkSize 8
#elif USE_NEON
typedef int16x8_t vec_t;
typedef int32x4_t psqt_vec_t;
using vec_t = int16x8_t;
using psqt_vec_t = int32x4_t;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) vaddq_s16(a,b)
@@ -252,9 +253,9 @@ namespace Stockfish::Eval::NNUE {
// Read network parameters
bool read_parameters(std::istream& stream) {
read_little_endian<BiasType >(stream, biases , HalfDimensions );
read_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
read_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
read_leb_128<BiasType >(stream, biases , HalfDimensions );
read_leb_128<WeightType >(stream, weights , HalfDimensions * InputDimensions);
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
return !stream.fail();
}
@@ -262,9 +263,9 @@ namespace Stockfish::Eval::NNUE {
// Write network parameters
bool write_parameters(std::ostream& stream) const {
write_little_endian<BiasType >(stream, biases , HalfDimensions );
write_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
write_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
write_leb_128<BiasType >(stream, biases , HalfDimensions );
write_leb_128<WeightType >(stream, weights , HalfDimensions * InputDimensions);
write_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
return !stream.fail();
}
@@ -332,27 +333,16 @@ namespace Stockfish::Eval::NNUE {
#endif
return psqt;
} // end of function transform()
} // end of function transform()
void hint_common_access(const Position& pos) const {
hint_common_access_for_perspective<WHITE>(pos);
hint_common_access_for_perspective<BLACK>(pos);
}
private:
template<Color Perspective>
void update_accumulator(const Position& pos) const {
// The size must be enough to contain the largest possible update.
// That might depend on the feature set and generally relies on the
// feature set's update cost calculation to be correct and never
// allow updates with more added/removed features than MaxActiveDimensions.
#ifdef VECTOR
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
// is defined in the VECTOR code below, once in each branch
vec_t acc[NumRegs];
psqt_vec_t psqt[NumPsqtRegs];
#endif
[[nodiscard]] std::pair<StateInfo*, StateInfo*> try_find_computed_accumulator(const Position& pos) const {
// Look for a usable accumulator of an earlier position. We keep track
// of the estimated gain in terms of features to be added/subtracted.
StateInfo *st = pos.state(), *next = nullptr;
@@ -367,218 +357,313 @@ namespace Stockfish::Eval::NNUE {
next = st;
st = st->previous;
}
return { st, next };
}
if (st->accumulator.computed[Perspective])
{
if (next == nullptr)
return;
// NOTE: The parameter states_to_update is an array of position states, ending with nullptr.
// All states must be sequential, that is states_to_update[i] must either be reachable
// by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr.
// computed_st must be reachable by repeatedly applying ->previous on states_to_update[0], if not nullptr.
template<Color Perspective, size_t N>
void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const {
static_assert(N > 0);
assert(states_to_update[N-1] == nullptr);
// Update incrementally in two steps. First, we update the "next"
// accumulator. Then, we update the current accumulator (pos.state()).
// Gather all features to be updated.
const Square ksq = pos.square<KING>(Perspective);
FeatureSet::IndexList removed[2], added[2];
FeatureSet::append_changed_indices<Perspective>(
ksq, next->dirtyPiece, removed[0], added[0]);
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
FeatureSet::append_changed_indices<Perspective>(
ksq, st2->dirtyPiece, removed[1], added[1]);
// Mark the accumulators as computed.
next->accumulator.computed[Perspective] = true;
pos.state()->accumulator.computed[Perspective] = true;
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
StateInfo *states_to_update[3] =
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
#ifdef VECTOR
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
// is defined in the VECTOR code below, once in each branch
vec_t acc[NumRegs];
psqt_vec_t psqt[NumPsqtRegs];
#endif
if (states_to_update[0] == nullptr)
return;
// Update incrementally going back through states_to_update.
// Gather all features to be updated.
const Square ksq = pos.square<KING>(Perspective);
// The size must be enough to contain the largest possible update.
// That might depend on the feature set and generally relies on the
// feature set's update cost calculation to be correct and never
// allow updates with more added/removed features than MaxActiveDimensions.
FeatureSet::IndexList removed[N-1], added[N-1];
{
int i = N-2; // last potential state to update. Skip last element because it must be nullptr.
while (states_to_update[i] == nullptr)
--i;
StateInfo *st2 = states_to_update[i];
for (; i >= 0; --i)
{
// Load accumulator
auto accTile = reinterpret_cast<vec_t*>(
&st->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_load(&accTile[k]);
states_to_update[i]->accumulator.computed[Perspective] = true;
for (IndexType i = 0; states_to_update[i]; ++i)
{
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_sub_16(acc[k], column[k]);
}
StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1];
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
// Store accumulator
accTile = reinterpret_cast<vec_t*>(
&states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
vec_store(&accTile[k], acc[k]);
}
for (; st2 != end_state; st2 = st2->previous)
FeatureSet::append_changed_indices<Perspective>(
ksq, st2->dirtyPiece, removed[i], added[i]);
}
}
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
{
// Load accumulator
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_load_psqt(&accTilePsqt[k]);
StateInfo* st = computed_st;
for (IndexType i = 0; states_to_update[i]; ++i)
{
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
}
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
#ifdef VECTOR
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
{
// Load accumulator
auto accTile = reinterpret_cast<vec_t*>(
&st->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_load(&accTile[k]);
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
}
// Store accumulator
accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]);
}
}
#else
for (IndexType i = 0; states_to_update[i]; ++i)
{
std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective],
st->accumulator.accumulation[Perspective],
HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k)
states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k];
st = states_to_update[i];
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[Perspective][j] -= weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k];
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_sub_16(acc[k], column[k]);
}
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[Perspective][j] += weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
}
}
#endif
}
else
{
// Refresh the accumulator
auto& accumulator = pos.state()->accumulator;
accumulator.computed[Perspective] = true;
FeatureSet::IndexList active;
FeatureSet::append_active_indices<Perspective>(pos, active);
#ifdef VECTOR
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
{
auto biasesTile = reinterpret_cast<const vec_t*>(
&biases[j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasesTile[k];
for (const auto index : active)
{
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (unsigned k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
auto accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[Perspective][j * TileHeight]);
for (unsigned k = 0; k < NumRegs; k++)
// Store accumulator
accTile = reinterpret_cast<vec_t*>(
&states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
vec_store(&accTile[k], acc[k]);
}
}
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
{
// Load accumulator
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_load_psqt(&accTilePsqt[k]);
for (IndexType i = 0; states_to_update[i]; ++i)
{
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_zero_psqt();
for (const auto index : active)
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
}
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
}
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
// Store accumulator
accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]);
}
}
#else
std::memcpy(accumulator.accumulation[Perspective], biases,
#else
for (IndexType i = 0; states_to_update[i]; ++i)
{
std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective],
st->accumulator.accumulation[Perspective],
HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[Perspective][k] = 0;
states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k];
for (const auto index : active)
st = states_to_update[i];
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j)
accumulator.accumulation[Perspective][j] += weights[offset + j];
st->accumulator.accumulation[Perspective][j] -= weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k];
}
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[Perspective][j] += weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
}
#endif
}
#endif
#if defined(USE_MMX)
_mm_empty();
#endif
}
template<Color Perspective>
void update_accumulator_refresh(const Position& pos) const {
#ifdef VECTOR
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
// is defined in the VECTOR code below, once in each branch
vec_t acc[NumRegs];
psqt_vec_t psqt[NumPsqtRegs];
#endif
// Refresh the accumulator
// Could be extracted to a separate function because it's done in 2 places,
// but it's unclear if compilers would correctly handle register allocation.
auto& accumulator = pos.state()->accumulator;
accumulator.computed[Perspective] = true;
FeatureSet::IndexList active;
FeatureSet::append_active_indices<Perspective>(pos, active);
#ifdef VECTOR
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
{
auto biasesTile = reinterpret_cast<const vec_t*>(
&biases[j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasesTile[k];
for (const auto index : active)
{
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (unsigned k = 0; k < NumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
auto accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[Perspective][j * TileHeight]);
for (unsigned k = 0; k < NumRegs; k++)
vec_store(&accTile[k], acc[k]);
}
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
{
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_zero_psqt();
for (const auto index : active)
{
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
}
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]);
}
#else
std::memcpy(accumulator.accumulation[Perspective], biases,
HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[Perspective][k] = 0;
for (const auto index : active)
{
const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j)
accumulator.accumulation[Perspective][j] += weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
}
#endif
#if defined(USE_MMX)
_mm_empty();
#endif
}
template<Color Perspective>
void hint_common_access_for_perspective(const Position& pos) const {
// Works like update_accumulator, but performs less work.
// Updates ONLY the accumulator for pos.
// Look for a usable accumulator of an earlier position. We keep track
// of the estimated gain in terms of features to be added/subtracted.
// Fast early exit.
if (pos.state()->accumulator.computed[Perspective])
return;
auto [oldest_st, _] = try_find_computed_accumulator<Perspective>(pos);
if (oldest_st->accumulator.computed[Perspective])
{
// Only update current position accumulator to minimize work.
StateInfo* states_to_update[2] = { pos.state(), nullptr };
update_accumulator_incremental<Perspective, 2>(pos, oldest_st, states_to_update);
}
else
{
update_accumulator_refresh<Perspective>(pos);
}
}
template<Color Perspective>
void update_accumulator(const Position& pos) const {
auto [oldest_st, next] = try_find_computed_accumulator<Perspective>(pos);
if (oldest_st->accumulator.computed[Perspective])
{
if (next == nullptr)
return;
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
// Currently we update 2 accumulators.
// 1. for the current position
// 2. the next accumulator after the computed one
// The heuristic may change in the future.
StateInfo *states_to_update[3] =
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
update_accumulator_incremental<Perspective, 3>(pos, oldest_st, states_to_update);
}
else
{
update_accumulator_refresh<Perspective>(pos);
}
}
alignas(CacheLineSize) BiasType biases[HalfDimensions];
alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -61,7 +61,7 @@ struct Entry {
int blockedCount;
};
typedef HashTable<Entry, 131072> Table;
using Table = HashTable<Entry, 131072>;
Entry* probe(const Position& pos);
+44 -50
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,6 +22,7 @@
#include <cstring> // For std::memset, std::memcmp
#include <iomanip>
#include <sstream>
#include <string_view>
#include "bitboard.h"
#include "misc.h"
@@ -46,7 +47,7 @@ namespace Zobrist {
namespace {
const string PieceToChar(" PNBRQK pnbrqk");
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
@@ -96,7 +97,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
// Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition"
// situations. Description of the algorithm in the following paper:
// https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
// http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
// First and second hash functions for indexing the cuckoo tables
inline int H1(Key h) { return h & 0x1fff; }
@@ -281,7 +282,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
chess960 = isChess960;
thisThread = th;
set_state(st);
set_state();
assert(pos_is_ok());
@@ -312,60 +313,59 @@ void Position::set_castling_right(Color c, Square rfrom) {
/// Position::set_check_info() sets king attacks to detect if a move gives check
void Position::set_check_info(StateInfo* si) const {
void Position::set_check_info() const {
si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinners[BLACK]);
si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinners[WHITE]);
st->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), st->pinners[BLACK]);
st->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), st->pinners[WHITE]);
Square ksq = square<KING>(~sideToMove);
si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
si->checkSquares[KING] = 0;
st->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
st->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
st->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
st->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK];
st->checkSquares[KING] = 0;
}
/// Position::set_state() computes the hash keys of the position, and other
/// data that once computed is updated incrementally as moves are made.
/// The function is only used when a new position is set up, and to verify
/// the correctness of the StateInfo data when running in debug mode.
/// The function is only used when a new position is set up
void Position::set_state(StateInfo* si) const {
void Position::set_state() const {
si->key = si->materialKey = 0;
si->pawnKey = Zobrist::noPawns;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
st->key = st->materialKey = 0;
st->pawnKey = Zobrist::noPawns;
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
set_check_info(si);
set_check_info();
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s];
st->key ^= Zobrist::psq[pc][s];
if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s];
st->pawnKey ^= Zobrist::psq[pc][s];
else if (type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
st->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
}
if (si->epSquare != SQ_NONE)
si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
if (st->epSquare != SQ_NONE)
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
if (sideToMove == BLACK)
si->key ^= Zobrist::side;
st->key ^= Zobrist::side;
si->key ^= Zobrist::castling[si->castlingRights];
st->key ^= Zobrist::castling[st->castlingRights];
for (Piece pc : Pieces)
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt];
st->materialKey ^= Zobrist::psq[pc][cnt];
}
@@ -568,8 +568,7 @@ bool Position::pseudo_legal(const Move m) const {
: MoveList<NON_EVASIONS>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
return false;
assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE);
// If the 'from' square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
@@ -765,9 +764,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update board and piece lists
remove_piece(capsq);
if (type_of(m) == EN_PASSANT)
board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
@@ -868,7 +864,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
sideToMove = ~sideToMove;
// Update king attacks used for fast check detection
set_check_info(st);
set_check_info();
// Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero
@@ -1020,7 +1016,7 @@ void Position::do_null_move(StateInfo& newSt) {
sideToMove = ~sideToMove;
set_check_info(st);
set_check_info();
st->repetition = 0;
@@ -1065,7 +1061,7 @@ Key Position::key_after(Move m) const {
/// SEE value of move is greater or equal to the given threshold. We'll use an
/// algorithm similar to alpha-beta pruning with a null window.
bool Position::see_ge(Move m, Value threshold) const {
bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const {
assert(is_ok(m));
@@ -1084,7 +1080,7 @@ bool Position::see_ge(Move m, Value threshold) const {
return true;
assert(color_of(piece_on(from)) == sideToMove);
Bitboard occupied = pieces() ^ from ^ to;
occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic
Color stm = sideToMove;
Bitboard attackers = attackers_to(to, occupied);
Bitboard stmAttackers, bb;
@@ -1115,45 +1111,44 @@ bool Position::see_ge(Move m, Value threshold) const {
// the bitboard 'attackers' any X-ray attackers behind it.
if ((bb = stmAttackers & pieces(PAWN)))
{
occupied ^= least_significant_square_bb(bb);
if ((swap = PawnValueMg - swap) < res)
break;
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
else if ((bb = stmAttackers & pieces(KNIGHT)))
{
occupied ^= least_significant_square_bb(bb);
if ((swap = KnightValueMg - swap) < res)
break;
occupied ^= least_significant_square_bb(bb);
}
else if ((bb = stmAttackers & pieces(BISHOP)))
{
occupied ^= least_significant_square_bb(bb);
if ((swap = BishopValueMg - swap) < res)
break;
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
else if ((bb = stmAttackers & pieces(ROOK)))
{
occupied ^= least_significant_square_bb(bb);
if ((swap = RookValueMg - swap) < res)
break;
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
}
else if ((bb = stmAttackers & pieces(QUEEN)))
{
occupied ^= least_significant_square_bb(bb);
if ((swap = QueenValueMg - swap) < res)
break;
occupied ^= least_significant_square_bb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
}
@@ -1167,6 +1162,11 @@ bool Position::see_ge(Move m, Value threshold) const {
return bool(res);
}
bool Position::see_ge(Move m, Value threshold) const {
Bitboard occupied;
return see_ge(m, occupied, threshold);
}
/// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates.
@@ -1323,12 +1323,6 @@ bool Position::pos_is_ok() const {
if (p1 != p2 && (pieces(p1) & pieces(p2)))
assert(0 && "pos_is_ok: Bitboards");
StateInfo si = *st;
ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize);
set_state(&si);
if (std::memcmp(&si, st, sizeof(StateInfo)))
assert(0 && "pos_is_ok: State");
for (Piece pc : Pieces)
if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
+25 -18
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -68,7 +68,7 @@ struct StateInfo {
/// start position to the position just before the search starts). Needed by
/// 'draw by repetition' detection. Use a std::deque because pointers to
/// elements are not invalidated upon list resizing.
typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;
/// Position class stores information regarding the board representation as
@@ -92,10 +92,9 @@ public:
// Position representation
Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
template<typename ...PieceTypes> Bitboard pieces(PieceType pt, PieceTypes... pts) const;
Bitboard pieces(Color c) const;
Bitboard pieces(Color c, PieceType pt) const;
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
template<typename ...PieceTypes> Bitboard pieces(Color c, PieceTypes... pts) const;
Piece piece_on(Square s) const;
Square ep_square() const;
bool empty(Square s) const;
@@ -126,6 +125,7 @@ public:
bool legal(Move m) const;
bool pseudo_legal(const Move m) const;
bool capture(Move m) const;
bool capture_stage(Move m) const;
bool gives_check(Move m) const;
Piece moved_piece(Move m) const;
Piece captured_piece() const;
@@ -144,6 +144,7 @@ public:
// Static Exchange Evaluation
bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const;
// Accessing hash keys
Key key() const;
@@ -178,8 +179,8 @@ public:
private:
// Initialization helpers (used while setting up a position)
void set_castling_right(Color c, Square rfrom);
void set_state(StateInfo* si) const;
void set_check_info(StateInfo* si) const;
void set_state() const;
void set_check_info() const;
// Other helpers
void move_piece(Square from, Square to);
@@ -204,7 +205,7 @@ private:
bool chess960;
};
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
std::ostream& operator<<(std::ostream& os, const Position& pos);
inline Color Position::side_to_move() const {
return sideToMove;
@@ -227,20 +228,18 @@ inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const {
return byTypeBB[pt];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return pieces(pt1) | pieces(pt2);
template<typename ...PieceTypes>
inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const {
return pieces(pt) | pieces(pts...);
}
inline Bitboard Position::pieces(Color c) const {
return byColorBB[c];
}
inline Bitboard Position::pieces(Color c, PieceType pt) const {
return pieces(c) & pieces(pt);
}
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return pieces(c) & (pieces(pt1) | pieces(pt2));
template<typename ...PieceTypes>
inline Bitboard Position::pieces(Color c, PieceTypes... pts) const {
return pieces(c) & pieces(pts...);
}
template<PieceType Pt> inline int Position::count(Color c) const {
@@ -383,8 +382,16 @@ inline bool Position::is_chess960() const {
inline bool Position::capture(Move m) const {
assert(is_ok(m));
// Castling is encoded as "king captures rook"
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
return (!empty(to_sq(m)) && type_of(m) != CASTLING)
|| type_of(m) == EN_PASSANT;
}
// returns true if a move is generated from the capture stage
// having also queen promotions covered, i.e. consistency with the capture stage move generation
// is needed to avoid the generation of duplicate moves.
inline bool Position::capture_stage(Move m) const {
assert(is_ok(m));
return capture(m) || promotion_type(m) == QUEEN;
}
inline Piece Position::captured_piece() const {
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ namespace Stockfish::PSQT
extern Score psq[PIECE_NB][SQUARE_NB];
// Fill psqt array from a set of internally linked parameters
extern void init();
void init();
} // namespace Stockfish::PSQT
+370 -342
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -72,6 +72,7 @@ struct RootMove {
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
Value averageScore = -VALUE_INFINITE;
Value uciScore = -VALUE_INFINITE;
bool scoreLowerbound = false;
bool scoreUpperbound = false;
int selDepth = 0;
@@ -80,7 +81,7 @@ struct RootMove {
std::vector<Move> pv;
};
typedef std::vector<RootMove> RootMoves;
using RootMoves = std::vector<RootMove>;
/// LimitsType struct stores information sent by GUI about available time to
+13 -15
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,9 +24,10 @@
#include <fstream>
#include <iostream>
#include <list>
#include <sstream>
#include <type_traits>
#include <mutex>
#include <sstream>
#include <string_view>
#include <type_traits>
#include "../bitboard.h"
#include "../cluster.h"
@@ -71,7 +72,7 @@ enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, Singl
inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); }
inline Square operator^(Square s, int i) { return Square(int(s) ^ i); }
const std::string PieceToChar = " PNBRQK pnbrqk";
constexpr std::string_view PieceToChar = " PNBRQK pnbrqk";
int MapPawns[SQUARE_NB];
int MapB1H1H7[SQUARE_NB];
@@ -142,7 +143,7 @@ struct SparseEntry {
static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes");
typedef uint16_t Sym; // Huffman symbol
using Sym = uint16_t; // Huffman symbol
struct LR {
enum Side { Left, Right };
@@ -200,13 +201,10 @@ public:
}
}
// Memory map the file and check it. File should be already open and will be
// closed after mapping.
// Memory map the file and check it.
uint8_t* map(void** baseAddress, uint64_t* mapping, TBType type) {
assert(is_open());
close(); // Need to re-open to get native file descriptor
if (is_open())
close(); // Need to re-open to get native file descriptor
#ifndef _WIN32
struct stat statbuf;
@@ -237,7 +235,7 @@ public:
}
#else
// Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
HANDLE fd = CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
if (fd == INVALID_HANDLE_VALUE)
@@ -330,7 +328,7 @@ struct PairsData {
// first access, when the corresponding file is memory mapped.
template<TBType Type>
struct TBTable {
typedef typename std::conditional<Type == WDL, WDLScore, int>::type Ret;
using Ret = typename std::conditional<Type == WDL, WDLScore, int>::type;
static constexpr int Sides = Type == WDL ? 2 : 1;
@@ -1516,7 +1514,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
// A return value false indicates that not all probes were successful.
bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
ProbeState result;
ProbeState result = OK;
StateInfo st;
// Obtain 50-move counter for the root position
@@ -1595,7 +1593,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ };
ProbeState result;
ProbeState result = OK;
StateInfo st;
WDLScore wdl;
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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 -22
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -60,7 +60,6 @@ void Thread::clear() {
counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
captureHistory.fill(0);
previousDepth = 0;
for (bool inCheck : { false, true })
for (StatsType c : { NoCaptures, Captures })
@@ -73,9 +72,9 @@ void Thread::clear() {
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() {
std::lock_guard<std::mutex> lk(mutex);
mutex.lock();
searching = true;
mutex.unlock(); // Unlock before notifying saves a few CPU-cycles
cv.notify_one(); // Wake up the thread in idle_loop()
}
@@ -125,20 +124,20 @@ void Thread::idle_loop() {
void ThreadPool::set(size_t requested) {
if (size() > 0) // destroy any existing thread(s)
if (threads.size() > 0) // destroy any existing thread(s)
{
main()->wait_for_search_finished();
while (size() > 0)
delete back(), pop_back();
while (threads.size() > 0)
delete threads.back(), threads.pop_back();
}
if (requested > 0) // create new thread(s)
{
push_back(new MainThread(0));
threads.push_back(new MainThread(0));
while (size() < requested)
push_back(new Thread(size()));
while (threads.size() < requested)
threads.push_back(new Thread(threads.size()));
clear();
// Reallocate the hash with the new threadpool size
@@ -157,7 +156,7 @@ void ThreadPool::set(size_t requested) {
void ThreadPool::clear() {
for (Thread* th : *this)
for (Thread* th : threads)
th->clear();
main()->callsCnt = 0;
@@ -190,7 +189,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
Tablebases::rank_root_moves(pos, rootMoves);
// After ownership transfer 'states' becomes empty, so if we stop the search
// and call 'go' again without setting a new position states.get() == NULL.
// and call 'go' again without setting a new position states.get() == nullptr.
assert(states.get() || setupStates.get());
if (states.get())
@@ -201,7 +200,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
// be deduced from a fen string, so set() clears them and they are set from
// setupStates->back() later. The rootState is per thread, earlier states are shared
// since they are read-only.
for (Thread* th : *this)
for (Thread* th : threads)
{
th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = th->bestMoveChanges = 0;
th->rootDepth = th->completedDepth = 0;
@@ -217,12 +216,12 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
Thread* ThreadPool::get_best_thread() const {
Thread* bestThread = front();
Thread* bestThread = threads.front();
std::map<Move, int64_t> votes;
Value minScore = VALUE_NONE;
// Find minimum score of all threads
for (Thread* th: *this)
for (Thread* th: threads)
minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth, and select the best thread
@@ -230,10 +229,10 @@ Thread* ThreadPool::get_best_thread() const {
return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
};
for (Thread* th : *this)
for (Thread* th : threads)
votes[th->rootMoves[0].pv[0]] += thread_value(th);
for (Thread* th : *this)
for (Thread* th : threads)
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
@@ -244,7 +243,8 @@ Thread* ThreadPool::get_best_thread() const {
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]
|| ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]]
&& thread_value(th) > thread_value(bestThread)))))
&& thread_value(th) * int(th->rootMoves[0].pv.size() > 2)
> thread_value(bestThread) * int(bestThread->rootMoves[0].pv.size() > 2)))))
bestThread = th;
return bestThread;
@@ -255,8 +255,8 @@ Thread* ThreadPool::get_best_thread() const {
void ThreadPool::start_searching() {
for (Thread* th : *this)
if (th != front())
for (Thread* th : threads)
if (th != threads.front())
th->start_searching();
}
@@ -265,8 +265,8 @@ void ThreadPool::start_searching() {
void ThreadPool::wait_for_search_finished() const {
for (Thread* th : *this)
if (th != front())
for (Thread* th : threads)
if (th != threads.front())
th->wait_for_search_finished();
}
+13 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -61,16 +61,14 @@ public:
Pawns::Table pawnsTable;
Material::Table materialTable;
size_t pvIdx, pvLast;
RunningAverage complexityAverage;
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
int selDepth, nmpMinPly;
Color nmpColor;
Value bestValue, optimism[COLOR_NB];
Position rootPos;
StateInfo rootState;
Search::RootMoves rootMoves;
Depth rootDepth, completedDepth, previousDepth;
Depth rootDepth, completedDepth;
Value rootDelta;
CounterMoveHistory counterMoves;
ButterflyHistory mainHistory;
@@ -108,13 +106,13 @@ struct MainThread : public Thread {
/// parking and, most importantly, launching a thread. All the access to threads
/// is done through this class.
struct ThreadPool : public std::vector<Thread*> {
struct ThreadPool {
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
void clear();
void set(size_t);
MainThread* main() const { return static_cast<MainThread*>(front()); }
MainThread* main() const { return static_cast<MainThread*>(threads.front()); }
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
uint64_t TT_saves() const { return accumulate(&Thread::TTsaves); }
@@ -124,13 +122,21 @@ struct ThreadPool : public std::vector<Thread*> {
std::atomic_bool stop, increaseDepth;
auto cbegin() const noexcept { return threads.cbegin(); }
auto begin() noexcept { return threads.begin(); }
auto end() noexcept { return threads.end(); }
auto cend() const noexcept { return threads.cend(); }
auto size() const noexcept { return threads.size(); }
auto empty() const noexcept { return threads.empty(); }
private:
StateListPtr setupStates;
std::vector<Thread*> threads;
uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
uint64_t sum = 0;
for (Thread* th : *this)
for (Thread* th : threads)
sum += (th->*member).load(std::memory_order_relaxed);
return sum;
}
+4 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -41,7 +41,7 @@ void* start_routine(void* ptr)
P* p = reinterpret_cast<P*>(ptr);
(p->first->*(p->second))(); // Call member function pointer
delete p;
return NULL;
return nullptr;
}
class NativeThread {
@@ -56,7 +56,7 @@ public:
pthread_attr_setstacksize(attr, TH_STACK_SIZE);
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
}
void join() { pthread_join(thread, NULL); }
void join() { pthread_join(thread, nullptr); }
};
} // namespace Stockfish
@@ -65,7 +65,7 @@ public:
namespace Stockfish {
typedef std::thread NativeThread;
using NativeThread = std::thread;
} // namespace Stockfish
+7 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -36,6 +36,12 @@ TimeManagement Time; // Our global time management object
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// if we have no time, no need to initialize TM, except for the start time,
// which is used by movetime.
startTime = limits.startTime;
if (limits.time[us] == 0)
return;
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
TimePoint npmsec = TimePoint(Options["nodestime"]);
@@ -59,8 +65,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
limits.npmsec = npmsec;
}
startTime = limits.startTime;
// Maximum move horizon of 50 moves
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,7 +87,7 @@ void TranspositionTable::clear() {
std::vector<std::thread> threads;
for (size_t idx = 0; idx < Options["Threads"]; ++idx)
for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx)
{
threads.emplace_back([this, idx]() {
@@ -98,7 +98,7 @@ void TranspositionTable::clear() {
// Each thread will zero its part of the hash table
const size_t stride = size_t(clusterCount / Options["Threads"]),
start = size_t(stride * idx),
len = idx != Options["Threads"] - 1 ?
len = idx != size_t(Options["Threads"]) - 1 ?
stride : clusterCount - start;
std::memset(&table[start], 0, len * sizeof(Cluster));
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
+4 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,8 +26,8 @@
namespace Stockfish {
typedef std::pair<int, int> Range; // Option's min-max values
typedef Range (RangeFun) (int);
using Range = std::pair<int, int>; // Option's min-max values
using RangeFun = Range (int);
// Default Range function, to calculate Option's min-max values
inline Range default_range(int v) {
@@ -75,7 +75,7 @@ struct SetRange {
class Tune {
typedef void (PostUpdate) (); // Post-update function
using PostUpdate = void (); // Post-update function
Tune() { read_results(); }
Tune(const Tune&) = delete;
+13 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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
@@ -103,8 +103,8 @@ constexpr bool Is64Bit = true;
constexpr bool Is64Bit = false;
#endif
typedef uint64_t Key;
typedef uint64_t Bitboard;
using Key = uint64_t;
using Bitboard = uint64_t;
constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 246;
@@ -186,6 +186,9 @@ enum Value : int {
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
// In the code, we make the assumption that these values
// are such that non_pawn_material() can be used to uniquely
// identify the material on the board.
PawnValueMg = 126, PawnValueEg = 208,
KnightValueMg = 781, KnightValueEg = 854,
BishopValueMg = 825, BishopValueEg = 915,
@@ -215,7 +218,7 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO }
};
typedef int Depth;
using Depth = int;
enum : int {
DEPTH_QS_CHECKS = 0,
@@ -413,6 +416,10 @@ inline Color color_of(Piece pc) {
return Color(pc >> 3);
}
constexpr bool is_ok(Move m) {
return m != MOVE_NONE && m != MOVE_NULL;
}
constexpr bool is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
@@ -442,10 +449,12 @@ constexpr Direction pawn_push(Color c) {
}
constexpr Square from_sq(Move m) {
assert(is_ok(m));
return Square((m >> 6) & 0x3F);
}
constexpr Square to_sq(Move m) {
assert(is_ok(m));
return Square(m & 0x3F);
}
@@ -470,10 +479,6 @@ constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
constexpr bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}
/// Based on a congruential pseudo random number generator
constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
+15 -10
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,6 +22,7 @@
#include <sstream>
#include <string>
#include "benchmark.h"
#include "evaluate.h"
#include "cluster.h"
#include "movegen.h"
@@ -32,13 +33,12 @@
#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
#include "nnue/evaluate_nnue.h"
using namespace std;
namespace Stockfish {
extern vector<string> setup_bench(const Position&, istream&);
namespace {
// FEN string for the initial position in standard chess
@@ -162,7 +162,7 @@ namespace {
uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
num = count_if(list.begin(), list.end(), [](const string& s) { return s.find("go ") == 0 || s.find("eval") == 0; });
TimePoint elapsed = now();
@@ -211,8 +211,8 @@ namespace {
// The coefficients of a third-order polynomial fit is based on the fishtest data
// for two parameters that need to transform eval to the argument of a logistic
// function.
constexpr double as[] = { -0.58270499, 2.68512549, 15.24638015, 344.49745382};
constexpr double bs[] = { -2.65734562, 15.96509799, -20.69040836, 73.61029937 };
constexpr double as[] = { 0.38036525, -2.82015070, 23.17882135, 307.36768407};
constexpr double bs[] = { -2.29434733, 13.27689788, -14.26828904, 63.45318330 };
// Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64
static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3]));
@@ -323,8 +323,13 @@ string UCI::value(Value v) {
stringstream ss;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY)
ss << "cp " << v * 100 / NormalizeToPawnValue;
else if (abs(v) < VALUE_MATE_IN_MAX_PLY)
{
const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply
ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply);
}
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
@@ -362,15 +367,15 @@ std::string UCI::square(Square s) {
string UCI::move(Move m, bool chess960) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
Square from = from_sq(m);
Square to = to_sq(m);
if (type_of(m) == CASTLING && !chess960)
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
+5 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 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,7 +35,7 @@ namespace UCI {
// the win_rate_model() such that Stockfish outputs an advantage of
// "100 centipawns" for a position if the engine has a 50% probability to win
// from this position in selfplay at fishtest LTC time control.
const int NormalizeToPawnValue = 361;
const int NormalizeToPawnValue = 328;
class Option;
@@ -45,12 +45,12 @@ struct CaseInsensitiveLess {
};
/// The options container is defined as a std::map
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
using OptionsMap = std::map<std::string, Option, CaseInsensitiveLess>;
/// The Option class implements each option as specified by the UCI protocol
class Option {
typedef void (*OnChange)(const Option&);
using OnChange = void (*)(const Option&);
public:
Option(OnChange = nullptr);
@@ -61,7 +61,7 @@ public:
Option& operator=(const std::string&);
void operator<<(const Option&);
operator double() const;
operator int() const;
operator std::string() const;
bool operator==(const char*) const;
+11 -11
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,13 +38,13 @@ UCI::OptionsMap Options; // Global object
namespace UCI {
/// 'On change' actions, triggered by an option's value change
void on_clear_hash(const Option&) { Search::clear(); }
void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option& o) { Threads.set(size_t(o)); }
void on_tb_path(const Option& o) { Tablebases::init(o); }
void on_use_NNUE(const Option& ) { Eval::NNUE::init(); }
void on_eval_file(const Option& ) { Eval::NNUE::init(); }
static void on_clear_hash(const Option&) { Search::clear(); }
static void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
static void on_logger(const Option& o) { start_logger(o); }
static void on_threads(const Option& o) { Threads.set(size_t(o)); }
static void on_tb_path(const Option& o) { Tablebases::init(o); }
static void on_use_NNUE(const Option&) { Eval::NNUE::init(); }
static void on_eval_file(const Option&) { Eval::NNUE::init(); }
/// Our case insensitive less() function as required by UCI protocol
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
@@ -73,7 +73,7 @@ void init(OptionsMap& o) {
o["UCI_Chess960"] << Option(false);
o["UCI_AnalyseMode"] << Option(false);
o["UCI_LimitStrength"] << Option(false);
o["UCI_Elo"] << Option(1350, 1350, 2850);
o["UCI_Elo"] << Option(1320, 1320, 3190);
o["UCI_ShowWDL"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100);
@@ -128,9 +128,9 @@ Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(min
Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f)
{ defaultValue = v; currentValue = cur; }
Option::operator double() const {
Option::operator int() const {
assert(type == "check" || type == "spin");
return (type == "spin" ? stof(currentValue) : currentValue == "true");
return (type == "spin" ? std::stoi(currentValue) : currentValue == "true");
}
Option::operator std::string() const {
+7 -1
View File
@@ -70,7 +70,8 @@ for args in "eval" \
"go depth 10" \
"go movetime 1000" \
"go wtime 8000 btime 8000 winc 500 binc 500" \
"bench 128 $threads 8 default depth"
"bench 128 $threads 8 default depth" \
"export_net verify.nnue"
do
echo "$prefix $exeprefix ./stockfish $args $postfix"
@@ -78,6 +79,11 @@ do
done
# verify the generated net equals the base net
network=`./stockfish uci | grep 'option name EvalFile type string default' | awk '{print $NF}'`
echo "Comparing $network to the written verify.nnue"
diff $network verify.nnue
# more general testing, following an uci protocol exchange
cat << EOF > game.exp
set timeout 240