diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 00000000..06dd8455
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,163 @@
+name: CD
+
+on:
+ push:
+ tags:
+ - '*'
+ workflow_dispatch:
+
+jobs:
+ Linux-OpenCL:
+ name: Linux OpenCL
+
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ fetch-tags: true
+ - name: Script
+ run: |
+ docker run --rm -i -v "$GITHUB_WORKSPACE:/workspace" -w /workspace centos:7 bash -s <<'EOF'
+ set -e -o pipefail
+ sed -i -e '/^mirrorlist/d;/^#baseurl=/{s,^#,,;s,/mirror,/vault,;}' /etc/yum.repos.d/CentOS*.repo
+ # yum update -y
+ yum install -y centos-release-scl epel-release
+ sed -i -e '/^mirrorlist/d;/^# *baseurl=/{s,^# *,,;s,/mirror,/vault,;}' /etc/yum.repos.d/CentOS*.repo
+ yum install -y devtoolset-11-gcc-c++ devtoolset-11-libstdc++-static make git ocl-icd-devel
+
+ source /opt/rh/devtoolset-11/enable
+ g++ --version
+ ldd --version
+
+ make STATIC_RUNTIME=1 -j "$(nproc)"
+ cp -vr README.* LICENSE tools/ build-release/
+ cd build-release
+ rm -f -- *.o
+ ./prpll -h
+ EOF
+ - uses: actions/upload-artifact@v7
+ with:
+ name: PRPLL-NTT_linux_x64_opencl
+ path: build-release/*
+
+ Linux-CUDA:
+ name: Linux CUDA
+
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - cuda: '13.2.1'
+ container: 'rockylinux8'
+ - cuda: '12.9.2'
+ container: 'rockylinux8'
+ - cuda: '11.8.0'
+ container: 'centos7'
+ - cuda: '10.2'
+ container: 'centos7'
+ fail-fast: false
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ fetch-tags: true
+ - name: Script
+ run: |
+ docker run --rm -i -v "$GITHUB_WORKSPACE:/workspace" -w /workspace "nvcr.io/nvidia/cuda:${{ matrix.cuda }}-devel-${{ matrix.container }}" bash -s <<'EOF'
+ set -e -o pipefail
+ if [[ "${{ matrix.container }}" == centos* ]]; then
+ sed -i -e '/^mirrorlist/d;/^#baseurl=/{s,^#,,;s,/mirror,/vault,;}' /etc/yum.repos.d/CentOS*.repo
+ # yum update -y
+ yum install -y centos-release-scl epel-release
+ sed -i -e '/^mirrorlist/d;/^# *baseurl=/{s,^# *,,;s,/mirror,/vault,;}' /etc/yum.repos.d/CentOS*.repo
+ yum install -y devtoolset-11-gcc-c++ devtoolset-11-libstdc++-static make git
+ source /opt/rh/devtoolset-11/enable
+ else
+ # dnf update -y
+ dnf install -y gcc-toolset-11-gcc-c++ make git
+ source /opt/rh/gcc-toolset-11/enable
+ fi
+
+ g++ --version
+ ldd --version
+
+ make CUDA=1 STATIC_RUNTIME=1 STATIC_CUDA=${{ matrix.cuda != '10.2' && '1' || '0' }} -j "$(nproc)"
+ cp -vr README.* LICENSE tools/ build-cuda/
+ cd build-cuda
+ rm -f -- *.o
+ if [[ "${{ matrix.cuda }}" == "10.2" ]]; then
+ cp -v /usr/local/cuda/lib64/{libnvrtc.so.10.2,libnvrtc-builtins.so.10.2} .
+ fi
+ # ./prpll -h
+ EOF
+ - uses: actions/upload-artifact@v7
+ with:
+ name: PRPLL-NTT_linux_x64_cuda_${{ matrix.cuda }}
+ path: build-cuda/*
+
+ Windows-OpenCL:
+ name: Windows OpenCL
+
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ fetch-tags: true
+ - uses: step-security/msvc-dev-cmd@v1
+ - name: Install OpenCL
+ run: |
+ vcpkg install opencl
+ - name: Before Script
+ shell: bash
+ run: |
+ bash genbundle.sh src/cuda/*.cuh src/cl/*.cl > src/bundle.cpp
+ printf '"%s"\n' "$(basename "$(git describe --tags --long --always)")" > src/version.inc
+ - name: Script
+ run: |
+ msbuild PRPLL.sln /m /p:Configuration=OpenCL-Release /p:StaticRuntime=true /p:OpenCLRoot=C:\vcpkg\installed\x64-windows
+ Copy-Item -Recurse README.*, LICENSE, tools\ build-msvc\OpenCL\Release\
+ cd build-msvc\OpenCL\Release\
+ & .\prpll -h
+ - uses: actions/upload-artifact@v7
+ with:
+ name: PRPLL-NTT_win_x64_opencl
+ path: build-msvc/OpenCL/Release/*
+
+ Windows-CUDA:
+ name: Windows CUDA
+
+ runs-on: windows-2022
+ strategy:
+ matrix:
+ cuda: ['13.2.1', '12.9.2', '11.8.0', '10.2.89']
+ fail-fast: false
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ fetch-tags: true
+ - uses: step-security/msvc-dev-cmd@v1
+ - name: Install CUDA Toolkit
+ uses: N-Storm/cuda-toolkit@v0.2.34
+ with:
+ cuda: ${{ matrix.cuda }}
+ - name: Before Script
+ shell: bash
+ run: |
+ bash genbundle.sh src/cuda/*.cuh src/cl/*.cl > src/bundle.cpp
+ printf '"%s"\n' "$(basename "$(git describe --tags --long --always)")" > src/version.inc
+ - name: Script
+ run: |
+ msbuild PRPLL.sln /m /p:Configuration=CUDA-Release /p:StaticRuntime=true /p:StaticCUDA=${{ matrix.cuda != '10.2.89' && 'true' || 'false' }}
+ Copy-Item -Recurse README.*, LICENSE, tools\ build-msvc\CUDA\Release\
+ cd build-msvc\CUDA\Release\
+ if ("${{ matrix.cuda }}" -eq "10.2.89") {
+ Copy-Item "$env:CUDA_PATH\bin\nvrtc64_102_0.dll", "$env:CUDA_PATH\bin\nvrtc-builtins64_102.dll" .
+ }
+ # & .\prpll -h
+ - uses: actions/upload-artifact@v7
+ with:
+ name: PRPLL-NTT_win_x64_cuda_${{ matrix.cuda }}
+ path: build-msvc/CUDA/Release/*
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3947c982..8037acef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,60 +4,227 @@ on:
push:
pull_request:
schedule:
- - cron: '0 0 1 * *'
+ - cron: '0 0 1 * *'
jobs:
- Linux:
- name: Linux
+ Kernel-Smoke:
+ name: Kernel compile + Gerbicz check (POCL CPU, ${{ matrix.os }})
+ # The kernels in src/cl are compiled at first use, on the device, so the build jobs below never see them.
+ # This compiles the FP64 kernel set on POCL's CPU device -- a non-NVIDIA OpenCL target -- and ends in a
+ # Gerbicz check. Two runners: the arm64 entry puts the kernels through LLVM's AArch64 backend and runs
+ # the host code on arm64, which the build-only arm jobs above never do. Debug build so the runtime
+ # asserts are exercised too. The integer-NTT types are left out (far too slow on a CPU device).
+ #
+ # POCL_WORK_GROUP_METHOD=loops: with the default (vectorizing) work-group method POCL spends 15-25 minutes
+ # compiling these kernels on the AVX-512 runner CPUs; with "loops" and POCL 7.2 the whole check takes about
+ # 10 seconds. Ubuntu's own POCL packages (5.0 on 24.04, 6.0 on 26.04) are slow even with "loops" and the
+ # conda-forge 7.1 build computes wrong results, so POCL 7.2 is built from source against the distro LLVM
+ # and cached; a cache hit costs seconds.
+ # Both runners are handled by the same commands: 26.04-arm has the same LLVM 21 as 26.04.
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-26.04, ubuntu-26.04-arm]
+ env:
+ POCL_WORK_GROUP_METHOD: loops
+ POCL_VERSION: "7.2"
+ steps:
+ - uses: actions/checkout@v7
+ - name: Install
+ # Installed unconditionally: the cached libpocl links against the LLVM runtime libraries, so they are
+ # needed on a cache hit too, not only to build POCL. The 26.04 images already carry llvm-21-dev and
+ # clang-21, but not libclang-cpp21-dev (which POCL needs); naming them all keeps this independent of
+ # what a future image happens to ship.
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y ocl-icd-opencl-dev cmake ninja-build llvm-21-dev libclang-21-dev libclang-cpp21-dev clang-21 libhwloc-dev
+ - name: Restore POCL
+ id: pocl-cache
+ uses: actions/cache@v4
+ with:
+ path: ~/pocl
+ key: pocl-${{ env.POCL_VERSION }}-${{ runner.os }}-${{ runner.arch }}-${{ matrix.os }}-llvm21
+ - name: Build POCL
+ if: steps.pocl-cache.outputs.cache-hit != 'true'
+ run: |
+ curl -sSLf "https://github.com/pocl/pocl/archive/refs/tags/v$POCL_VERSION.tar.gz" -o pocl.tar.gz
+ tar -xzf pocl.tar.gz
+ cmake -G Ninja -S "pocl-$POCL_VERSION" -B pocl-build -DCMAKE_BUILD_TYPE=Release -DWITH_LLVM_CONFIG=/usr/bin/llvm-config-21 \
+ -DENABLE_ICD=ON -DENABLE_TESTS=OFF -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX="$HOME/pocl" -DPOCL_INSTALL_ICD_VENDORDIR="$HOME/pocl/etc/OpenCL/vendors"
+ ninja -C pocl-build install
+ - name: Use POCL
+ run: echo "OCL_ICD_VENDORS=$HOME/pocl/etc/OpenCL/vendors" >> "$GITHUB_ENV"
+ - name: Build
+ run: make DEBUG=1 -O -j "$(nproc)"
+ - name: Compile the kernels and pass a Gerbicz check
+ # Step-level bound (not job-level, so a slow apt mirror cannot fail the job): a correct run takes
+ # well under a minute each. prpll ignores SIGTERM while inside an OpenCL compile, hence -s KILL.
+ #
+ # The same check runs under a few -use settings and the residues must all agree. A -use knob is a
+ # performance setting, so it cannot change the answer: after a fixed number of iterations the residue
+ # depends only on the exponent. That makes a one-line invariant out of the failure this code is most
+ # prone to -- a kernel that is correct in the shipped configuration and computes the wrong thing in
+ # another -- which a single-configuration run cannot see at all. TAIL_KERNELS=0 swaps the double-wide
+ # tail kernels for single-wide ones, and WMUL=1 changes how carryFused partitions its workgroup.
+ timeout-minutes: 25
+ run: |
+ cd build-debug
+ ./prpll -h
+ want=""
+ for cfg in "" "-use TAIL_KERNELS=0" "-use WMUL=1"; do
+ # Start each run from scratch: left alone, prpll would resume from the previous run's savefile.
+ rm -rf gpuowl-0.log 5000011
+ # Dump the log whatever happened, so a timeout or crash still shows how far the kernels got.
+ # Steps run under "bash -e", so the run has to be guarded: unguarded, a failing run ends the step
+ # right there and the dump never happens. prpll's own stdout is block-buffered into the runner's
+ # pipe and is lost when it is killed, so the log file is the only record.
+ rc=0
+ timeout -s KILL 6m ./prpll -device 0 -prp 5000011 -fft 256:2:256 -block 200 -iters 400 $cfg || rc=$?
+ cat gpuowl-0.log || true
+ test "$rc" -eq 0
+ res=$(grep -aoE 'OK +400 +[0-9a-f]{16}' gpuowl-0.log | grep -oE '[0-9a-f]{16}$')
+ test -n "$res"
+ echo "residue with '${cfg:-defaults}': $res"
+ if [ -z "$want" ]; then
+ want=$res
+ elif [ "$res" != "$want" ]; then
+ echo "::error::-use changed the residue: '$cfg' gives $res, defaults give $want"
+ exit 1
+ fi
+ done
+
+ - name: Verify a proof under every FFT type
+ # The Gerbicz check above never reaches tailMulLow: that kernel is used only when a proof is built,
+ # so it can be wrong while everything above stays green. That is not hypothetical -- the double-wide
+ # tailMul read a whole line with the wrong lane and no test here noticed, because a PRP run and its
+ # Gerbicz check are both perfectly happy without it.
+ #
+ # Verifying a known-good proof closes that hole cheaply: it runs tailSquare and tailMulLow for a few
+ # thousand iterations, once per FFT type, and the six here are the ones allShapes() enumerates, so
+ # every number type (FP64, FP32, GF31, GF61) and every hybrid of them gets exercised. The proof is
+ # type-agnostic: it is a list of residues, so one file checks all six.
+ timeout-minutes: 30
+ run: |
+ cd build-debug
+ for t in 0 1 2 3 4 51; do
+ rm -rf gpuowl-0.log 786433
+ rc=0
+ timeout -s KILL 10m ./prpll -device 0 -verify ../test/786433-10.proof -fft "$t:256:2:256" || rc=$?
+ cat gpuowl-0.log || true
+ test "$rc" -eq 0
+ done
+
+ Linux-OpenCL:
+ name: Linux OpenCL
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-22.04, ubuntu-24.04]
+ os: [ubuntu-22.04, ubuntu-24.04, ubuntu-26.04, ubuntu-22.04-arm, ubuntu-24.04-arm, ubuntu-26.04-arm]
cxx: [g++, clang++]
+ exclude:
+ - os: ubuntu-22.04-arm
+ cxx: clang++
fail-fast: false
env:
CXX: ${{ matrix.cxx }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Install
run: |
sudo apt-get update -y
- sudo apt-get install -y cppcheck ocl-icd-opencl-dev pocl-opencl-icd
+ sudo apt-get install -y ocl-icd-opencl-dev pocl-opencl-icd
$CXX --version
- name: Script
run: |
- make prpll -O -j "$(nproc)"
- cd build-release
+ make DEBUG=1 -O -j "$(nproc)"
+ cd build-debug
rm -f -- *.o
./prpll -h
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
if: always()
with:
- name: ${{ matrix.os }}_${{ matrix.cxx }}_prpll
- path: ${{ github.workspace }}
- - name: Cppcheck
+ name: ${{ matrix.os }}_${{ endsWith(matrix.os, '-arm') && 'arm' || 'x86' }}_${{ matrix.cxx }}_opencl_prpll
+ path: build-debug/
+
+ Linux-CUDA:
+ name: Linux CUDA
+
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-22.04, ubuntu-24.04, ubuntu-26.04]
+ cxx: [g++, clang++]
+ fail-fast: false
+ env:
+ CXX: ${{ matrix.cxx }}
+ steps:
+ - uses: actions/checkout@v7
+ - name: Install
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y nvidia-cuda-toolkit
+ $CXX --version
+ - name: Script
+ run: |
+ make DEBUG=1 CUDA=1 -O -j "$(nproc)"
+ cd build-debug
+ rm -f -- *.o
+ ./prpll -h
+ - uses: actions/upload-artifact@v7
+ if: always()
+ with:
+ name: ${{ matrix.os }}_${{ endsWith(matrix.os, '-arm') && 'arm' || 'x86' }}_${{ matrix.cxx }}_cuda_prpll
+ path: build-debug/
+
+ Cppcheck:
+ name: Cppcheck
+
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - name: Install
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y cppcheck
+ - name: Script
run: cppcheck --enable=all --force .
- - name: Clang-Tidy
- if: ${{ matrix.cxx == 'clang++' }}
+
+ Clang-Tidy:
+ name: Clang-Tidy
+
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - name: Script
run: clang-tidy -checks='bugprone-*,-bugprone-reserved-identifier,cert-*,-cert-dcl37-c,-cert-dcl51-cpp,clang-analyzer-*,concurrency-*,misc-*,-misc-no-recursion,modernize-*,-modernize-use-trailing-return-type,performance-*,portability-*,readability-const-return-type,readability-container-*,readability-duplicate-include,readability-else-after-return,readability-make-member-function-cons,readability-non-const-parameter,readability-redundant-*,readability-simplify-*,readability-string-compare,readability-use-*' -header-filter='.*' src/*.cpp -- -Wall -O3 -std=gnu++20
continue-on-error: true
- - name: ShellCheck
- run: shopt -s globstar; shellcheck -o avoid-nullary-conditions,check-extra-masked-returns,check-set-e-suppressed,deprecate-which,quote-safe-variables,require-double-brackets -s bash **/*.sh
+
+ ShellCheck:
+ name: ShellCheck
+
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - name: Script
+ run: shopt -s globstar; shellcheck -o avoid-nullary-conditions,check-set-e-suppressed,deprecate-which,quote-safe-variables,require-double-brackets -s bash **/*.sh
continue-on-error: true
- Windows:
- name: Windows
+ Windows-MSYS2:
+ name: Windows MSYS2
- runs-on: windows-latest
+ runs-on: ${{ matrix.os }}
strategy:
matrix:
+ os: [windows-latest] # windows-11-arm
cxx: [g++, clang++]
fail-fast: false
env:
CXX: ${{ matrix.cxx }}
+ PACKAGE_PREFIX: mingw-w64-${{ endsWith(matrix.os, '-arm') && 'clang-aarch64' || 'x86_64' }}-
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Before Install
run: |
echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
@@ -66,42 +233,99 @@ jobs:
echo "LIBPATH=-LC:\msys64\mingw64\lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Install
run: |
- pacman -S --noconfirm mingw-w64-x86_64-gmp mingw-w64-x86_64-opencl-icd
+ pacman -S --noconfirm "${env:PACKAGE_PREFIX}opencl-icd"
& $env:CXX --version
- name: Install Clang
if: ${{ matrix.cxx == 'clang++' }}
run: |
- pacman -S --noconfirm mingw-w64-x86_64-clang
+ pacman -S --noconfirm "${env:PACKAGE_PREFIX}clang"
& $env:CXX --version
- name: Script
- run: | # Cannot use `make exe`, as the OpenCL ICD Loader does not support static linking
- make prpll -O -j $env:NUMBER_OF_PROCESSORS
- cd build-release
+ run: |
+ make DEBUG=1 -O -j $env:NUMBER_OF_PROCESSORS
+ cd build-debug
rm *.o
.\prpll.exe -h
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
+ if: always()
+ with:
+ name: win_${{ endsWith(matrix.os, '-arm') && 'arm' || 'x86' }}_${{ matrix.cxx }}_prpll
+ path: build-debug/
+
+ Windows-MSVC-OpenCL:
+ name: Windows MSVC OpenCL
+
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/msvc-dev-cmd@v1
+ - name: Install OpenCL
+ run: |
+ vcpkg install opencl
+ - name: Before Script
+ shell: bash
+ run: |
+ bash genbundle.sh src/cuda/*.cuh src/cl/*.cl > src/bundle.cpp
+ printf '"%s"\n' "$(basename "$(git describe --tags --long --always)")" > src/version.inc
+ - name: Script
+ run: |
+ msbuild PRPLL.sln /m /p:Configuration=OpenCL-Debug /p:OpenCLRoot=C:\vcpkg\installed\x64-windows
+ cd build-msvc\OpenCL\Debug\
+ & .\prpll -h
+ - uses: actions/upload-artifact@v7
if: always()
with:
- name: win_${{ matrix.cxx }}_prpll
- path: ${{ github.workspace }}
+ name: win_x86_msvc_opencl_prpll
+ path: build-msvc/
+
+ Windows-MSVC-CUDA:
+ name: Windows MSVC CUDA
+
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/msvc-dev-cmd@v1
+ - name: Install CUDA Toolkit
+ uses: N-Storm/cuda-toolkit@v0.2.34
+ - name: Before Script
+ shell: bash
+ run: |
+ bash genbundle.sh src/cuda/*.cuh src/cl/*.cl > src/bundle.cpp
+ printf '"%s"\n' "$(basename "$(git describe --tags --long --always)")" > src/version.inc
+ - name: Script
+ run: |
+ msbuild PRPLL.sln /m /p:Configuration=CUDA-Debug
+ # cd build-msvc\CUDA\Debug\
+ # & .\prpll -h
+ - uses: actions/upload-artifact@v7
+ if: always()
+ with:
+ name: win_x86_msvc_cuda_prpll
+ path: build-msvc/
macOS:
name: macOS
- runs-on: macos-13
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [macos-26-intel, macos-latest]
+ fail-fast: false
+ env:
+ CXX: g++-15
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Install
run: |
- brew install gcc@14
+ $CXX --version
- name: Script
run: |
- make prpll -j "$(sysctl -n hw.ncpu)"
- cd build-release
+ make DEBUG=1 -j "$(sysctl -n hw.ncpu)"
+ cd build-debug
rm -f -- *.o
./prpll -h
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
if: always()
with:
- name: macos_prpll
- path: ${{ github.workspace }}
+ name: macos_${{ endsWith(matrix.os, '-intel') && 'x86' || 'arm' }}_prpll
+ path: build-debug/
diff --git a/Makefile b/Makefile
index 26bffeda..3d15b836 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,14 @@
-# Use "make DEBUG=1" for a debug build
+# Use "make CUDA=1" for a CUDA build, use "make DEBUG=1" for a debug build
# The build artifacts are put in the "build-release" subfolder (or "build-debug" for a debug build).
# On Windows invoke with "make exe" or "make all"
+DEBUG = 0
+CUDA = 0
+STATIC_RUNTIME = 0
+STATIC_CUDA = 0
+
# Uncomment below as desired to set a particular compiler or force a debug build:
# CXX = g++-12
# DEBUG = 1
@@ -12,41 +17,54 @@
HOST_OS = $(shell uname -s)
-ifeq ($(HOST_OS), Darwin)
-# Real GCC (not clang), needed for 128-bit floats and std::filesystem::path
-CXX = g++-14
+CXX ?= g++
+
+ifeq ($(CUDA), 1)
+ BIN=build-cuda
+ CUDASRCS1 = clwrap_cuda.cpp cudawrap.cpp
+ CUDAFLAGS = -DCUDA_BACKEND -Isrc/cuda -I/usr/local/cuda/include
+ CUDAOBJS = $(CUDASRCS1:%.cpp=$(BIN)/%.o)
+ ifeq ($(STATIC_CUDA), 1)
+ OPENCL_LIBS = -L/usr/local/cuda/lib64 -Wl,--start-group -lnvrtc_static -lnvrtc-builtins_static -lnvptxcompiler_static -Wl,--end-group -lcuda -lpthread -ldl
+ else
+ OPENCL_LIBS = -L/usr/local/cuda/lib64 -Wl,-rpath,'$$ORIGIN' -lnvrtc -lcuda -lpthread
+ endif
else
-CXX = g++
+ BIN=build-release
+ CUDAFLAGS =
+ CUDAOBJS =
+ ifeq ($(HOST_OS), Darwin)
+ OPENCL_LIBS = -framework OpenCL
+ else
+ OPENCL_LIBS = -lOpenCL -lpthread
+ endif
endif
-ifneq ($(findstring MINGW, $(HOST_OS)), MINGW)
-COMMON_FLAGS = -Wall -std=c++20 -static-libstdc++ -static-libgcc
-else
+COMMON_FLAGS = -Wall -Wextra $(CUDAFLAGS) -std=c++20
+
+ifeq ($(STATIC_RUNTIME),1)
+ LDFLAGS += -static-libstdc++ -static-libgcc
+
+ ifeq ($(findstring MINGW, $(HOST_OS)), MINGW)
# For mingw-64 use this:
-COMMON_FLAGS = -Wall -std=c++20 -static-libstdc++ -static-libgcc -static
+ LDFLAGS += -static
+ endif
endif
-# -fext-numeric-literals
-ifeq ($(HOST_OS), Darwin)
-OPENCL_LIBS = -framework OpenCL
-else
-OPENCL_LIBS = -lOpenCL
+ifeq ($(findstring MINGW, $(HOST_OS)), MINGW)
+ CPPFLAGS += -DWINVER=0x0601 -D_WIN32_WINNT=0x0601
+ LDFLAGS += -Wl,--subsystem,console:6.01
endif
-
+# -fext-numeric-literals
ifeq ($(DEBUG), 1)
BIN=build-debug
-
-CXXFLAGS = -g $(COMMON_FLAGS)
-STRIP=
+CXXFLAGS = -g -Og $(COMMON_FLAGS)
else
-BIN=build-release
-
-CXXFLAGS = -O2 -DNDEBUG $(COMMON_FLAGS)
-STRIP=-s
+CXXFLAGS = -O3 -flto -DNDEBUG $(COMMON_FLAGS)
endif
@@ -56,7 +74,7 @@ SRCS2 = test.cpp
# SRCS=$(addprefix src/, $(SRCS1))
-OBJS = $(SRCS1:%.cpp=$(BIN)/%.o)
+OBJS = $(CUDAOBJS) $(SRCS1:%.cpp=$(BIN)/%.o)
DEPDIR := $(BIN)/.d
$(shell mkdir -p $(DEPDIR) >/dev/null)
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
@@ -70,35 +88,44 @@ prpll: $(BIN)/prpll
amd: $(BIN)/prpll-amd
#$(BIN)/test: $(BIN)/test.o
-# $(CXX) $(CXXFLAGS) -o $@ $< $(LIBPATH) ${STRIP}
+# $(CXX) $(CXXFLAGS) -o $@ $< $(LIBPATH)
$(BIN)/prpll: ${OBJS}
- $(CXX) $(CXXFLAGS) -o $@ ${OBJS} $(LIBPATH) $(OPENCL_LIBS) ${STRIP}
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ ${OBJS} $(LIBPATH) $(OPENCL_LIBS)
# Instead of linking with libOpenCL, link with libamdocl64
$(BIN)/prpll-amd: ${OBJS}
- $(CXX) $(CXXFLAGS) -o $@ ${OBJS} $(LIBPATH) -lamdocl64 -L/opt/rocm/lib ${STRIP}
+ $(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ ${OBJS} $(LIBPATH) -lamdocl64 -L/opt/rocm/lib
clean:
- rm -rf build-debug build-release
+ rm -rf build-debug build-release build-cuda
$(BIN)/%.o : src/%.cpp $(DEPDIR)/%.d
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
+$(BIN)/%.o : src/cuda/%.cpp $(DEPDIR)/%.d
+ $(COMPILE.cc) $(OUTPUT_OPTION) $<
+ $(POSTCOMPILE)
-# src/bundle.cpp is just a wrapping of the OpenCL sources (*.cl) as a C string.
+# src/bundle.cpp is just a wrapping of the OpenCL sources (*.cl) as a C string (as well as the CUDA OpenCL translation code)
-src/bundle.cpp: genbundle.sh src/cl/*.cl
- ./genbundle.sh $^ > src/bundle.cpp
+src/bundle.cpp: genbundle.sh src/cuda/*.cuh src/cl/*.cl
+ bash genbundle.sh $^ > src/bundle.cpp
$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d
src/version.cpp : src/version.inc
+# The version string compiled into the binary and reported to PrimeNet in
+# every result. Defaults to `git describe` of the checkout; a build from an
+# exported tree (no .git) or a packager that wants the upstream string passes
+# it explicitly: make VERSION=v8.0-57-g6cb4c12
+VERSION ?= $(shell basename `git describe --tags --long --dirty --always --match 'v/prpll/*'`)
+
src/version.inc: FORCE
- echo \"`basename \`git describe --tags --long --dirty --always --match v/prpll/*\``\" > $(BIN)/version.new
+ echo \"$(VERSION)\" > $(BIN)/version.new
diff -q -N $(BIN)/version.new $@ >/dev/null || mv $(BIN)/version.new $@
echo Version: `cat $@`
diff --git a/PRPLL.sln b/PRPLL.sln
new file mode 100644
index 00000000..6eaecdd1
--- /dev/null
+++ b/PRPLL.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PRPLL", "PRPLL.vcxproj", "{2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ OpenCL-Debug|x64 = OpenCL-Debug|x64
+ OpenCL-Release|x64 = OpenCL-Release|x64
+ CUDA-Debug|x64 = CUDA-Debug|x64
+ CUDA-Release|x64 = CUDA-Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.OpenCL-Debug|x64.ActiveCfg = OpenCL-Debug|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.OpenCL-Debug|x64.Build.0 = OpenCL-Debug|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.OpenCL-Release|x64.ActiveCfg = OpenCL-Release|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.OpenCL-Release|x64.Build.0 = OpenCL-Release|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.CUDA-Debug|x64.ActiveCfg = CUDA-Debug|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.CUDA-Debug|x64.Build.0 = CUDA-Debug|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.CUDA-Release|x64.ActiveCfg = CUDA-Release|x64
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}.CUDA-Release|x64.Build.0 = CUDA-Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5185D8CA-7025-4E3B-8190-85F2B7B4A526}
+ EndGlobalSection
+EndGlobal
diff --git a/PRPLL.vcxproj b/PRPLL.vcxproj
new file mode 100644
index 00000000..743cb33d
--- /dev/null
+++ b/PRPLL.vcxproj
@@ -0,0 +1,198 @@
+
+
+
+
+ OpenCL-Debug
+ x64
+
+
+ OpenCL-Release
+ x64
+
+
+ CUDA-Debug
+ x64
+
+
+ CUDA-Release
+ x64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+
+ 17.0
+ {2B3F9C8E-3384-0407-D9E1-D3E86AA8823A}
+ Win32Proj
+ PRPLL
+ PRPLL
+ 10.0
+
+
+
+ Application
+ true
+ MultiByte
+ v143
+
+
+ Application
+ false
+ true
+ MultiByte
+ v143
+
+
+
+
+
+
+
+
+ true
+ false
+
+
+ $(ProjectDir)build-msvc\OpenCL\Debug\
+ $(ProjectDir)build-msvc\obj\OpenCL\Debug\
+ prpll
+ true
+
+
+ $(ProjectDir)build-msvc\OpenCL\Release\
+ $(ProjectDir)build-msvc\obj\OpenCL\Release\
+ prpll
+
+
+ $(ProjectDir)build-msvc\CUDA\Debug\
+ $(ProjectDir)build-msvc\obj\CUDA\Debug\
+ prpll
+ true
+
+
+ $(ProjectDir)build-msvc\CUDA\Release\
+ $(ProjectDir)build-msvc\obj\CUDA\Release\
+ prpll
+
+
+
+ Level4
+ WIN32;WIN64;_CONSOLE;_CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions)
+ stdcpp20
+ Sync
+ true
+
+
+ Console
+
+
+
+
+ Disabled
+ _DEBUG;%(PreprocessorDefinitions)
+ MultiThreadedDebug
+ MultiThreadedDebugDLL
+ ProgramDatabase
+ EnableFastChecks
+
+
+ true
+
+
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;%(PreprocessorDefinitions)
+ MultiThreaded
+ MultiThreadedDLL
+
+
+ true
+ true
+ true
+
+
+
+
+ WINVER=0x0601;_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)
+
+
+ /SUBSYSTEM:CONSOLE,6.01 %(AdditionalOptions)
+
+
+
+
+ $(OpenCLRoot)\debug\lib;%(AdditionalLibraryDirectories)
+ OpenCL.lib;%(AdditionalDependencies)
+
+
+
+
+ $(OpenCLRoot)\lib;%(AdditionalLibraryDirectories)
+ OpenCL.lib;%(AdditionalDependencies)
+
+
+
+
+ CUDA_BACKEND;%(PreprocessorDefinitions)
+ $(ProjectDir)\src\cuda;$(CUDA_PATH)\include;%(AdditionalIncludeDirectories)
+
+
+ $(CUDA_PATH)\lib\x64;%(AdditionalLibraryDirectories)
+
+
+
+
+ cuda.lib;nvrtc.lib;Ws2_32.lib;%(AdditionalDependencies)
+
+
+
+
+ cuda.lib;nvrtc_static.lib;nvrtc-builtins_static.lib;nvptxcompiler_static.lib;User32.lib;Ws2_32.lib;%(AdditionalDependencies)
+
+
+
+
+
diff --git a/README.md b/README.md
index 5ae916af..ba6b9ada 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
PRPLL implements two primality tests for Mersenne numbers: PRP ("PRobable Prime") and LL ("Lucas-Lehmer") as the name suggests.
-PRPLL is an OpenCL (GPU) program for primality testing Mersenne numbers.
+PRPLL is an OpenCL (GPU) and CUDA program for primality testing Mersenne numbers.
## Build
@@ -32,12 +32,21 @@ Invoke `make` in the source directory.
See `prpll -h` for the command line options.
+## License
+
+This project is licensed under the **GNU General Public License v3.0** - see [LICENSE](LICENSE) for details.
+
+
+## Credits
+
+[PRPLL](https://github.com/preda/gpuowl) was originally authored (as gpuowl) by **Mihai Preda**. **George Woltman** authored optimizations and NTT code contributions. The CUDA backend was authored by **"Sherpa"** in honor of John Allen Frey.
+
+
## Why LL
For Mersenne primes search, the PRP test is by far preferred over LL, such that LL is not used anymore for search.
But LL is still used to verify a prime found by PRP (which is a very rare occurence).
-
### Lucas-Lehmer (LL)
This is a test that proves whether a Mersenne number is prime or not, but without providing a factor in the case where it is not prime.
The Lucas-Lehmer test is very simple to describe: iterate the function f(x)=(x^2 - 2) modulo M(p) starting with the number 4. If
diff --git a/README.txt b/README.txt
index 64b8dcd9..825fb6c3 100644
--- a/README.txt
+++ b/README.txt
@@ -9,6 +9,7 @@ In the gpuowl project directory (where the file Makefile is located) run make.
This will produce a file "prpll" in the build-debug or build-release subdirectory.
Use "make" to do a release build in the "build-release" subdirectory.
+Use "make CUDA=1" to produce a CUDA version in the "build-cuda" subdirectory.
Use "make DEBUG=1" to produce a debug build in the "build-debug" subdirectory.
Use "make exe" for a Windows build.
Use "make clean" to remove the "build-debug" and "build-release" directories.
@@ -28,3 +29,6 @@ Run
you need to fix your OpenCL installation first.
2. run "prpll -h", and verify that it displays a list of devices towards the end.
+
+3. run "prpll -tune", to tune various PRPLL options for your GPU.
+
diff --git a/genbundle.sh b/genbundle.sh
index ec042bb2..d13521f8 100755
--- a/genbundle.sh
+++ b/genbundle.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
cat < CL_FILE_NAMES\{${names}\}\;
+echo "static const std::vector CL_FILE_NAMES{${names}};"
cat <& getClFileNames() { return CL_FILE_NAMES; }
diff --git a/src/AllocTrac.cpp b/src/AllocTrac.cpp
index b951bcd8..1995b719 100644
--- a/src/AllocTrac.cpp
+++ b/src/AllocTrac.cpp
@@ -1,7 +1,6 @@
// Copyright (C) Mihai Preda.
#include "AllocTrac.h"
-#include
std::atomic AllocTrac::totalAlloc = 0;
size_t AllocTrac::maxAlloc = size_t(15) * 1024 * 1024 * 1024; // 15 GB
diff --git a/src/AllocTrac.h b/src/AllocTrac.h
index 2bcedb4c..0c3169c4 100644
--- a/src/AllocTrac.h
+++ b/src/AllocTrac.h
@@ -36,8 +36,8 @@ class AllocTrac {
AllocTrac(const AllocTrac&) = delete;
void operator=(const AllocTrac&) = delete;
- AllocTrac(AllocTrac&& rhs) : size(rhs.size) { rhs.size = 0; }
- AllocTrac& operator=(AllocTrac&& rhs) {
+ AllocTrac(AllocTrac&& rhs) noexcept : size(rhs.size) { rhs.size = 0; }
+ AllocTrac& operator=(AllocTrac&& rhs) noexcept {
AllocTrac tmp{std::move(rhs)};
swap(*this, tmp);
return *this;
diff --git a/src/Args.cpp b/src/Args.cpp
index 041202ad..bb6bbe54 100644
--- a/src/Args.cpp
+++ b/src/Args.cpp
@@ -2,13 +2,14 @@
#include "Args.h"
#include "File.h"
-#include "FFTConfig.h"
#include "clwrap.h"
#include "gpuid.h"
#include "Proof.h"
+#include "version.h"
#include
#include
+#include
#include
#include
#include
@@ -16,6 +17,9 @@
#include
#include
+// This is a copy of the args.verbose level. It allows the CUDA wrapper to access the value.
+int prpll_verbose = 0;
+
int Args::value(const string& key, int valNotFound) const {
auto it = flags.find(key);
if (it == flags.end()) { return valNotFound; }
@@ -50,7 +54,7 @@ vector Args::splitArgLine(const string& inputLine) {
ret.push_back({prev, {}});
prev = s;
} else {
- ret.push_back({prev, s});
+ ret.emplace_back(prev, s);
prev.clear();
}
}
@@ -65,14 +69,14 @@ vector Args::splitArgLine(const string& inputLine) {
// Splits a string of the form "Foo=bar,C,D=1" into key=value pairs, with value defaulting to "1".
vector Args::splitUses(string ss) { // pass by value is intentional
vector ret;
- std::replace(ss.begin(), ss.end(), ',', ' ');
+ std::ranges::replace(ss, ',', ' ');
std::istringstream iss{ss};
- vector uses{std::istream_iterator{iss}, std::istream_iterator{}};
+ vector const uses{std::istream_iterator{iss}, std::istream_iterator{}};
for (const string &s : uses) {
auto pos = s.find('=');
- string key = (pos == string::npos) ? s : s.substr(0, pos);
- string val = (pos == string::npos) ? "1"s : s.substr(pos+1);
- ret.push_back({key, val});
+ string const key = (pos == string::npos) ? s : s.substr(0, pos);
+ string const val = (pos == string::npos) ? "1"s : s.substr(pos+1);
+ ret.emplace_back(key, val);
}
return ret;
}
@@ -86,7 +90,7 @@ void Args::readConfig(const fs::path& path) {
}
}
-u32 Args::getProofPow(u32 exponent) const {
+u32 Args::getProofPow(u64 exponent) const {
if (proofPow == -1) { return ProofSet::bestPower(exponent); }
assert(proofPow >= 1);
return proofPow;
@@ -94,19 +98,18 @@ u32 Args::getProofPow(u32 exponent) const {
string Args::tailDir() const { return fs::path{dir}.filename().string(); }
-bool Args::hasFlag(const string& key) const { return flags.find(key) != flags.end(); }
+bool Args::hasFlag(const string& key) const { return flags.contains(key); }
void Args::printHelp() {
printf(R"(
PRPLL is "PRobable Prime and Lucas-Lehmer Categorizer", AKA "Purple-cat"
-PRPLL is under active development and not ready for production use.
-PRPLL is an OpenCL (GPU) program for primality testing Mersenne numbers (of the form 2^n - 1).
+PRPLL is an OpenCL/CUDA (GPU) program for primality testing Mersenne numbers (of the form 2^n - 1).
To check that OpenCL is installed correctly use the command "clinfo". If clinfo does not find any
devices or otherwise fails, this program will not run.
-This program is tested on Linux/ROCm (AMD GPUs); it may also run on Windows and on Nvidia GPUs.
+This program is tested on Linux/ROCm (AMD GPUs); it also runs on Windows and on Nvidia GPUs.
For information about Mersenne primes search see https://www.mersenne.org/
@@ -117,8 +120,7 @@ and should be able to run.
Worktodo:
PRPLL keeps the active tasks in per-worker files worktodo-0.txt, worktodo-1.txt etc in the local directory.
These per-worker files are supplied from the global worktodo.txt file if -pool is used.
-In turn the global worktodo.txt can be supplied through the primenet.py script,
-either the one located at gpuowl/tools/primenet.py or https://download.mersenne.ca/primenet.py
+In turn the work files can be supplied through AutoPrimeNet, located at https://download.mersenne.ca/AutoPrimeNet
It is also possible to manually add exponents by adding lines of the form "PRP=118063003" to worktodo-.txt
@@ -167,17 +169,12 @@ named "config.txt" in the prpll run directory.
-cache : use binary kernel cache; useful with repeated use of -roeTune and -tune
-roe : measure the Round-Off Error (Z) for more iterations (slow)
--use : comma separated list of defines for configuring gpuowl.cl, such as:
- -use FAST_BARRIER: on AMD Radeon VII and older AMD GPUs, use a faster barrier(). Do not use
- this option on Nvidia GPUs or on RDNA AMD GPUs where it produces errors
+-use : comma separated list of defines for configuring openCL code, such as:
+ -use FAST_BARRIER: on AMD Radeon VII and older AMD GPUs, use a faster barrier(). This option
+ may not work on Nvidia GPUs or on RDNA AMD GPUs where it produces errors
(which are nevertheless detected).
-use NO_ASM : do not use __asm() blocks (inline assembly)
- -use STATS= : enable carry statistics collection & logging, for the kernel according to :
- 1 = CarryFused
- 2 = CarryFusedMul
- 4 = CarryA
- 8 = CarryMul
- -use TAIL_KERNELS= : change how tailSquare operates according to :
+ -use TAIL_KERNELS= : change how tailSquare and tailMul operate according to :
0 = single wide, single kernel
1 = single wide, two kernels
2 = double wide, single kernel
@@ -196,15 +193,21 @@ named "config.txt" in the prpll run directory.
1 = All trig values are pre-computed and read from memmory.
-use DEBUG : enable asserts in OpenCL kernels (slow, developers)
+ -use STATS= : enable carry statistics collection & logging (developers), for the kernel according to :
+ 1 = CarryFused, 2 = CarryFusedMul, 4 = CarryA, 8 = CarryMul
-tune : Looks for best settings to include in config.txt. Times many FFTs to find fastest one to test exponents -- written to tune.txt.
An -fft can be given on the command line to limit which FFTs are timed.
Options are not required. If present, the options are a comma separated list from below.
- noconfig - Skip timings to find best config.txt settings
+ noconfig - Skip timings to find best config.txt settings.
+ inplace - Skip timings for not-in-place FFTs and NTTs. All nVidia GPUs seem to prefer in-place FFTs and NTTs.
fp64 - Tune for settings that affect FP64 FFTs. Time FP64 FFTs for tune.txt.
ntt - Tune for settings that affect integer NTTs. Time integer NTTs for tune.txt.
+ nofp32 - Do not tune for settings that affect FP32 FFTs. Some openCL compilers have trouble with FP32.
minexp= - Time FFTs to find the best one for exponents greater than .
maxexp= - Time FFTs to find the best one for exponents less than .
+ fp6431 - Time FP64+M31 FFTs for tune.txt. Only GPUs with great FP64 performance will find this beneficial.
+ quick= - Use higher values for a quicker, potentially less accurate tune. Val ranges from 1 to 10.
-device : select the GPU at position N in the list of devices
-uid : select the GPU with the given UID (on ROCm/AMDGPU, Linux)
-pci : select the GPU with the given PCI BDF, e.g. "0c:00.0"
@@ -219,7 +222,7 @@ Device selection : use one of -uid , -pci , -device , see the list
}
for (unsigned i = 0; i < deviceIds.size(); ++i) {
cl_device_id id = deviceIds[i];
- string bdf = getBdfFromDevice(id);
+ string const bdf = getBdfFromDevice(id);
printf("%2u : %7s | %16s | %-24s | %s | %s\n",
i,
bdf.c_str(),
@@ -238,7 +241,7 @@ Device selection : use one of -uid , -pci , -device , see the list
u32 activeSize = 0;
float maxBpw = 0;
string variants;
- for (enum FFT_TYPES type : {FFT64, FFT3161, FFT3261, FFT61}) {
+ for (enum FFT_TYPES const type : {FFT64, FFT3161, FFT3261, FFT61}) {
for (auto c : configs) {
if (c.fft_type != type) continue;
if (c.size() != activeSize) {
@@ -268,9 +271,12 @@ void Args::parse(const string& line) {
// conditional defines predicated on a FFT
char fftBuf[32];
char configBuf[256];
- sscanf(line.c_str(), "! %31s %255s", fftBuf, configBuf);
- string fft = fftBuf;
- string config = configBuf;
+ if (sscanf(line.c_str(), "! %31s %255s", fftBuf, configBuf) != 2) { // otherwise the buffers are uninitialised
+ log("config line ignored (expected \"! \"): \"%s\"\n", line.c_str());
+ return;
+ }
+ string const fft = fftBuf;
+ string const config = configBuf;
perFftConfig[fft] = splitUses(config);
return;
}
@@ -284,10 +290,14 @@ void Args::parse(const string& line) {
if (key == "-h" || key == "--help") {
printHelp();
throw "help";
- } else if (key == "-version") {
- // log("PRPLL %s\n", VERSION);
+ } if (key == "-version") {
+ // Plain stdout, no log prefix: the flag exists for scripts and launchers
+ // that record which build wrote a result (Task.cpp reports VERSION to
+ // PrimeNet), so the one line must be the version and nothing else.
+ printf("%s\n", (VERSION[0] == 'v') ? VERSION + 1 : VERSION);
+ fflush(stdout);
throw "version";
- } else if (key == "-info") {
+ } if (key == "-info") {
if (s.empty()) {
log("-info expects an FFT spec, e.g. -info 1K:13:256\n");
throw "-info ";
@@ -296,12 +306,12 @@ void Args::parse(const string& line) {
for (const FFTShape& shape : FFTShape::multiSpec(s)) {
for (u32 variant = 0; variant <= LAST_VARIANT; variant = next_variant (variant)) {
if (variant != LAST_VARIANT && shape.fft_type != FFT64) continue;
- FFTConfig fft{shape, variant, CARRY_AUTO};
+ FFTConfig const fft{shape, variant, CARRY_AUTO};
log("%12s | %.2f | %5.1f\n", fft.spec().c_str(), fft.maxBpw(), fft.maxExp() / 1'000'000.0);
}
}
throw "info";
- } else if (key == "-od") {
+ } if (key == "-od") {
double od = stod(s);
fftOverdrive = 1 + od / 1000;
} else if (key == "-roe") {
@@ -310,15 +320,17 @@ void Args::parse(const string& line) {
} else if (key == "-tune") {
doTune = true;
if (!s.empty()) { tune = s; }
- } else if (key == "-ctune") {
- doCtune = true;
- if (!s.empty()) { ctune.push_back(s); }
+// } else if (key == "-ctune") {
+// doCtune = true;
+// if (!s.empty()) { ctune.push_back(s); }
} else if (key == "-ztune") {
doZtune = true;
} else if (key == "-carryTune") {
carryTune = true;
} else if (key == "-verbose" || key == "-v") {
- verbose = true;
+ if (s.empty()) verbose = 1;
+ else verbose = stoi(s);
+ prpll_verbose = verbose;
} else if (key == "-time") {
profile = true;
} else if (key == "-workers") {
@@ -363,11 +375,14 @@ void Args::parse(const string& line) {
}
}
else if (key == "-maxAlloc" || key == "-maxalloc") {
- assert(!s.empty());
+ if (s.empty()) { // s.back() below would be undefined
+ log("-maxAlloc expects a value, e.g. -maxAlloc 4G\n");
+ throw "-maxAlloc ";
+ }
u32 multiple = (s.back() == 'G') ? (1u << 30) : (1u << 20);
maxAlloc = size_t(stod(s) * multiple + .5);
}
- else if (key == "-iters") { iters = stoi(s); assert(iters && (iters % 10000 == 0)); }
+ else if (key == "-iters") { iters = stoi(s); assert(iters > 0); } // any positive count; release never enforced the old multiple-of-10000 rule
else if (key == "-prp" || key == "-PRP") { prpExp = stoll(s); }
else if (key == "-ll" || key == "-LL") { llExp = stoll(s); }
else if (key == "-smallest") { smallest = true; }
@@ -380,7 +395,7 @@ void Args::parse(const string& line) {
else if (key == "-dir") { dir = s; }
else if (key == "-carry") {
if (s == "short" || s == "long") {
- carry = s == "short" ? CARRY_SHORT : CARRY_LONG;
+ carry = s == "short" ? CARRY_32 : CARRY_64;
} else {
log("-carry expects short|long\n");
throw "-carry expects short|long";
@@ -393,8 +408,8 @@ void Args::parse(const string& line) {
}
} else if (key == "-log") {
logStep = stoi(s);
- if (logStep % 1000 != 0) {
- log("-log must be a multiple of 1000\n");
+ if (logStep == 0 || logStep % 1000 != 0) { // 0 would divide by zero in the PRP loop
+ log("-log must be a positive multiple of 1000\n");
throw "invalid log size";
}
} else if (key == "-use") {
@@ -408,7 +423,12 @@ void Args::parse(const string& line) {
} else if (key == "-unsafeMath") {
safeMath = false;
} else if (key == "-save") {
- nSavefiles = stoi(s);
+ int const n = stoi(s);
+ if (n < 1) { // 0 makes Saver::trimFiles index v[-1]
+ log("-save must be at least 1\n");
+ throw "invalid -save value";
+ }
+ nSavefiles = n;
} else {
log("Argument '%s' '%s' not understood\n", key.c_str(), s.c_str());
throw "args";
@@ -418,7 +438,9 @@ void Args::parse(const string& line) {
void Args::setDefaults() {
uid = getUidFromPos(device);
- log("device %d, OpenCL %s, unique id '%s'\n", device, getDriverVersionByPos(device).c_str(), uid.c_str());
+ cl_device_id dev = getDevice(device);
+ log("device %d, OpenCL %s, %s, unique id '%s'\n", device, getDriverVersionByPos(device).c_str(),
+ isAmdGpu(dev) ? getBoardName(dev).c_str() : getDeviceName(dev).c_str(), uid.c_str());
if (!masterDir.empty()) {
assert(masterDir.is_absolute());
diff --git a/src/Args.h b/src/Args.h
index 795cd99c..5f1a1a0c 100644
--- a/src/Args.h
+++ b/src/Args.h
@@ -3,6 +3,7 @@
#pragma once
#include "common.h"
+#include "FFTConfig.h"
#include
#include