Warrior_EA/DirectML/build_cpu.bat
AnimateDread a9677bb538 perf: the CPU DLL's dot products were never vectorized - /fp:fast
Measured on this machine's actual CPU at the real 760-wide geometry, through a
real DLL boundary (an earlier harness #included the .cpp and the fast-math
build hoisted the timing loop, reporting a flat ~4us for shapes 8x apart).

Every hot kernel is a floating-point reduction. Under the default /fp:precise
MSVC may not reassociate one, so it cannot vectorize one - the dot product was
scalar mulsd/addsd through a single accumulator. Forward pass measured
1.1-1.9 GFLOP/s precise vs 1.7-2.8 GFLOP/s fast, and the same three
neurons*inputs loops (forward, hidden gradient, weight-gradient accumulate)
dominate an era.

batch_accum_check passes on both builds with identical output to every digit
it prints, including the 5-decade optimizer scale-invariance sweep. The
deploy-time CPU-vs-MQL5 self-check tolerance is 1.0e-3, ~11 orders looser than
fast-math drift.

/arch:AVX2 is now explicitly forbidden in the script with the reason. This CPU
is an Ivy Bridge-EP Xeon: AVX yes, AVX2/FMA no. An AVX2 build faults on every
kernel, SehCallFn swallows it per dispatch, buffers are never written, and
every shape takes a flat ~5us - which benchmarks as a 250x speedup until you
check that the outputs are all zero. /arch:AVX alone was measured and bought
nothing; these loops are memory-bound and Ivy Bridge splits 256-bit loads
into 2x128 anyway.

Requires rebuilding WarriorCPU.dll (build_cpu.bat) - the .ex5 is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:20:12 -04:00

83 lines
3.5 KiB
Batchfile

@echo off
REM Builds WarriorCPU.dll with MSVC. Run this from a plain cmd/PowerShell
REM window - it locates and loads the VS build environment itself, no
REM need to open a "Developer Command Prompt" first.
setlocal
set VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist %VSWHERE% (
echo Could not find vswhere.exe - is Visual Studio / Build Tools installed?
exit /b 1
)
for /f "usebackq tokens=*" %%i in (`%VSWHERE% -latest -products * -property installationPath`) do (
set VSINSTALL=%%i
)
if not defined VSINSTALL (
echo Could not find any Visual Studio / Build Tools installation.
exit /b 1
)
if not exist "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat" (
echo Found a VS install at "%VSINSTALL%" but it has no C++ toolset ^(vcvars64.bat missing^).
echo Install the "Desktop development with C++" workload ^(or the smaller
echo "Build Tools for Visual Studio"^) and re-run this script.
exit /b 1
)
call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
if errorlevel 1 exit /b 1
cd /d "%~dp0"
REM ---------------------------------------------------------------------------
REM /fp:fast is load-bearing, not cosmetic. Every hot kernel in WarriorCPU.cpp
REM is a floating-point REDUCTION (`sum += in[k] * w[shift+k]` and friends).
REM Under the default /fp:precise the compiler may not reassociate a reduction,
REM so it cannot vectorize one either - the dot product compiles to scalar
REM mulsd/addsd chained through a single accumulator, one double at a time.
REM Measured on the Xeon E5-1650 v2 this trains on, at the real 760-wide
REM geometry: 1.1-1.9 GFLOP/s precise vs 1.7-2.8 GFLOP/s fast, i.e. ~1.5-2x on
REM the three neurons*inputs MAC loops that dominate a training era (forward,
REM hidden gradient, weight-gradient accumulate).
REM Verified safe by DirectML\batch_accum_check.bat: every check passes with
REM byte-identical output to the /fp:precise build, and the deploy-time
REM CPU-vs-MQL5 self-check tolerance (CPU_INFERENCE_MAX_DIFF) is 1.0e-3, some
REM eleven orders of magnitude looser than the drift fast-math can introduce.
REM
REM DO NOT ADD /arch:AVX2. This machine's CPU (Ivy Bridge-EP, 2013) has AVX but
REM NOT AVX2 or FMA, so an AVX2 build turns every kernel into an illegal
REM instruction. That failure is SILENT and looks like success: SehCallFn
REM catches the fault per dispatch, the kernel returns 0, buffers stay
REM untouched, and every shape takes a flat ~5us regardless of size - which
REM benchmarks as a 250x "speedup" until you notice the outputs are all zero.
REM /arch:AVX alone was measured too and bought nothing (these loops are
REM memory-bound, and Ivy Bridge splits 256-bit loads into 2x128 internally).
REM ---------------------------------------------------------------------------
cl.exe /nologo /LD /EHsc /O2 /fp:fast /std:c++17 ^
WarriorCPU.cpp ^
/Fe:WarriorCPU.dll
if errorlevel 1 (
echo Build failed.
exit /b 1
)
set "TARGET_ROOT=%APPDATA%\MetaQuotes"
if exist "%TARGET_ROOT%\Terminal" (
for /d %%D in ("%TARGET_ROOT%\Terminal\*") do (
if exist "%%~fD\MQL5\Libraries" (
copy /Y "%~dp0WarriorCPU.dll" "%%~fD\MQL5\Libraries\WarriorCPU.dll" >nul 2>&1
)
)
)
if exist "%TARGET_ROOT%\Tester" (
for /d %%D in ("%TARGET_ROOT%\Tester\*") do (
if exist "%%~fD\MQL5\Libraries" (
copy /Y "%~dp0WarriorCPU.dll" "%%~fD\MQL5\Libraries\WarriorCPU.dll" >nul 2>&1
)
)
)
echo.
echo Built DirectML\WarriorCPU.dll
echo Deployed to any discovered MetaTrader MQL5\Libraries folders.
endlocal