diff --git a/.github/actions/setup-randlapack-deps-windows/action.yml b/.github/actions/setup-randlapack-deps-windows/action.yml new file mode 100644 index 00000000..0353a588 --- /dev/null +++ b/.github/actions/setup-randlapack-deps-windows/action.yml @@ -0,0 +1,63 @@ +name: setup-randlapack-deps-windows +description: > + Builds/restores RandLAPACK's native Windows dependencies (oneMKL via vcpkg, + GoogleTest, Random123, BLAS++, LAPACK++) and exports their locations as + environment variables (MKLROOT, googletest_PREFIX, Random123_DIR, blaspp_DIR, + lapackpp_DIR). Requires an initialized MSVC environment (ilammy/msvc-dev-cmd). + +inputs: + sanitize-address: + description: Build an AddressSanitizer-instrumented GoogleTest. + required: false + default: "false" + +runs: + using: composite + steps: + # Caches live beside the workspace, keyed on the setup script so any recipe + # change invalidates them. + - name: cache oneMKL (vcpkg) + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\vcpkg-installed + key: windows-vcpkg-intel-mkl-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: cache GoogleTest + if: inputs.sanitize-address != 'true' + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\googletest-install + key: windows-msvc-googletest-1.17.0-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: cache GoogleTest (ASan) + if: inputs.sanitize-address == 'true' + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\googletest-asan-install + key: windows-msvc-googletest-asan-1.17.0-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: cache Random123 + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\Random123-install + key: windows-random123-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: cache BLAS++ + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\blaspp-install + key: windows-msvc-blaspp-remove-symv-debug-print-ilp64-sequential-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: cache LAPACK++ + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\..\windows-deps\lapackpp-install + key: windows-msvc-lapackpp-msvc-direct-includes-ilp64-sequential-1-${{ hashFiles('.github/actions/setup-randlapack-deps-windows/setup.ps1') }} + + - name: build missing dependencies + shell: pwsh + run: | + $sanitizeAddress = '${{ inputs.sanitize-address }}' -eq 'true' + & "$env:GITHUB_ACTION_PATH/setup.ps1" ` + -DependencyRoot "${env:GITHUB_WORKSPACE}\..\windows-deps" ` + -SanitizeAddress:$sanitizeAddress diff --git a/.github/actions/setup-randlapack-deps-windows/setup.ps1 b/.github/actions/setup-randlapack-deps-windows/setup.ps1 new file mode 100644 index 00000000..174b0d1e --- /dev/null +++ b/.github/actions/setup-randlapack-deps-windows/setup.ps1 @@ -0,0 +1,253 @@ +# Builds and installs RandLAPACK's native Windows dependencies: +# - oneMKL (via vcpkg, ILP64 + sequential DLL set) +# - GoogleTest v1.17.0 +# - Random123 (headers only) +# - BLAS++ from BallisticLA/blaspp, branch remove-symv-debug-print +# - LAPACK++ from BallisticLA/lapackpp, branch msvc-direct-includes +# +# The BallisticLA branches carry the two one-line MSVC fixes that upstream +# icl-utk-edu has not merged yet (blaspp PR #132, lapackpp PR #87). Once those +# merge, both clones below can move back to upstream master. +# +# Every step is idempotent: work already present under -DependencyRoot (for +# example, restored from a CI cache) is left alone. Run from an MSVC developer +# environment (cl.exe on PATH). + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$DependencyRoot, + + [string]$VcpkgExecutable = "", + + # Use an existing oneMKL install (e.g. from the oneAPI installer) instead + # of fetching MKL through vcpkg. Must contain the ILP64 DLL import libs. + [string]$MklRoot = "", + + [switch]$SanitizeAddress +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +function Invoke-Checked { + param([string]$Program, [string[]]$Arguments) + & $Program @Arguments + if ($LASTEXITCODE -ne 0) { + throw "'$Program $($Arguments -join ' ')' failed with exit code $LASTEXITCODE." + } +} + +function Convert-ToCMakePath { + param([string]$Path) + return $Path.Replace('\', '/') +} + +function Find-PackageConfigDirectory { + # Install layouts of blaspp/lapackpp vary between revisions; search for the + # package config file instead of hardcoding lib/cmake/. + param([string]$InstallRoot, [string]$PackageName) + $config = Get-ChildItem -Path $InstallRoot -Recurse -Filter "${PackageName}Config.cmake" | + Select-Object -First 1 + if (-not $config) { + throw "Could not find ${PackageName}Config.cmake under $InstallRoot." + } + return $config.DirectoryName +} + +function Clone-Head { + param([string]$Url, [string]$Destination, [string]$Branch = "") + if (Test-Path $Destination) { + Write-Host "Reusing existing clone at $Destination" + return + } + $cloneArgs = @("clone", "--depth", "1") + if ($Branch -ne "") { $cloneArgs += @("--branch", $Branch) } + $cloneArgs += @($Url, $Destination) + Invoke-Checked "git" $cloneArgs +} + +function Export-GitHubValue { + # Publishes a value as a process env var, and to GITHUB_ENV/GITHUB_OUTPUT + # when running under GitHub Actions (harmless locally). + param([string]$Name, [string]$Value) + Set-Item -Path "Env:$Name" -Value $Value + if ($env:GITHUB_ENV) { Add-Content -Path $env:GITHUB_ENV -Value "$Name=$Value" } + if ($env:GITHUB_OUTPUT) { + $outputName = $Name.ToLowerInvariant().Replace('_', '-') + Add-Content -Path $env:GITHUB_OUTPUT -Value "$outputName=$Value" + } + Write-Host "$Name = $Value" +} + +# ---------------------------------------------------------------- guards ---- + +$resolvedRoot = [System.IO.Path]::GetFullPath($DependencyRoot) +if ($resolvedRoot -eq [System.IO.Path]::GetPathRoot($resolvedRoot)) { + throw "DependencyRoot must not be a filesystem root." +} +New-Item -ItemType Directory -Force -Path $resolvedRoot | Out-Null + +if (-not (Get-Command "cl.exe" -ErrorAction SilentlyContinue)) { + throw "cl.exe is not on PATH. Run from an MSVC developer environment (or ilammy/msvc-dev-cmd in CI)." +} + +# ---------------------------------------------------------------- oneMKL ---- + +if ($MklRoot -ne "") { + # Bring-your-own MKL (oneAPI installer layout: import libs in lib\ on + # current releases, lib\intel64 on older ones). + $mklRoot = [System.IO.Path]::GetFullPath($MklRoot) + $mklLibDir = "" + foreach ($candidate in @((Join-Path $mklRoot "lib"), (Join-Path $mklRoot "lib\intel64"))) { + if (Test-Path (Join-Path $candidate "mkl_intel_ilp64_dll.lib")) { + $mklLibDir = $candidate + break + } + } + if ($mklLibDir -eq "") { + throw "-MklRoot $MklRoot does not contain mkl_intel_ilp64_dll.lib under lib\ or lib\intel64\." + } + $mklBin = @((Join-Path $mklRoot "bin"), (Join-Path $mklRoot "redist\intel64")) | + Where-Object { Test-Path $_ } | Select-Object -First 1 + if (-not $mklBin) { throw "-MklRoot $MklRoot has no bin\ (or redist\intel64\) DLL directory." } + Write-Host "Using existing oneMKL at $mklRoot" +} else { + if ($VcpkgExecutable -eq "") { + foreach ($candidateRoot in @($env:VCPKG_INSTALLATION_ROOT, $env:VCPKG_ROOT)) { + if ($candidateRoot -and (Test-Path (Join-Path $candidateRoot "vcpkg.exe"))) { + $VcpkgExecutable = Join-Path $candidateRoot "vcpkg.exe" + break + } + } + } + if ($VcpkgExecutable -eq "") { + $found = Get-Command "vcpkg.exe" -ErrorAction SilentlyContinue + if ($found) { $VcpkgExecutable = $found.Source } + } + if ($VcpkgExecutable -eq "") { + throw ("Could not locate vcpkg.exe (checked -VcpkgExecutable, VCPKG_INSTALLATION_ROOT, " + + "VCPKG_ROOT, PATH). Alternatively pass -MklRoot pointing at an existing oneMKL install.") + } + + $vcpkgInstallRoot = Join-Path $resolvedRoot "vcpkg-installed" + $mklRoot = Join-Path $vcpkgInstallRoot "x64-windows" + $mklLibDir = Join-Path $mklRoot "lib" + if (Test-Path (Join-Path $mklLibDir "mkl_intel_ilp64_dll.lib")) { + Write-Host "Reusing oneMKL at $mklRoot" + } else { + Invoke-Checked $VcpkgExecutable @( + "install", "intel-mkl:x64-windows", "--x-install-root=$vcpkgInstallRoot") + } + $mklBin = Join-Path $mklRoot "bin" +} + +foreach ($required in @( + $mklBin, + (Join-Path $mklLibDir "mkl_intel_ilp64_dll.lib"), + (Join-Path $mklLibDir "mkl_sequential_dll.lib"), + (Join-Path $mklLibDir "mkl_core_dll.lib"))) { + if (-not (Test-Path $required)) { throw "oneMKL install is missing $required." } +} +$env:PATH = "$mklBin;$env:PATH" + +# ------------------------------------------------------------- GoogleTest ---- + +$gtestVariant = if ($SanitizeAddress) { "googletest-asan" } else { "googletest" } +$gtestInstall = Join-Path $resolvedRoot "$gtestVariant-install" +if (Test-Path (Join-Path $gtestInstall "include\gtest\gtest.h")) { + Write-Host "Reusing GoogleTest at $gtestInstall" +} else { + $gtestSrc = Join-Path $resolvedRoot "$gtestVariant-src" + Clone-Head "https://github.com/google/googletest.git" $gtestSrc "v1.17.0" + $gtestBuild = Join-Path $resolvedRoot "$gtestVariant-build" + $gtestArgs = @( + "-S", $gtestSrc, "-B", $gtestBuild, "-G", "NMake Makefiles", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=$(Convert-ToCMakePath $gtestInstall)", + "-DBUILD_GMOCK=OFF", "-DINSTALL_GTEST=ON") + if ($SanitizeAddress) { + # MSVC container-annotation state must agree across all linked objects, + # so an ASan build needs an ASan-instrumented GoogleTest. + $gtestArgs += "-DCMAKE_CXX_FLAGS=/fsanitize=address /Zi" + } + Invoke-Checked "cmake" $gtestArgs + Invoke-Checked "cmake" @("--build", $gtestBuild, "--target", "install") +} + +# -------------------------------------------------------------- Random123 ---- + +$random123Install = Join-Path $resolvedRoot "Random123-install" +if (Test-Path (Join-Path $random123Install "include\Random123\philox.h")) { + Write-Host "Reusing Random123 at $random123Install" +} else { + $random123Src = Join-Path $resolvedRoot "Random123-src" + Clone-Head "https://github.com/DEShawResearch/Random123.git" $random123Src + New-Item -ItemType Directory -Force -Path (Join-Path $random123Install "include") | Out-Null + Copy-Item -Recurse -Force (Join-Path $random123Src "include\Random123") ` + (Join-Path $random123Install "include\Random123") +} + +# ----------------------------------------------------------------- BLAS++ ---- + +$blasppInstall = Join-Path $resolvedRoot "blaspp-install" +if (Test-Path $blasppInstall) { + Write-Host "Reusing BLAS++ at $blasppInstall" +} else { + $blasppSrc = Join-Path $resolvedRoot "blaspp-src" + Clone-Head "https://github.com/BallisticLA/blaspp.git" $blasppSrc "remove-symv-debug-print" + $blasppBuild = Join-Path $resolvedRoot "blaspp-build" + # ILP64 + sequential is required: RandBLAS's MKL sparse backend static-asserts + # that its int64_t sparse indices match sizeof(MKL_INT). + $mklLibs = @( + (Convert-ToCMakePath (Join-Path $mklLibDir "mkl_intel_ilp64_dll.lib")), + (Convert-ToCMakePath (Join-Path $mklLibDir "mkl_sequential_dll.lib")), + (Convert-ToCMakePath (Join-Path $mklLibDir "mkl_core_dll.lib"))) -join ';' + Invoke-Checked "cmake" @( + "-S", $blasppSrc, "-B", $blasppBuild, "-G", "NMake Makefiles", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=$(Convert-ToCMakePath $blasppInstall)", + "-DBUILD_SHARED_LIBS=ON", + "-Duse_cmake_find_blas=false", + "-DBLAS_LIBRARIES=$mklLibs", + "-Dblas_int=ilp64", + "-Dblas_threaded=false", + "-Duse_openmp=false", + "-Dgpu_backend=none", + "-Dbuild_tests=OFF") + Invoke-Checked "cmake" @("--build", $blasppBuild, "--target", "install") +} +$blasppDir = Find-PackageConfigDirectory $blasppInstall "blaspp" + +# --------------------------------------------------------------- LAPACK++ ---- + +$lapackppInstall = Join-Path $resolvedRoot "lapackpp-install" +if (Test-Path $lapackppInstall) { + Write-Host "Reusing LAPACK++ at $lapackppInstall" +} else { + $lapackppSrc = Join-Path $resolvedRoot "lapackpp-src" + Clone-Head "https://github.com/BallisticLA/lapackpp.git" $lapackppSrc "msvc-direct-includes" + $lapackppBuild = Join-Path $resolvedRoot "lapackpp-build" + Invoke-Checked "cmake" @( + "-S", $lapackppSrc, "-B", $lapackppBuild, "-G", "NMake Makefiles", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=$(Convert-ToCMakePath $lapackppInstall)", + "-Dblaspp_DIR=$(Convert-ToCMakePath $blasppDir)", + "-DBUILD_SHARED_LIBS=ON", + "-Dgpu_backend=none", + "-Dbuild_tests=OFF") + Invoke-Checked "cmake" @("--build", $lapackppBuild, "--target", "install") +} +$lapackppDir = Find-PackageConfigDirectory $lapackppInstall "lapackpp" + +# ----------------------------------------------------------------- export ---- + +Export-GitHubValue "MKLROOT" (Convert-ToCMakePath $mklRoot) +Export-GitHubValue "MKL_BIN" $mklBin +Export-GitHubValue "googletest_PREFIX" (Convert-ToCMakePath $gtestInstall) +Export-GitHubValue "Random123_DIR" (Convert-ToCMakePath (Join-Path $random123Install "include")) +Export-GitHubValue "blaspp_DIR" (Convert-ToCMakePath $blasppDir) +Export-GitHubValue "lapackpp_DIR" (Convert-ToCMakePath $lapackppDir) +if ($env:GITHUB_PATH) { Add-Content -Path $env:GITHUB_PATH -Value $mklBin } + +Write-Host "All native Windows dependencies are ready under $resolvedRoot" diff --git a/.github/scripts/windows/run-ci.ps1 b/.github/scripts/windows/run-ci.ps1 new file mode 100644 index 00000000..e192cd9b --- /dev/null +++ b/.github/scripts/windows/run-ci.ps1 @@ -0,0 +1,112 @@ +# Configures, builds, installs, and tests RandLAPACK natively on Windows with +# MSVC + oneMKL (ILP64, sequential). Shared between GitHub CI and local runs. +# +# Local use, from an MSVC developer prompt in the repository root: +# .github\scripts\windows\run-ci.ps1 -Task Core -SetupDependencies +# +# CI runs the setup-randlapack-deps-windows action first (for caching) and then +# invokes this script without -SetupDependencies. +# +# OpenMP on MSVC uses the /openmp:llvm runtime, selected by RandBLAS's build +# system (RandBLAS #184): it is the only MSVC mode that accepts RandLAPACK's +# 64-bit loop indices and collapse clauses. Pass -OpenMP to enable it; +# without the switch the build is serial (also fully functional -- +# RandLAPACK guards all OpenMP use). + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidateSet("Core")] + [string]$Task, + + [string]$SourceRoot = "", + + [string]$WorkRoot = "", + + [string]$DependencyRoot = "", + + [switch]$SetupDependencies, + + [switch]$OpenMP, + + [switch]$SanitizeAddress +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +function Invoke-Checked { + param([string]$Program, [string[]]$Arguments) + & $Program @Arguments + if ($LASTEXITCODE -ne 0) { + throw "'$Program $($Arguments -join ' ')' failed with exit code $LASTEXITCODE." + } +} + +function Require-EnvironmentVariable { + param([string]$Name) + $value = [Environment]::GetEnvironmentVariable($Name) + if (-not $value) { + throw "Environment variable $Name is not set. Run setup.ps1 (or pass -SetupDependencies)." + } + return $value +} + +if ($SourceRoot -eq "") { + if ($env:GITHUB_WORKSPACE) { $SourceRoot = $env:GITHUB_WORKSPACE } + else { $SourceRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\..")) } +} +if ($WorkRoot -eq "") { + $WorkRoot = Join-Path (Split-Path $SourceRoot -Parent) "RandLAPACK-windows-ci" +} +if ($DependencyRoot -eq "") { + $DependencyRoot = Join-Path (Split-Path $SourceRoot -Parent) "windows-deps" +} + +if ($SetupDependencies) { + & (Join-Path $SourceRoot ".github\actions\setup-randlapack-deps-windows\setup.ps1") ` + -DependencyRoot $DependencyRoot -SanitizeAddress:$SanitizeAddress +} + +$blasppDir = Require-EnvironmentVariable "blaspp_DIR" +$lapackppDir = Require-EnvironmentVariable "lapackpp_DIR" +$random123Dir = Require-EnvironmentVariable "Random123_DIR" +$gtestPrefix = Require-EnvironmentVariable "googletest_PREFIX" +$mklRoot = Require-EnvironmentVariable "MKLROOT" +$mklBin = [Environment]::GetEnvironmentVariable("MKL_BIN") +if (-not $mklBin) { $mklBin = "$($mklRoot.Replace('/', '\'))\bin" } + +# oneMKL enters through raw library paths recorded by BLAS++, so its DLLs are +# not covered by the TARGET_RUNTIME_DLLS staging and must be on PATH for the +# build-time gtest discovery and for ctest. +$env:PATH = "$mklBin;$env:PATH" + +$buildDir = Join-Path $WorkRoot "RandLAPACK-build" +$installDir = Join-Path $WorkRoot "RandLAPACK-install" + +$configureArgs = @( + "-S", $SourceRoot, "-B", $buildDir, + "-G", "Ninja", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=$($installDir.Replace('\', '/'))", + "-Dblaspp_DIR=$blasppDir", + "-Dlapackpp_DIR=$lapackppDir", + "-DRandom123_DIR=$random123Dir", + "-DCMAKE_PREFIX_PATH=$gtestPrefix", + # The RandBLAS submodule's own tests are covered by RandBLAS's CI; + # building and running them here roughly doubled the job time. + "-DBUILD_TESTS=OFF" +) +if (-not $OpenMP) { $configureArgs += "-DCMAKE_DISABLE_FIND_PACKAGE_OpenMP=TRUE" } +if ($SanitizeAddress) { $configureArgs += "-DSANITIZE_ADDRESS=ON" } + +Invoke-Checked "cmake" $configureArgs +Invoke-Checked "cmake" @("--build", $buildDir, "--target", "install") + +# Same exclusion as the Linux and macOS core jobs. +Invoke-Checked "ctest" @( + "--test-dir", $buildDir, + "--exclude-regex", "^TestABRIK\.ABRIK_catch_instability", + "--output-on-failure") + +Write-Host "RandLAPACK Windows $Task validation succeeded." diff --git a/.github/workflows/core-linux.yaml b/.github/workflows/core-linux.yaml index c6c38a83..c060fdff 100644 --- a/.github/workflows/core-linux.yaml +++ b/.github/workflows/core-linux.yaml @@ -8,6 +8,14 @@ on: branches-ignore: - cqrrp-gpu-benchmarking +# BLAS++/LAPACK++/Random123 track their upstream default branches but change +# rarely; their installs are cached below. Bump the -v suffix in the cache key +# to force a rebuild against fresh upstream clones. +# +# The RandBLAS submodule's own test suite is disabled (-DBUILD_TESTS=OFF): +# the pinned commit is already tested by RandBLAS's CI, and rebuilding and +# rerunning its ~450 tests here roughly doubled the job time. + jobs: build: runs-on: ubuntu-latest @@ -33,7 +41,18 @@ jobs: echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list sudo apt update + - name: cache dependencies + id: deps-cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/../blaspp-install + ${{ github.workspace }}/../lapackpp-install + ${{ github.workspace }}/../Random123-install + key: core-deps-${{ runner.os }}-v1 + - name: install BLAS++ + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/icl-utk-edu/blaspp.git @@ -41,9 +60,10 @@ jobs: cd blaspp-build pwd cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=`pwd`/../blaspp-install -Dbuild_tests=OFF ../blaspp - make -j2 install + make -j$(nproc) install - name: install Random123 + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/DEShawResearch/Random123.git @@ -51,6 +71,7 @@ jobs: make prefix=`pwd`/../Random123-install install-include - name: install LAPACK++ + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/icl-utk-edu/lapackpp.git @@ -61,7 +82,7 @@ jobs: -DCMAKE_INSTALL_PREFIX=`pwd`/../lapackpp-install \ -Dbuild_tests=OFF \ `pwd`/../lapackpp - make -j2 install + make -j$(nproc) install - name: build and test RandLAPACK (Release) run: | @@ -73,9 +94,10 @@ jobs: -Dlapackpp_DIR=`pwd`/../lapackpp-install/lib/cmake/lapackpp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ -DCMAKE_INSTALL_PREFIX=`pwd`/../RandLAPACK-install \ + -DBUILD_TESTS=OFF \ `pwd`/../RandLAPACK - make -j2 - make -j2 install + make -j$(nproc) + make -j$(nproc) install ctest --exclude-regex "^TestABRIK\.ABRIK_catch_instability" --output-on-failure - name: build and test extras @@ -89,7 +111,7 @@ jobs: -Dblaspp_DIR=`pwd`/../blaspp-install/lib/cmake/blaspp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ ../RandLAPACK/extras - make -j2 + make -j$(nproc) ctest --output-on-failure - name: build benchmarks @@ -100,7 +122,66 @@ jobs: cmake \ -DRandLAPACK_DIR=`pwd`/../RandLAPACK-install/lib/cmake/RandLAPACK \ ../RandLAPACK/benchmark - make -j2 + make -j$(nproc) + + # Runs in parallel with the Release job instead of doubling its wall time. + build-asan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: submodule update + run: | + git submodule update --init --recursive + + - name: configure OS + run: | + set -x + export DEBIAN_FRONTEND="noninteractive" + sudo apt-get update -qq + sudo apt-get install -qq -y git-core gcc g++ gfortran cmake libgtest-dev libopenblas-openmp-dev + + - name: cache dependencies + id: deps-cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/../blaspp-install + ${{ github.workspace }}/../lapackpp-install + ${{ github.workspace }}/../Random123-install + key: core-deps-${{ runner.os }}-v1 + + - name: install BLAS++ + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/icl-utk-edu/blaspp.git + mkdir blaspp-build + cd blaspp-build + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=`pwd`/../blaspp-install -Dbuild_tests=OFF ../blaspp + make -j$(nproc) install + + - name: install Random123 + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/DEShawResearch/Random123.git + cd Random123/ + make prefix=`pwd`/../Random123-install install-include + + - name: install LAPACK++ + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/icl-utk-edu/lapackpp.git + mkdir lapackpp-build + cd lapackpp-build + cmake -DCMAKE_BUILD_TYPE=Release \ + -Dblaspp_DIR=`pwd`/../blaspp-install/lib/cmake/blaspp \ + -DCMAKE_INSTALL_PREFIX=`pwd`/../lapackpp-install \ + -Dbuild_tests=OFF \ + `pwd`/../lapackpp + make -j$(nproc) install - name: build and test RandLAPACK (Debug/asan) run: | @@ -113,8 +194,8 @@ jobs: -Dlapackpp_DIR=`pwd`/../lapackpp-install/lib/cmake/lapackpp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ -DCMAKE_INSTALL_PREFIX=`pwd`/../RandLAPACK-install-asan \ + -DBUILD_TESTS=OFF \ `pwd`/../RandLAPACK - make -j2 - make -j2 install + make -j$(nproc) + make -j$(nproc) install ctest --exclude-regex "^TestABRIK\.ABRIK_catch_instability" --output-on-failure - diff --git a/.github/workflows/core-macos.yaml b/.github/workflows/core-macos.yaml index 975ac8ca..4092be8c 100644 --- a/.github/workflows/core-macos.yaml +++ b/.github/workflows/core-macos.yaml @@ -8,6 +8,9 @@ on: branches-ignore: - cqrrp-gpu-benchmarking +# Dependency installs are cached and the RandBLAS submodule's own test suite +# is disabled; see core-linux.yaml for the reasoning. + jobs: build: runs-on: macos-latest @@ -26,16 +29,28 @@ jobs: set -x brew install googletest + - name: cache dependencies + id: deps-cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/../blaspp-install + ${{ github.workspace }}/../lapackpp-install + ${{ github.workspace }}/../Random123-install + key: core-deps-${{ runner.os }}-v1 + - name: install BLAS++ + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/icl-utk-edu/blaspp.git mkdir blaspp-build cd blaspp-build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=`pwd`/../blaspp-install -Dbuild_tests=OFF ../blaspp - make -j2 install + make -j$(sysctl -n hw.ncpu) install - name: install Random123 + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/DEShawResearch/Random123.git @@ -44,6 +59,7 @@ jobs: cp -rp include/Random123 `pwd`/../Random123-install/include/ - name: install LAPACK++ + if: steps.deps-cache.outputs.cache-hit != 'true' run: | cd .. git clone https://github.com/icl-utk-edu/lapackpp.git @@ -54,7 +70,7 @@ jobs: -DCMAKE_INSTALL_PREFIX=`pwd`/../lapackpp-install \ -Dbuild_tests=OFF \ `pwd`/../lapackpp - make -j2 install + make -j$(sysctl -n hw.ncpu) install - name: build and test RandLAPACK (Release) run: | @@ -66,9 +82,10 @@ jobs: -Dlapackpp_DIR=`pwd`/../lapackpp-install/lib/cmake/lapackpp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ -DCMAKE_INSTALL_PREFIX=`pwd`/../RandLAPACK-install \ + -DBUILD_TESTS=OFF \ `pwd`/../RandLAPACK - make -j2 - make -j2 install + make -j$(sysctl -n hw.ncpu) + make -j$(sysctl -n hw.ncpu) install ctest --exclude-regex "^TestABRIK\.ABRIK_catch_instability" --output-on-failure - name: build and test extras @@ -82,7 +99,7 @@ jobs: -Dblaspp_DIR=`pwd`/../blaspp-install/lib/cmake/blaspp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ ../RandLAPACK/extras - make -j2 + make -j$(sysctl -n hw.ncpu) ctest --output-on-failure - name: build benchmarks @@ -93,9 +110,67 @@ jobs: cmake \ -DRandLAPACK_DIR=`pwd`/../RandLAPACK-install/lib/cmake/RandLAPACK \ ../RandLAPACK/benchmark - make -j2 + make -j$(sysctl -n hw.ncpu) + + # Runs in parallel with the Release job instead of doubling its wall time. + build-asan: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 - - name: test build and test RandLAPACK (Debug/asan) + - name: submodule update + run: | + git submodule update --init --recursive + + - name: configure OS + run: | + set -x + brew install googletest + + - name: cache dependencies + id: deps-cache + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/../blaspp-install + ${{ github.workspace }}/../lapackpp-install + ${{ github.workspace }}/../Random123-install + key: core-deps-${{ runner.os }}-v1 + + - name: install BLAS++ + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/icl-utk-edu/blaspp.git + mkdir blaspp-build + cd blaspp-build + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=`pwd`/../blaspp-install -Dbuild_tests=OFF ../blaspp + make -j$(sysctl -n hw.ncpu) install + + - name: install Random123 + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/DEShawResearch/Random123.git + cd Random123/ + mkdir -p `pwd`/../Random123-install/include + cp -rp include/Random123 `pwd`/../Random123-install/include/ + + - name: install LAPACK++ + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/icl-utk-edu/lapackpp.git + mkdir lapackpp-build + cd lapackpp-build + cmake -DCMAKE_BUILD_TYPE=Release \ + -Dblaspp_DIR=`pwd`/../blaspp-install/lib/cmake/blaspp \ + -DCMAKE_INSTALL_PREFIX=`pwd`/../lapackpp-install \ + -Dbuild_tests=OFF \ + `pwd`/../lapackpp + make -j$(sysctl -n hw.ncpu) install + + - name: build and test RandLAPACK (Debug/asan) run: | cd .. mkdir RandLAPACK-build-asan @@ -106,8 +181,8 @@ jobs: -Dlapackpp_DIR=`pwd`/../lapackpp-install/lib/cmake/lapackpp \ -DRandom123_DIR=`pwd`/../Random123-install/include/ \ -DCMAKE_INSTALL_PREFIX=`pwd`/../RandLAPACK-install-asan \ + -DBUILD_TESTS=OFF \ `pwd`/../RandLAPACK - make -j2 - make -j2 install + make -j$(sysctl -n hw.ncpu) + make -j$(sysctl -n hw.ncpu) install ctest --exclude-regex "^TestABRIK\.ABRIK_catch_instability" --output-on-failure - diff --git a/.github/workflows/core-windows.yaml b/.github/workflows/core-windows.yaml new file mode 100644 index 00000000..60d270db --- /dev/null +++ b/.github/workflows/core-windows.yaml @@ -0,0 +1,43 @@ +name: core-windows +on: + pull_request: + branches-ignore: + - cqrrp-gpu-benchmarking + workflow_dispatch: + push: + branches-ignore: + - cqrrp-gpu-benchmarking + +jobs: + build-windows: + name: windows-msvc-mkl-ilp64-${{ matrix.label }} + runs-on: windows-2022 + strategy: + fail-fast: false + matrix: + include: + - label: serial + openmp: false + - label: openmp + openmp: true + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: initialize MSVC + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: setup native Windows dependencies + uses: ./.github/actions/setup-randlapack-deps-windows + + - name: build, install, and test RandLAPACK + shell: pwsh + run: | + $openmp = '${{ matrix.openmp }}' -eq 'true' + & "$env:GITHUB_WORKSPACE/.github/scripts/windows/run-ci.ps1" ` + -Task Core ` + -WorkRoot "$env:GITHUB_WORKSPACE/../RandLAPACK-windows-ci" ` + -OpenMP:$openmp diff --git a/.github/workflows/install-script.yaml b/.github/workflows/install-script.yaml index 36d6cf51..50276d1d 100644 --- a/.github/workflows/install-script.yaml +++ b/.github/workflows/install-script.yaml @@ -7,7 +7,9 @@ # part of the script's contract); # 3. the dependency-discovery path (BLASPP_INSTALL_DIR & co.) configures # a second project against already-installed dependencies. -# A Windows job slot is reserved for the native-Windows work (install.ps1). +# The Windows job runs install.ps1 the same way (fresh + idempotent re-run). +# The Linux/macOS lanes invoke the root install.sh wrapper on purpose: users +# type `bash install.sh`, so CI exercises the wrapper along with the script. name: install-script on: pull_request: @@ -107,5 +109,36 @@ jobs: - name: re-run the installer in place (idempotency) run: bash RandNLA-project/lib/RandLAPACK/install.sh --yes --no-gpu - # install-windows: reserved. Lands with the native Windows support - # (install.ps1 + MSVC/oneMKL recipe mirroring RandBLAS PR #179). + install-windows: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v4 + with: + path: RandLAPACK + submodules: recursive + + - name: initialize MSVC + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + # Shares the cached dependency stack with core-windows (same keys, + # same ..\windows-deps location); install.ps1 then reuses it through + # -DependencyRoot instead of rebuilding under its project directory. + - name: setup native Windows dependencies + uses: ./RandLAPACK/.github/actions/setup-randlapack-deps-windows + + - name: run the installer + shell: pwsh + run: | + & "$env:GITHUB_WORKSPACE/RandLAPACK/install/install.ps1" ` + -ProjectDir "$env:GITHUB_WORKSPACE/RandNLA-project" ` + -DependencyRoot "$env:GITHUB_WORKSPACE/../windows-deps" + + - name: re-run the installer in place (idempotency) + shell: pwsh + run: | + & "$env:GITHUB_WORKSPACE/RandLAPACK/install/install.ps1" ` + -ProjectDir "$env:GITHUB_WORKSPACE/RandNLA-project" ` + -DependencyRoot "$env:GITHUB_WORKSPACE/../windows-deps" ` + -SkipTests diff --git a/CMake/rl_config.cmake b/CMake/rl_config.cmake index 27e2ddcf..7e43e0d6 100644 --- a/CMake/rl_config.cmake +++ b/CMake/rl_config.cmake @@ -1,4 +1,9 @@ +# Values substituted into an installed CMake package file must use CMake path +# syntax. Native Windows backslashes would otherwise be parsed as escape +# sequences when a downstream project loads RandLAPACKConfig.cmake. +file(TO_CMAKE_PATH "${RandLAPACK_lapackpp_DIR}" RandLAPACK_lapackpp_DIR) + configure_file(CMake/RandLAPACKConfig.cmake.in ${CMAKE_INSTALL_LIBDIR}/cmake/RandLAPACK/RandLAPACKConfig.cmake @ONLY) diff --git a/CMake/rl_runtime_dlls.cmake b/CMake/rl_runtime_dlls.cmake new file mode 100644 index 00000000..c5d12311 --- /dev/null +++ b/CMake/rl_runtime_dlls.cmake @@ -0,0 +1,19 @@ +# Native Windows builds use TARGET_RUNTIME_DLLS to stage imported shared-library +# dependencies (BLAS++/LAPACK++ DLLs) beside executables, so that test discovery +# and test runs find them without PATH edits. The generator expression requires +# CMake 3.21, which is already this project's minimum. Note this only covers +# CMake imported targets: oneMKL enters through raw library paths recorded by +# BLAS++, so the MKL bin directory must be on PATH at build and test time. +function(randlapack_stage_runtime_dlls target) + if (WIN32) + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + COMMAND_EXPAND_LISTS + VERBATIM + ) + endif() +endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt index a05204a8..d6dccafe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ enable_testing() include(rl_build_options) include(rl_version) +include(rl_runtime_dlls) # Find dependencies find_package(lapackpp REQUIRED) @@ -72,7 +73,7 @@ option(RandLAPACK_EXTERNAL_RandBLAS # The submodule pin, recorded as a variable so configure-time checks also work # in tarball builds (no .git directory). Update alongside every submodule # bump; with a git checkout present, the cross-check below enforces that. -set(RandLAPACK_RandBLAS_PIN "8417f4bb911c6ca3aa83b157a594758d2d4fab8c") +set(RandLAPACK_RandBLAS_PIN "04f2018afdb29a9478ae70cb1a52b36a9156146f") find_package(Git QUIET) if (Git_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") diff --git a/INSTALL.md b/INSTALL.md index a8932156..9ec1ee66 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -298,3 +298,39 @@ If you're having trouble installing RandLAPACK, you can always refer to [that wo The workflow includes statements which print the working directory and list the contents of that directory at various points in the installation. We do that so that it's easier to infer a valid choice of directory structure for building RandLAPACK. + +## 6. Native Windows (MSVC) + +RandLAPACK builds natively on Windows with MSVC (Visual Studio 2022) and +Intel oneMKL using ILP64, sequential linking. The easy path, from a +"Developer PowerShell for VS 2022" prompt in the repository root: + +```powershell +.\install\install.ps1 +``` + +This builds all dependencies (oneMKL through vcpkg, or pass +`-MklRoot ` to use an existing oneAPI install), then builds, installs, +and tests RandLAPACK, mirroring `install.sh`'s directory layout. The header +comment in `install.ps1` documents the options. + +Points worth knowing: + +- **ILP64 is required.** RandBLAS's MKL sparse backend checks at compile time + that its 64-bit indices match `MKL_INT`, so BLAS++ must be configured with + `-Dblas_int=ilp64` against `mkl_intel_ilp64_dll.lib` (the installer does + this for you). +- **BLAS++/LAPACK++ come from BallisticLA branches** (`BallisticLA/blaspp@remove-symv-debug-print`, + `BallisticLA/lapackpp@msvc-direct-includes`) until two one-line MSVC fixes + merge upstream (blaspp PR #132, lapackpp PR #87). +- **OpenMP on MSVC uses the `/openmp:llvm` runtime**, selected automatically + by RandBLAS's build system — the only MSVC mode that accepts RandLAPACK's + 64-bit loop indices and `collapse` clauses. Serial builds (OpenMP off) are + also fully functional. +- **GPU support is not available on native Windows.** +- Executables that link MKL need the MKL `bin` directory on `PATH` at run + time; the installer prints the path to keep. + +CI runs this configuration on every pull request (`core-windows` workflow); +`.github/scripts/windows/run-ci.ps1 -Task Core -SetupDependencies` reproduces +that job locally. diff --git a/INSTALL_SCRIPT.md b/INSTALL_SCRIPT.md index 7a77afd2..866a602b 100644 --- a/INSTALL_SCRIPT.md +++ b/INSTALL_SCRIPT.md @@ -1,5 +1,9 @@ # Using RandLAPACK's Automated Install Script +The installer scripts live in the `install/` directory (`install/install.sh` +for Linux/macOS, `install/install.ps1` for native Windows); a small wrapper is +kept at the repository root so `bash install.sh` keeps working. + This guide explains how to use the `install.sh` script to automatically install RandLAPACK and all of its dependencies (BLAS++, LAPACK++, Random123) with a single command. diff --git a/RandBLAS b/RandBLAS index 8417f4bb..04f2018a 160000 --- a/RandBLAS +++ b/RandBLAS @@ -1 +1 @@ -Subproject commit 8417f4bb911c6ca3aa83b157a594758d2d4fab8c +Subproject commit 04f2018afdb29a9478ae70cb1a52b36a9156146f diff --git a/RandLAPACK/CMakeLists.txt b/RandLAPACK/CMakeLists.txt index 62f33ec9..2554df5c 100644 --- a/RandLAPACK/CMakeLists.txt +++ b/RandLAPACK/CMakeLists.txt @@ -46,14 +46,38 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rl_config.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/RandLAPACK) +# RandLAPACK is header-only, so MSVC requirements must propagate to every +# translation unit that includes its headers: /utf-8 because several headers +# contain non-ASCII characters in string literals (Greek letters in timing +# and diagnostic messages), which MSVC otherwise interprets in the local +# codepage. GCC and Clang read source as UTF-8 already. +target_compile_options(RandLAPACK INTERFACE + $<$:/utf-8>) + # Build-time only: warning and sanitizer flags should not leak into the installed target. target_compile_options(RandLAPACK INTERFACE - $ - $) + $:-Wall>> + $:-Wextra>> + $:/W4>>) if (SANITIZE_ADDRESS) - target_compile_options(RandLAPACK INTERFACE $) - target_link_options(RandLAPACK INTERFACE $) + if (MSVC) + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("/fsanitize=address" RANDLAPACK_MSVC_ASAN_SUPPORTED) + if (NOT RANDLAPACK_MSVC_ASAN_SUPPORTED) + message(FATAL_ERROR "SANITIZE_ADDRESS=ON requires an MSVC toolchain with " + "/fsanitize=address support and its AddressSanitizer runtime installed.") + endif() + # /fsanitize=address is a compile option, not a raw link option, on MSVC. + target_compile_options(RandLAPACK INTERFACE + $ $) + target_link_options(RandLAPACK INTERFACE $) + elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(RandLAPACK INTERFACE $) + target_link_options(RandLAPACK INTERFACE $) + else() + message(FATAL_ERROR "SANITIZE_ADDRESS=ON is not supported with ${CMAKE_CXX_COMPILER_ID}.") + endif() endif() target_include_directories( diff --git a/RandLAPACK/drivers/rl_hqrrp.hh b/RandLAPACK/drivers/rl_hqrrp.hh index 0dd1809c..847527a1 100644 --- a/RandLAPACK/drivers/rl_hqrrp.hh +++ b/RandLAPACK/drivers/rl_hqrrp.hh @@ -98,17 +98,11 @@ void _LAPACK_lafrb( LAPACK_dlarfb( & side_, & trans_, & direction_, & storev_, & m_, & n_, & k_, (double *) buff_U, & ldim_U, (double *) buff_T, & ldim_T, (double *) buff_B, & ldim_B, (double *) buff_W, & ldim_W - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1, 1, 1, 1 - #endif ); } else if (typeid(T) == typeid(float)) { LAPACK_slarfb( & side_, & trans_, & direction_, & storev_, & m_, & n_, & k_, (float *) buff_U, & ldim_U, (float *) buff_T, & ldim_T, (float *) buff_B, & ldim_B, (float *) buff_W, & ldim_W - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1, 1, 1, 1 - #endif ); } else { // Unsupported type @@ -136,9 +130,6 @@ void _LAPACK_larf( (double *) tau, (double *) C, & ldc_, (double *) work - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1 - #endif ); } else if (typeid(T) == typeid(float)) { LAPACK_slarf( & side_, & m_, & n_, @@ -146,9 +137,6 @@ void _LAPACK_larf( (float *) tau, (float *) C, & ldc_, (float *) work - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1 - #endif ); } else { // Unsupported type @@ -387,9 +375,6 @@ static int64_t NoFLA_QRP_downdate_partial_norms( // Some initializations. char dlmach_param = 'E'; tol3z = sqrt( LAPACK_dlamch( & dlmach_param - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1 - #endif ) ); ptr_d = buff_d; ptr_e = buff_e; diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 6aa7749d..454a3d5c 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -52,7 +52,7 @@ function(add_benchmark) set(MVO CXX_SOURCES LINK_LIBS) cmake_parse_arguments(PARSE_ARGV 0 TGT "${OPTS}" "${NVPO}" "${MVO}") add_executable(${TGT_NAME} ${TGT_CXX_SOURCES}) - target_compile_options(${TGT_NAME} PRIVATE "-g") + target_compile_options(${TGT_NAME} PRIVATE $<$:-g>) # Include directories come from target_link_libraries, no need to set manually target_link_libraries(${TGT_NAME} ${TGT_LINK_LIBS}) message(STATUS "RandLAPACK: added ${TGT_NAME} benchmark") @@ -65,7 +65,7 @@ function(add_benchmark_gpu) set(MVO CU_SOURCES LINK_LIBS) cmake_parse_arguments(PARSE_ARGV 0 TGT "${OPTS}" "${NVPO}" "${MVO}") add_executable(${TGT_NAME} ${TGT_CU_SOURCES}) - target_compile_options(${TGT_NAME} PRIVATE "-g") + target_compile_options(${TGT_NAME} PRIVATE $<$:-g>) target_link_libraries(${TGT_NAME} ${TGT_LINK_LIBS}) message(STATUS "RandLAPACK: added ${TGT_NAME} GPU benchmark") endfunction() diff --git a/benchmark/bench_BQRRP/BQRRP_pivot_quality.cc b/benchmark/bench_BQRRP/BQRRP_pivot_quality.cc index e366ab77..c404e566 100644 --- a/benchmark/bench_BQRRP/BQRRP_pivot_quality.cc +++ b/benchmark/bench_BQRRP/BQRRP_pivot_quality.cc @@ -82,9 +82,6 @@ void _LAPACK_gejsv( work, lwork_, iwork_, info_ - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1, 1, 1, 1, 1, 1 - #endif ); return; @@ -242,7 +239,7 @@ static void sv_ratio( double* buff_workspace = new double[8 * m * n](); int64_t lwork[1]; lwork[0] = 8 * m * n; - int64_t iwork[8 * std::min(m,n)]; + int64_t* iwork = new int64_t[8 * std::min(m, n)](); int64_t info[1]; _LAPACK_gejsv( @@ -292,6 +289,7 @@ static void sv_ratio( data_regen(m_info, all_data, state_gen); delete[] buff_workspace; + delete[] iwork; } int main(int argc, char *argv[]) { diff --git a/benchmark/bench_BQRRP/BQRRP_subroutines_speed.cc b/benchmark/bench_BQRRP/BQRRP_subroutines_speed.cc index 16b1ce5f..13945874 100644 --- a/benchmark/bench_BQRRP/BQRRP_subroutines_speed.cc +++ b/benchmark/bench_BQRRP/BQRRP_subroutines_speed.cc @@ -89,9 +89,6 @@ void _LAPACK_ilaenv( LAPACK_ilaenv( & ISPEC_, & NAME, & OPTS, N1_, N2_, N3_, N4_ - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1 - #endif ); return; } diff --git a/benchmark/bench_BQRRP/find_test_mat_spectrum.cc b/benchmark/bench_BQRRP/find_test_mat_spectrum.cc index 86ae1674..8d0c9041 100644 --- a/benchmark/bench_BQRRP/find_test_mat_spectrum.cc +++ b/benchmark/bench_BQRRP/find_test_mat_spectrum.cc @@ -53,9 +53,6 @@ void _LAPACK_gejsv( work, lwork_, iwork_, info_ - #ifdef LAPACK_FORTRAN_STRLEN_END - //, 1, 1, 1, 1, 1, 1 - #endif ); return; @@ -86,7 +83,7 @@ void get_spectrum( double* buff_workspace = new double[8 * m * n](); int64_t lwork[1]; lwork[0] = 8 * m * n; - int64_t iwork[8 * std::min(m,n)]; + int64_t* iwork = new int64_t[8 * std::min(m, n)](); int64_t info[1]; _LAPACK_gejsv( @@ -116,6 +113,8 @@ void get_spectrum( } file << "\n"; + delete[] buff_workspace; + delete[] iwork; free(A); free(U); free(VT); diff --git a/docs/CI.md b/docs/CI.md new file mode 100644 index 00000000..f69adca5 --- /dev/null +++ b/docs/CI.md @@ -0,0 +1,75 @@ +# RandLAPACK's GitHub CI + +Four workflows run on every push and pull request. They divide into two +lanes per operating system: the **core** workflows hand-replicate the build +recipe step by step (so a failure points at a specific step), and the +**install-script** workflow runs the user-facing installers end to end (so +the thing we tell users to type is itself under test). + +| Workflow | Job | Runner | Validates | Required check? | +|----------|-----|--------|-----------|-----------------| +| core-linux | `build` | ubuntu-latest | Release build + tests, extras build + tests, benchmark compile (OpenBLAS) | **yes** (`build`) | +| core-linux | `build-asan` | ubuntu-latest | Debug + AddressSanitizer build and test run | no | +| core-macos | `build` | macos-latest | same as Linux `build`, on Apple's toolchain | **yes** (`build`) | +| core-macos | `build-asan` | macos-latest | Debug + AddressSanitizer build and test run | no | +| core-windows | `build-windows` | windows-2022 | MSVC + oneMKL (ILP64, sequential) build + tests, serial (no OpenMP) | no (new) | +| install-script | `install-linux` | ubuntu-latest | `install.sh`: fresh install, idempotent re-run, dependency-discovery path | **yes** | +| install-script | `install-macos` | macos-latest | `install.sh`: fresh install, idempotent re-run | **yes** | +| install-script | `install-windows` | windows-2022 | `install/install.ps1`: fresh install, idempotent re-run | no (new) | + +Required-check names match **job** names (`build`, `install-linux`, +`install-macos`), not "workflow / job" display names. The two Windows jobs +and the two `build-asan` jobs are deliberately not required yet; they become +candidates once they have a green track record. + +## Things that are deliberate (do not "fix" without reading this) + +- **core-macos is red on purpose.** `TestQB.Polynomial_Decay_general1` fails + on Apple Silicon because Apple's default (old) Accelerate LAPACK has a + broken divide-and-conquer `gesdd`. The failure is kept as a canary: when + the planned migration to the new Accelerate interface happens, this test + flipping green is the evidence the bug is gone. A local fix exists + (reference SVD via `gesvd`) but is intentionally unmerged. +- **The RandBLAS submodule's own tests do not run here** + (`-DBUILD_TESTS=OFF` in the core recipes). The pinned commit is already + tested by RandBLAS's CI; rebuilding its ~450 tests in every RandLAPACK job + roughly doubled job times. The install-script lanes keep them, preserving + exactly what a user's install builds. +- **Windows builds are serial (OpenMP off) for now.** RandLAPACK's OpenMP + loops need MSVC's `/openmp:llvm` runtime (64-bit indices, `collapse`), + which RandBLAS's build system does not select yet; see BallisticLA/RandBLAS#184. + When that lands, core-windows grows an OpenMP leg. +- **BLAS++ and LAPACK++ on Windows come from BallisticLA fork branches** + (`BallisticLA/blaspp@remove-symv-debug-print`, + `BallisticLA/lapackpp@msvc-direct-includes`) carrying two one-line MSVC + fixes. Upstream PRs are open (blaspp #132, lapackpp #87); once merged, the + clones in `.github/actions/setup-randlapack-deps-windows/setup.ps1` move + back to upstream master. + +## Caches + +Dependencies (BLAS++, LAPACK++, Random123 — plus oneMKL and GoogleTest on +Windows) are built once and cached; a cache-miss run is several minutes +slower than a warm one. + +| Cache key prefix | Used by | Lives beside | +|------------------|---------|--------------| +| `core-deps--v` | core-linux, core-macos (both jobs) | the workspace (`../*-install`) | +| `installer-deps--v` | install-linux | `deps-install/` in the workspace | +| `windows-*-v` (five keys) | core-windows, install-windows | `..\windows-deps` | + +To force a rebuild against fresh upstream clones, bump the `-v` suffix in +the relevant key. The Windows keys are additionally salted with a hash of +`setup.ps1`, so editing that script invalidates them automatically. +BLAS++/LAPACK++ track upstream default branches, so a stale cache also +means frozen upstream — bump the suffix when upstream matters. + +## Reproducing CI locally + +- Linux/macOS core recipe: follow the steps in the workflow file; they are + ordinary `cmake` + `make` invocations. +- Installer lanes: `bash install.sh --yes --no-gpu` from a fresh clone. +- Windows (from an MSVC developer prompt in the repository root): + `.github\scripts\windows\run-ci.ps1 -Task Core -SetupDependencies` + reproduces core-windows; `.\install\install.ps1` reproduces + install-windows. diff --git a/extras/test/linops/test_ext_solver_linop_unified.cc b/extras/test/linops/test_ext_solver_linop_unified.cc index 838de4e5..49a5e1e6 100644 --- a/extras/test/linops/test_ext_solver_linop_unified.cc +++ b/extras/test/linops/test_ext_solver_linop_unified.cc @@ -285,7 +285,7 @@ class TestExtSolverLinOpUnified : public ::testing::Test { T rtol = 100 * std::numeric_limits::epsilon(); RandBLAS::testing::matrices_approx_equal( Layout::ColMajor, Op::NoTrans, m, n, C_solver.data(), m, - C_reference.data(), m, __PRETTY_FUNCTION__, __FILE__, __LINE__, + C_reference.data(), m, __RANDBLAS_PRETTY_FUNCTION__, __FILE__, __LINE__, atol, rtol ); @@ -346,7 +346,7 @@ class TestExtSolverLinOpUnified : public ::testing::Test { T rtol = 100 * std::numeric_limits::epsilon(); RandBLAS::testing::matrices_approx_equal( Layout::ColMajor, Op::NoTrans, m, n, C_halfsv.data(), m, - C_reference.data(), m, __PRETTY_FUNCTION__, __FILE__, __LINE__, + C_reference.data(), m, __RANDBLAS_PRETTY_FUNCTION__, __FILE__, __LINE__, atol, rtol ); @@ -402,7 +402,7 @@ class TestExtSolverLinOpUnified : public ::testing::Test { T rtol = 100 * std::numeric_limits::epsilon(); RandBLAS::testing::matrices_approx_equal( Layout::ColMajor, Op::NoTrans, m, n, C_halfsv.data(), m, - C_reference.data(), m, __PRETTY_FUNCTION__, __FILE__, __LINE__, + C_reference.data(), m, __RANDBLAS_PRETTY_FUNCTION__, __FILE__, __LINE__, atol, rtol ); diff --git a/install.sh b/install.sh index 0fe96323..263c2b58 100755 --- a/install.sh +++ b/install.sh @@ -1,484 +1,4 @@ -#!/bin/bash -# RandLAPACK autoinstaller. -# -# Installs RandLAPACK with all of its dependencies and builds the extras and -# benchmark projects. The directory that contains the RandLAPACK clone ends up -# with a top-level "RandNLA-project" directory: -# lib: RandLAPACK, blaspp, lapackpp sources -# install: RandLAPACK-install, blaspp-install, lapackpp-install, random123 -# build: one build directory per project above -# -# Usage: bash install.sh [options] -# -# -y, --yes Assume "yes" for every prompt (also the behavior when -# stdin is not a terminal, e.g. curl | bash or CI). -# --gpu Build with CUDA support without asking. -# --no-gpu Build without GPU support without asking. -# -j, --jobs Parallel build jobs (default: number of cores). -# --fresh Clear all build directories first. The default reuses -# them, so re-running after a failure or a source -# update is an incremental rebuild. -# --modify-rc Append RANDNLA_PROJECT_DIR / RANDNLA_PROJECT_GPU_AVAIL -# exports to your shell config. The default never -# touches your shell config; the final summary prints -# the export lines to add yourself if you want them. -# --project-dir Place/locate RandNLA-project at D instead of next to -# this clone. -# -h, --help Show this help and exit. -# -# Every option has an environment-variable equivalent (flags win): -# RANDLAPACK_INSTALL_YES=1, RANDLAPACK_INSTALL_GPU=on|off, -# RANDLAPACK_INSTALL_JOBS=N, RANDLAPACK_INSTALL_FRESH=1, -# RANDLAPACK_INSTALL_MODIFY_RC=1, RANDLAPACK_INSTALL_PROJECT_DIR=D -# -# Already-installed dependencies are discovered through: -# BLASPP_INSTALL_DIR, LAPACKPP_INSTALL_DIR, RANDOM123_INSTALL_DIR -# (RandBLAS is intentionally not covered: it stays a git submodule.) -# -# All compiler output goes to /install.log; the console shows one -# line per step. On failure the log path is printed. -# -# Prerequisites are listed in INSTALL.md. -set -euo pipefail - -#============================================================================== -# Option parsing. Environment variables provide defaults; flags override. -#============================================================================== -ASSUME_YES="${RANDLAPACK_INSTALL_YES:-0}" -GPU_CHOICE="${RANDLAPACK_INSTALL_GPU:-ask}" # ask | on | off -JOBS="${RANDLAPACK_INSTALL_JOBS:-}" -FRESH="${RANDLAPACK_INSTALL_FRESH:-0}" -MODIFY_RC="${RANDLAPACK_INSTALL_MODIFY_RC:-0}" -PROJECT_DIR_OVERRIDE="${RANDLAPACK_INSTALL_PROJECT_DIR:-}" - -usage() { sed -n '2,45p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; } - -while [[ $# -gt 0 ]]; do - case "$1" in - -y|--yes) ASSUME_YES=1 ;; - --gpu) GPU_CHOICE="on" ;; - --no-gpu) GPU_CHOICE="off" ;; - -j|--jobs) JOBS="${2:?--jobs requires a number}"; shift ;; - --jobs=*) JOBS="${1#*=}" ;; - --fresh) FRESH=1 ;; - --modify-rc) MODIFY_RC=1 ;; - --project-dir) PROJECT_DIR_OVERRIDE="${2:?--project-dir requires a path}"; shift ;; - --project-dir=*) PROJECT_DIR_OVERRIDE="${1#*=}" ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1 (see --help)" >&2; exit 2 ;; - esac - shift -done - -# Prompts happen only on a terminal and only without --yes. When stdin is not -# a terminal (piped/CI), every prompt silently takes its default. -INTERACTIVE=0 -if [[ -t 0 && "$ASSUME_YES" != "1" ]]; then - INTERACTIVE=1 -fi - -# ask -> returns 0 for yes. -ask() { - local question="$1" default="$2" reply - if [[ "$INTERACTIVE" != "1" ]]; then - [[ "$default" == "y" ]] - return - fi - read -r -p "$question [$( [[ $default == y ]] && echo Y/n || echo y/N )]: " reply - reply="${reply:-$default}" - [[ "$reply" == "y" || "$reply" == "Y" || "$reply" == "yes" ]] -} - -if [[ -z "$JOBS" ]]; then - JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 8) -fi - -# Plain output when not on a terminal or when NO_COLOR/TERM=dumb ask for it. -if [[ -t 1 && -z "${NO_COLOR:-}" && "${TERM:-}" != "dumb" ]]; then - C_OK=$'\033[32m'; C_ERR=$'\033[31m'; C_BOLD=$'\033[1m'; C_OFF=$'\033[0m' -else - C_OK=""; C_ERR=""; C_BOLD=""; C_OFF="" -fi - -#============================================================================== -# Toolchain checks. Warn always; abort only if the user says so at a prompt. -#============================================================================== -PREFERRED_GCC_VERSION="13.3.0" -CURRENT_GCC_VERSION=$(gcc --version 2>/dev/null | head -n 1 | awk '{print $NF}') -if [[ "$CURRENT_GCC_VERSION" != "$PREFERRED_GCC_VERSION" ]]; then - echo "Note: GCC $PREFERRED_GCC_VERSION is the reference version; found ${CURRENT_GCC_VERSION:-none}." - if ! ask "Continue with the current GCC?" y; then - echo "Stopping at your request. Install GCC $PREFERRED_GCC_VERSION and re-run." - exit 1 - fi -fi - -#============================================================================== -# GPU decision. --gpu/--no-gpu (or RANDLAPACK_INSTALL_GPU) decide outright; -# otherwise detection + prompt. Non-interactive defaults: NVIDIA detected -> -# GPU on; AMD or nothing detected -> GPU off (the CUDA-only build cannot -# succeed on AMD, so saying yes for the user would guarantee a failure). -#============================================================================== -RANDLAPACK_CUDA="OFF" -RANDNLA_PROJECT_GPU_AVAIL="none" -case "$GPU_CHOICE" in - on) RANDLAPACK_CUDA="ON"; RANDNLA_PROJECT_GPU_AVAIL="auto" ;; - off) ;; - ask) - if command -v nvidia-smi &> /dev/null; then - if ask "NVIDIA GPU detected. Build with CUDA support?" y; then - RANDLAPACK_CUDA="ON"; RANDNLA_PROJECT_GPU_AVAIL="auto" - fi - elif { command -v lspci &>/dev/null && lspci | grep -i "VGA" | grep -qi "AMD"; } || \ - { [[ "$(uname)" == "Darwin" ]] && system_profiler SPDisplaysDataType 2>/dev/null | grep -qi "AMD"; }; then - if ask "AMD GPU detected, but only a CUDA build is available for now. Attempt a CUDA build anyway?" n; then - RANDLAPACK_CUDA="ON"; RANDNLA_PROJECT_GPU_AVAIL="auto" - fi - else - echo "No GPU detected; building without GPU support." - fi - ;; - *) echo "RANDLAPACK_INSTALL_GPU must be 'on' or 'off' (got '$GPU_CHOICE')" >&2; exit 2 ;; -esac - -if [[ "$RANDNLA_PROJECT_GPU_AVAIL" == "auto" ]]; then - PREFERRED_NVCC_VERSION="12.9" - CURRENT_NVCC_VERSION=$(nvcc --version 2>/dev/null | grep "release" | awk '{print $5}' | cut -d',' -f1) - if [[ "$CURRENT_NVCC_VERSION" != "$PREFERRED_NVCC_VERSION" ]]; then - echo "Note: NVCC $PREFERRED_NVCC_VERSION is the reference version; found ${CURRENT_NVCC_VERSION:-none}." - if ! ask "Continue with the current NVCC?" y; then - echo "Stopping at your request. Install NVCC $PREFERRED_NVCC_VERSION and re-run." - exit 1 - fi - fi -fi - -#============================================================================== -# macOS preflight: Homebrew OpenBLAS + libomp, SDK C++ headers, OpenMP hints. -#============================================================================== -BLAS_INT="int64" -MACOS_BLAS_FLAGS="" -MACOS_LAPACK_FLAGS="" -MACOS_OPENMP_FLAGS="" -if [[ "$(uname)" == "Darwin" ]]; then - if [[ ! -f /opt/homebrew/opt/openblas/lib/libopenblas.dylib ]]; then - echo "ERROR: OpenBLAS not found. Install it first: brew install openblas" >&2 - exit 1 - fi - if [[ ! -f /opt/homebrew/opt/libomp/lib/libomp.dylib ]]; then - echo "ERROR: libomp not found. Install it first: brew install libomp" >&2 - exit 1 - fi - BLAS_INT="int32" - MACOS_SDK_PATH=$(xcrun --show-sdk-path) - # SDK C++ headers + Apple Clang OpenMP flags (no native OpenMP; Homebrew - # libomp). Appending to CXXFLAGS/CFLAGS lets cmake pick them up via - # CMAKE__FLAGS_INIT for all try_compile tests, including FindOpenMP. - export CXXFLAGS="-isystem ${MACOS_SDK_PATH}/usr/include/c++/v1 -Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" - export CFLAGS="-Xpreprocessor -fopenmp -I/opt/homebrew/opt/libomp/include" - export LDFLAGS="-L/opt/homebrew/opt/libomp/lib" - MACOS_BLAS_FLAGS="-DBLAS_LIBRARIES=/opt/homebrew/opt/openblas/lib/libopenblas.dylib -Dblas_fortran=add" - MACOS_LAPACK_FLAGS="-DLAPACK_LIBRARIES=/opt/homebrew/opt/openblas/lib/libopenblas.dylib" - MACOS_OPENMP_FLAGS="-DOpenMP_C_LIB_NAMES=omp -DOpenMP_CXX_LIB_NAMES=omp -DOpenMP_omp_LIBRARY=/opt/homebrew/opt/libomp/lib/libomp.dylib -DOpenMP_C_FLAGS=-Xpreprocessor;-fopenmp -DOpenMP_CXX_FLAGS=-Xpreprocessor;-fopenmp" -fi - -#============================================================================== -# Project layout. The clone moves itself into /RandNLA-project/lib/ -# on first run; on re-runs (script already under lib/) the layout is detected. -#============================================================================== -SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") -PARENT_DIR=$(dirname "$SCRIPT_DIR") -PARENT_BASE=$(basename "$PARENT_DIR") -if [[ -n "$PROJECT_DIR_OVERRIDE" ]]; then - RANDNLA_PROJECT_DIR="$PROJECT_DIR_OVERRIDE" -elif [[ "$PARENT_BASE" == "lib" ]]; then - RANDNLA_PROJECT_DIR=$(dirname "$PARENT_DIR") -else - RANDNLA_PROJECT_DIR="$PARENT_DIR/RandNLA-project" -fi - -mkdir -p "$RANDNLA_PROJECT_DIR"/{install,lib,build} -for d in blaspp-build lapackpp-build RandLAPACK-build extras-build benchmark-build; do - if [[ "$FRESH" == "1" ]]; then - rm -rf "$RANDNLA_PROJECT_DIR/build/$d" - fi - mkdir -p "$RANDNLA_PROJECT_DIR/build/$d" -done - -LOG="$RANDNLA_PROJECT_DIR/install.log" -: > "$LOG" -echo "RandLAPACK install started $(date)" >> "$LOG" - -# run_step