Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ All notable changes to CableDyn are recorded in this file. The format follows

### Changed

- The Windows release executables (`CableDyn_driver.exe`, `openfast.exe`) are compiled for
processors with AVX2 (x86-64-v3: Intel Haswell and AMD Excavator or newer; CMake option
`CABLEDYN_TARGET_AVX2`), which makes the driver 13-33% faster on the benchmark decks
(`lazy_wave_vessel_rao` 11.9 s to 8.8 s, `als_volturnus_line_break_time` 35.5 s to 23.9 s).
On an older processor they stop at start with a message naming the missing instructions.
Results change at round-off: in the last printed digit on the lazy-wave and buoy decks; in the
line-break deck the platform's slow drift after the break amplifies the round-off past 900 s,
with the same peaks and standard deviations to four digits.
- OpenFAST: `CalcOutput` no longer evaluates the reactions of the finite-EI cables when it saves
the committed kinematics before transferring the current ones; it discarded them. With a
248-element power cable in the IEA-15 VolturnUS-S mooring this cuts `CalcOutput` from 41 s to
Expand Down
54 changes: 54 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ option(CABLEDYN_ENABLE_COVERAGE "Instrument the GNU Fortran core for gcov/gcovr
option(CABLEDYN_ENABLE_OPENMP "Enable OpenMP acceleration when the compiler provides it" ON)
option(CABLEDYN_STATIC_WINDOWS_RELEASE
"Build a single-file non-OpenMP Windows release with Intel IFX/MSVC" OFF)
option(CABLEDYN_TARGET_AVX2
"Compile the Fortran core for x86-64-v3 (AVX2, FMA, BMI1/2); programs then stop with a message on older processors"
OFF)

if(CABLEDYN_STATIC_WINDOWS_RELEASE)
if(NOT WIN32 OR NOT CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM" OR NOT MSVC)
Expand All @@ -25,6 +28,8 @@ if(CABLEDYN_STATIC_WINDOWS_RELEASE)
# static MSVC CRT, and no OpenMP runtime dependency (IFX has no static OpenMP
# runtime on Windows).
set(CABLEDYN_ENABLE_OPENMP OFF CACHE BOOL "" FORCE)
# The release executables target x86-64-v3 (CABLEDYN_TARGET_AVX2 below).
set(CABLEDYN_TARGET_AVX2 ON CACHE BOOL "" FORCE)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded" CACHE STRING "" FORCE)
# Large finite-EI meshes create compiler-generated array temporaries in deeply
# nested residual/assembly calls. Keep those temporaries off the process stack;
Expand Down Expand Up @@ -63,6 +68,39 @@ if(CABLEDYN_STATIC_WINDOWS_RELEASE)
endforeach()
endif()

# x86-64-v3 (Intel Haswell, AMD Excavator and newer): the vector maths of the wave
# kinematics and the element kernels run four doubles wide instead of two. On the release
# benchmark decks the IFX driver is 13-33% faster; results change at round-off (the wider
# vector maths), identically on every x86-64-v3 processor. Contraction into fused
# multiply-adds stays off: the compiler would fuse the same expression differently where it
# is inlined, so a restored or re-entered state would no longer continue a run bit for bit
# (test_line_attachments), for 2-5% more speed. IFX takes the /arch form, documented for
# any processor with these instructions (the /Qx form is documented for Intel processors;
# both gave the same code and speed on the benchmarks).
# Only the Fortran sources take it: src/cabledyn_cpu_guard.c stays baseline x86-64 and
# stops a program on an older processor with a message before any Fortran code runs.
if(CABLEDYN_TARGET_AVX2)
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64|X86_64)$")
message(FATAL_ERROR "CABLEDYN_TARGET_AVX2 needs an x86-64 target (found ${CMAKE_SYSTEM_PROCESSOR})")
endif()
if(CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM")
if(WIN32)
string(APPEND CMAKE_Fortran_FLAGS " /arch:CORE-AVX2 /Qfma-")
else()
string(APPEND CMAKE_Fortran_FLAGS " -march=core-avx2 -no-fma")
endif()
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 11)
message(FATAL_ERROR "CABLEDYN_TARGET_AVX2 needs GNU Fortran 11 or newer (-march=x86-64-v3)")
endif()
string(APPEND CMAKE_Fortran_FLAGS " -march=x86-64-v3 -ffp-contract=off")
else()
message(FATAL_ERROR "CABLEDYN_TARGET_AVX2 supports Intel IFX and GNU Fortran")
endif()
set_source_files_properties(src/cabledyn_cpu_guard.c PROPERTIES
COMPILE_DEFINITIONS CABLEDYN_REQUIRE_X86_64_V3)
endif()

# src/cabledyn_mutex.c uses C11 <stdatomic.h>, which the MSVC C compiler provides only
# behind this flag, in every MSVC configuration (static release or not).
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
Expand Down Expand Up @@ -328,6 +366,7 @@ add_library(cabledyn_obj OBJECT
src/cabledyn_mutex.c
src/cabledyn_blas.c
src/cabledyn_crt_locale.c
src/cabledyn_cpu_guard.c
src/cabledyn_path.c
src/cabledyn_fatal.c
src/CableDyn_Precision.f90
Expand Down Expand Up @@ -1996,6 +2035,21 @@ add_executable(test_crt_locale_guard tests/test_crt_locale_guard.f90 tests/crt_l
target_link_libraries(test_crt_locale_guard PRIVATE cabledyn_core)
add_test(NAME crt_locale_guard COMMAND test_crt_locale_guard $<IF:$<BOOL:${MINGW}>,1,0>)
set_tests_properties(crt_locale_guard PROPERTIES LABELS "fortran;unit")
# The x86-64-v3 start-up processor check is armed exactly in CABLEDYN_TARGET_AVX2 builds.
add_executable(test_cpu_guard tests/test_cpu_guard.f90)
target_link_libraries(test_cpu_guard PRIVATE cabledyn_core)
add_test(NAME cpu_guard COMMAND test_cpu_guard $<IF:$<BOOL:${CABLEDYN_TARGET_AVX2}>,1,0>)
set_tests_properties(cpu_guard PROPERTIES LABELS "fortran;unit")
if(CABLEDYN_TARGET_AVX2)
# The armed check stops the driver before any Fortran code runs, with the message naming
# what is missing (a simulated feature, so the stop is exercised on any processor).
add_test(NAME cpu_guard_stop
COMMAND ${CMAKE_COMMAND} -E env CABLEDYN_CPU_GUARD_SIMULATE=SIMULATED-FEATURE
$<TARGET_FILE:cabledyn> --version)
set_tests_properties(cpu_guard_stop PROPERTIES LABELS "fortran;unit"
PASS_REGULAR_EXPRESSION "This computer lacks: SIMULATED-FEATURE"
FAIL_REGULAR_EXPRESSION "CableDyn v")
endif()
# An abnormal end of the driver process is never silent: a stack overflow (on a 1 MiB stack
# in Windows GNU builds), an access violation and, off Windows, SIGTERM each end the process
# with a report on stderr of the cause and the simulated time reached; the march records the
Expand Down
12 changes: 12 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ The Debug configuration (`-DCMAKE_BUILD_TYPE=Debug`) adds `-fcheck=all
conda OpenBLAS through `CONDA_PREFIX`. `cmake --install build --prefix stage`
installs the driver, the shared library, and `CableDyn_CAPI.h`.

`-DCABLEDYN_TARGET_AVX2=ON` compiles the Fortran core for x86-64-v3 (AVX2, FMA,
BMI1/2: Intel Haswell and AMD Excavator or newer) with IFX (`/arch:CORE-AVX2`) or
gfortran 11+ (`-march=x86-64-v3`), with contraction into fused multiply-adds off
(`/Qfma-`, `-ffp-contract=off`) so a restored state still continues a run bit for
bit. The static Windows release always sets it: its IFX driver runs the benchmark
decks 13-33% faster, and the results change at round-off. Such a build checks the
processor when it starts
(`src/cabledyn_cpu_guard.c`, compiled for baseline x86-64) and stops with a
message naming the missing instructions instead of crashing on the first one;
the driver's `--version` output and the OpenFAST module's start-up lines report the
x86-64-v3 build. The default (`OFF`) runs on any x86-64 processor.

A Windows build with gfortran links every executable with a 64 MiB stack reserve
(the MinGW default is 2 MiB). With OpenMP enabled, gfortran keeps fixed-size local
arrays on the stack, and the stack depth of the OpenBLAS kernels differs between
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ a named error rather than running approximately.

| Asset | Contents |
|---|---|
| `CableDyn_driver.exe` | Standalone solver for Windows x64 |
| `CableDyn_driver.exe` | Standalone solver for Windows x64 (AVX2 processor) |
| `openfast.exe` | OpenFAST with CableDyn available as `CompMooring = 5` |
| `cabledyn-0.2.0-py3-none-any.whl` | Python package (pre- and post-processing, batch runs) |
| `cabledyn-0.2.0.tar.gz` | Python source distribution |
Expand Down
14 changes: 14 additions & 0 deletions app/cabledyn.f90
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ PROGRAM cabledyn
CD_HermiteCable_Dyn_Recovery_Reset, CD_HermiteCable_Dyn_Recovery_Count, &
CD_HermiteCable_Dyn_Get_Assembly_Profile, HC_ASM_NSEC, HC_ASM_NAMES
USE, INTRINSIC :: ISO_FORTRAN_ENV, ONLY: error_unit, output_unit, int64
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT
IMPLICIT NONE
TYPE :: StandaloneProgress
INTEGER :: nstep = 0, stride = 1
Expand All @@ -56,6 +57,14 @@ PROGRAM cabledyn
CHARACTER(*), PARAMETER :: PROG = 'CableDyn_driver'
! Lock file beside the outputs that keeps a second run off the same output root.
CHARACTER(*), PARAMETER :: LOCK_SUFFIX = '.cabledyn.lock'
INTERFACE
! 1 when this build targets x86-64-v3 and carries the start-up processor check of
! src/cabledyn_cpu_guard.c (the reference also keeps that check in the static link)
FUNCTION cpu_guard_armed() BIND(C, name='cabledyn_cpu_guard_armed') RESULT(armed)
IMPORT :: C_INT
INTEGER(C_INT) :: armed
END FUNCTION cpu_guard_armed
END INTERFACE
! Longest suffix a run appends to the output root (".Line<id>.range.out" with a 10-digit id).
INTEGER, PARAMETER :: MAX_OUTPUT_SUFFIX = 25
! Command-line paths as UTF-8; the native spellings are what the Fortran runtime opens.
Expand All @@ -80,6 +89,11 @@ PROGRAM cabledyn
SELECT CASE (TRIM(deck_path))
CASE ('-v', '-V', '-version', '-VERSION', '--version')
CALL CD_Print_Banner()
IF (cpu_guard_armed() /= 0) THEN
WRITE (output_unit, '(A)') ' Build x86-64-v3 (AVX2)'
ELSE
WRITE (output_unit, '(A)') ' Build baseline'
END IF
STOP 0, QUIET = .TRUE.
CASE ('-h', '-H', '-help', '-HELP', '--help', '-?', '/?')
CALL CD_Print_Banner()
Expand Down
8 changes: 8 additions & 0 deletions doc/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ What is on the release page
the ``examples/`` folder of the **Source code (zip)** archive on the same release page.
Download it once and unzip it next to the executables.

.. note::

The executables need a 64-bit processor with AVX2 (the x86-64-v3 level: Intel Haswell,
2013, and AMD Excavator, 2015, or newer), for which they are compiled. On an older processor
they stop at start with a message that names the missing instructions; there, build CableDyn
from source as `DEVELOPMENT.md
<https://github.com/SMI-Lab-Inha/CableDyn/blob/main/DEVELOPMENT.md>`_ describes.

Download and verify
~~~~~~~~~~~~~~~~~~~

Expand Down
30 changes: 27 additions & 3 deletions release/build_static_windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ $common = '-G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_Fortran_COMPILER=ifx ' +
# so both executables link the netlib reference LAPACK/BLAS, compiled here with the
# release IFX from a pinned, checksummed source archive. /fp:precise keeps IEEE
# semantics inside LAPACK; the upstream CMake applies a strict model only on Unix.
# /arch:CORE-AVX2 is the x86-64-v3 target of the CableDyn core (CABLEDYN_TARGET_AVX2).
$lapackVersion = '3.12.1'
$lapackSha256 = '2ca6407a001a474d4d4d35f3a61550156050c48016d949f0da0529c0aa052422'
$lapackUrl = "https://github.com/Reference-LAPACK/lapack/archive/refs/tags/v$lapackVersion.tar.gz"
Expand Down Expand Up @@ -160,7 +161,7 @@ Invoke-StaticToolchain ("cmake -S `"$lapackSource`" -B `"$lapackBuild`" $common
'-DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DBUILD_SINGLE=OFF -DBUILD_DOUBLE=ON ' +
'-DBUILD_COMPLEX=OFF -DBUILD_COMPLEX16=OFF -DBUILD_INDEX64_EXT_API=OFF ' +
'-DUSE_OPTIMIZED_BLAS=OFF -DUSE_OPTIMIZED_LAPACK=OFF ' +
'"-DCMAKE_Fortran_FLAGS=/nologo /libs:static /fp:precise" ' +
'"-DCMAKE_Fortran_FLAGS=/nologo /libs:static /fp:precise /arch:CORE-AVX2" ' +
"`"-DCMAKE_INSTALL_PREFIX=$lapackInstall`"")
Invoke-StaticToolchain "cmake --build `"$lapackBuild`" --target blas lapack -j $Jobs"
Invoke-StaticToolchain "cmake --install `"$lapackBuild`""
Expand Down Expand Up @@ -220,8 +221,28 @@ Copy-Item -LiteralPath $vsOpenFAST -Destination $openfast -Force
$savedPath = $env:PATH
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot"
try {
& $driver --version
if ($LASTEXITCODE -ne 0) { throw 'CableDyn_driver.exe --version failed in isolated PATH' }
$versionRun = Invoke-NativeCapture $driver @('--version')
Write-Host $versionRun.Text
if ($versionRun.ExitCode -ne 0) { throw 'CableDyn_driver.exe --version failed in isolated PATH' }
# the release targets x86-64-v3 and carries the start-up processor check
if ($versionRun.Text -notmatch 'Build\s+x86-64-v3') {
throw "CableDyn_driver.exe is not the x86-64-v3 build with the processor check`n$($versionRun.Text)"
}
# On a processor without x86-64-v3 both executables stop at start with the message
# (simulated here): exit 3 before any CableDyn or OpenFAST code prints.
$env:CABLEDYN_CPU_GUARD_SIMULATE = 'SIMULATED-FEATURE'
try {
foreach ($stopCase in @(@($driver, '--version'), @($openfast, '-h'))) {
$stopRun = Invoke-NativeCapture $stopCase[0] @($stopCase[1])
if ($stopRun.ExitCode -ne 3 -or $stopRun.Text -notmatch 'This computer lacks: SIMULATED-FEATURE' -or
$stopRun.Text -match 'CableDyn v|OpenFAST') {
throw "$($stopCase[0]) did not stop at start on a processor without x86-64-v3 " +
"(exit $($stopRun.ExitCode))`n$($stopRun.Text)"
}
}
} finally {
Remove-Item Env:CABLEDYN_CPU_GUARD_SIMULATE -ErrorAction SilentlyContinue
}
$driverRoot = Join-Path $cdBuild 'driver_static_smoke'
& $driver (Join-Path $CableDynRoot 'examples\chain_catenary_shallow_30m.dat') $driverRoot
if ($LASTEXITCODE -ne 0) { throw 'CableDyn_driver.exe static smoke case failed' }
Expand Down Expand Up @@ -399,6 +420,9 @@ try {
if ($runLog -notmatch 'Running CableDyn \(' -or $runLog -notmatch 'OpenFAST terminated normally') {
throw "openfast.exe did not prove CableDyn identity and normal termination`n$runLog"
}
if ($runLog -notmatch 'CableDyn build: x86-64-v3') {
throw "openfast.exe does not carry the x86-64-v3 CableDyn core with the processor check`n$runLog"
}
if ($runLog -notmatch 'Created CableDyn model:' -or
$runLog -notmatch 'Line 1 fairlead effective tension:' -or
$runLog -notmatch ('Requested CableDyn OUTPUTS at t = 0 s \(equilibrium pose, SeaState ' +
Expand Down
Loading
Loading