Verification on Windows x64, NVIDIA GPU and Excel
This report covers what could not be verified on the arm64 development Mac (no GPU, no Windows Excel) or on the ubuntu
CI: the Hybridizer-related tests on x64, CUDA generation, GPU = CPU equality, GPU performance and the Excel add-in.
Branch verif/windows-gpu, taken from main after PRs #62 and #63 were merged.
Environment
| Item | Version |
|---|---|
| OS | Windows 11 Home 10.0.26300, x64 |
| CPU | AMD Ryzen 9 7845HX, 12 cores / 24 threads, 63 GB RAM (laptop, on mains power) |
| GPU | NVIDIA GeForce RTX 4070 Laptop GPU, sm_89, 36 SMs, 8 GB, WDDM mode |
| NVIDIA driver | 610.74 (CUDA UMD 13.3) |
| CUDA toolkit | 13.0 (nvcc V13.0.48), CUDA_PATH set |
| .NET | SDK 8.0.425 (installed during the verification), 9.0.318, 10.0.401; runtimes 8.0.31, 9.0.20, 10.0.12 |
| Hybridizer | global tool hybridizer 7.5.1, "Hybridizer Essentials", free edition: CUDA flavor only, JIT compilation by nvrtc; runtime Hybridizer.Runtime.CUDAImports 3.7.1 |
| nvcc host compiler | Visual Studio Community 2022 17.14 (C++ tools); VS 2026 18.10 also installed |
| Excel | Microsoft 365 (O365BusinessRetail), 16.0 build 20430, 64-bit, French interface |
| Intel MKL | absent |
The .NET 8 SDK was missing at the start: the first pass was built with SDK 10 (bypassing global.json).
It was then installed (winget install Microsoft.DotNet.SDK.8, 8.0.425); build, CUDA satellite and tests were
redone with it, with the same result (1556 passed, 10 skipped, 0 failed).
Missing, not installed:
- Intel MKL (optional): required by 7 tests (
mkl_rt). Suggested installation: Intel oneMKL, for example throughwinget search oneMKLthenwinget install <id found>, or the Intel oneAPI Base Toolkit installer.
Summary
| Item | Status |
|---|---|
x64 baseline: build and tests, [HybridizerFact] executed |
verified |
| CUDA generation by Hybridizer (opt-in step) | fixed (9 tool or library problems worked around), verified |
| GPU = CPU equality, all requested families except Longstaff-Schwartz | verified |
| Longstaff-Schwartz on GPU | failed, blocked by the free edition of Hybridizer (delegates) |
| Hybridizer runtime races on the GPU side | two data losses found, worked around, documented |
| BenchmarkDotNet benchmarks, all suites | verified |
| GPU vs CPU | measured |
| Excel add-in in a real 64-bit Excel | verified (64/64), one documented behavior corrected |
Commits on the branch (one per problem, Lore format):
- Reductions with
Action<int, int, T[]>delegates excluded from generation (Hybridizer emits a class deriving from::System::Array). - Longstaff-Schwartz regressor without
fixedbuffers (Hybridizer 7.5 generates undeclared pinned variables). SimulatableScheduler.SimulateSingle: 64-bit block bounds (257 paths out of 1,048,583 never simulated with 4096 blocks).- Ticket #60:
EuropeanCallOption<HullWhiteDiscounting, …>registered. - ClosedForm kernel classes made instantiable, so that
HybRunner.Wrapreaches their entry points. - Opt-in Hybridizer build step (
InFine.QuantFinance.Cuda,build/hybridizer). - GPU = CPU tests and wiring of
CudaBackend.Launcher. - Refresh of host copies after each launch (runtime race, CUDA error 700).
- Excel smoke test through COM and correction of
docs/excel.md. - GPU full-revaluation test extended to Sobol (
uint[]). - GPU vs CPU benchmarks, README and this report.
1. x64 baseline
dotnet build QuantFinance.sln -c Release: 0 errors. dotnet test on main before any change:
| Passed | Skipped | Failed |
|---|---|---|
| 1539 | 13 | 0 |
The [HybridizerFact] / [HybridizerTheory] tests, skipped on arm64, all run on x64. The 13 skipped tests were all
waiting for MKL or a Hybridizer-generated binary. At the end of the branch:
| Passed | Skipped | Failed |
|---|---|---|
| 1556 | 10 | 0 |
Remaining skipped tests, each with its reason:
| Test | Reason |
|---|---|
TestMethod_MRG32K3A_MKL, TestMethod_MRG32K3A_MKL_GET_NAN, AmericanCall_CSharp, AmericanCall_CUDA |
MKL absent |
AmericanCall_AVX, AmericanCall_OMP, TestMethod_AmericanPut_LSPaper_AVX, TestMethod_MRG32K3A_VsOMP |
AVX/OMP flavors absent from the free edition of Hybridizer (and MKL for the first two) |
Cuda_LongstaffSchwartz_PaperExample_GivesThePublishedPrice, Cuda_LongstaffSchwartz_AmericanPut_MatchesTheHostPrice |
delegates not supported (section 3) |
The GPU tests run in a few seconds, but most of that time goes to the CPU references and to module loading: each
kernel lasts only a few milliseconds. The GPU tests that run: Cuda_* of Coherence (9), ClosedFormCudaTests (4),
FullRevaluationCudaTests (Philox and Sobol), BacktestCudaTests, TestMethod_MRG32K3A_VsCuda. Three consecutive runs of
Coherence.Tests: stable.
2. CUDA generation
Build step
dotnet build InFine.QuantFinance.Cuda/InFine.QuantFinance.Cuda.csproj -c Release -p:HybridizeCuda=true
hybridizes the ten kernel assemblies (Scheduling, Numerics, Arithmetic, ClosedForm, Random, Models, Payoffs, Risk,
Backtesting, LongstaffSchwartz), compiles the generated sources with nvcc and produces InFine.QuantFinance_CUDA.dll
(2.3 MB), copied to artifacts/cuda/. Directory.Build.targets drops it next to the test and benchmark assemblies.
Without the property, the project merely compiles: the ubuntu CI needs neither Hybridizer, nor CUDA, nor a GPU. Duration:
58 s of generation and nvrtc JIT, then 17 s of nvcc.
The chain follows hybridizer-io/hybridizer-basic-samples (src/Directory.Build.targets: CUDA version detection through
nvcc --version, vswhere then vcvarsall, nvcc on hybridizer.generated.cpp and hybridizer.wrappers.cu). The device
code is a cubin compiled by nvrtc for the GPU present at build time (--gpu-architecture=auto), embedded in
hybridizer.generated.cpp. The satellite is therefore not portable from one GPU architecture to another.
Problems encountered
| Problem | Cause | Resolution |
|---|---|---|
The hybridizer launcher hangs at 0% CPU, with no output |
deadlock in the launcher that relays the worker's output when it is large | direct call to dotnet Hybridizer.Application.dll |
input assembly not found, then a hang |
the tool resolves dependencies only next to each input | the project copies all its dependencies (CopyLocalLockFileAssemblies) |
System.Text.Json 10.0.0.0 not found |
runtime 3.7.1 depends on the System.Text.Json 10.0.1 package | same |
An item with the same key has already been added |
an assembly listed before one of its dependencies is loaded twice | topological order enforced (HybridizedAssembly) |
namespace "hybridizer" has no member "is_infinity" |
double.IsInfinity mapped to a function absent from the headers |
quantfinance.cuda.builtins, passed first (the first registration wins) |
IsNegativeInfinity mapped to hybridizer::is_even_integer |
error in the shipped builtins | not used by the kernels; flagged in the builtins file |
cannot open source file "quantfinance.tools.h" |
[IntrinsicInclude] headers left in research/ |
build/hybridizer/include, with __shfl_xor_sync (maskless __shfl_xor no longer exists since CUDA 9 on sm_70+) |
name followed by "::" must be a class or namespace name : ::System::Array |
generic delegate with an array argument (Action<int, int, T[]>) |
ParallelReduction<T> and SingleBlockParallelReduction<T>, unused on device, ignored |
identifier "a1" is undefined |
fixed statements translated into undeclared pinned variables |
LS regressor in scalar fields, bit-identical prices |
cuLinkComplete (300) device kernel image is invalid |
--relocatable-device-code=true |
option removed |
| A successful run displays the errors of the previous run | hybridizer.stderr is not truncated |
working folder emptied before each generation |
Known risk points
| Item | Status | Detail |
|---|---|---|
out parameters |
verified | BlackScholesKernels.Greeks / Black76Kernels.Greeks (10 and 9 out) through GreeksArray on GPU |
ulong, uint[] |
verified | ulong Philox seed (full revaluation, bootstrap); uint[] Sobol directions and offsets (full revaluation with Sobol) |
Cplx struct and operator overloads (Heston COS) |
verified | HestonKernels.PriceArray within 2.9e-15 of the forward |
Readonly structs Dual / Dual4 |
generated, not executed | the Arithmetic assembly hybridizes without error, but no entry point uses the duals yet |
Math.Sinh / Math.Pow ("Let's Be Rational") |
verified | ImpliedVolatilityKernels.Black76Array within 1.4e-15 of the CPU |
| Cross-assembly calls | verified | Risk → ClosedForm and Random, Payoffs → Models, Models → Numerics |
29-parameter [EntryPoint] SimulateScenarios |
verified | Philox and Sobol |
int indexing i*M + s of instrumentPnl |
verified, no fix | the host refuses M × N > Array.MaxLength (< 2^31) before allocating |
(found along the way) N * blockIdx.x in int in SimulateSingle |
fixed | 64-bit bounds, test with 4096 blocks |
3. GPU = CPU equality
CudaBackend.Launcher is wired by a module initializer of InFine.QuantFinance.Coherence.Tests
(CudaLauncher): HybRunner.Cuda(path).SetDistrib(32, 128).Wrap(new SimulatableScheduler()).SimulateSingle(...). The
library's simulatables run on the GPU; those of the test project (reference Philox Brownian fill, which allocates) stay
on the host. The expected binary is now called InFine.QuantFinance_CUDA.dll everywhere
(NativeEnvironment.CudaSatelliteName) instead of InFine.Quantfinance.KEPLER.dll. It is loaded by full path,
because HybRunner resolves a bare name relative to the current directory.
| Entry point / kernel | Test | Tolerance | Observed difference | Status |
|---|---|---|---|---|
BlackScholesMertonPrimitive_CallPut_Array(Multi) |
ClosedFormCudaTests (100,003 options) |
1e-12 rel. | 2.5e-13 | verified |
BlackScholesKernels_Price_Array / Greeks_Array |
same | price 1e-12, Greeks 1e-10 rel. | price 2.7e-13, theta 5.0e-12 | verified |
Black76Kernels_Price_Array / Greeks_Array |
same | same | price 2.7e-13, theta 1.7e-12 | verified |
ImpliedVolatilityKernels_Black76_Array |
same (96,024 identifiable options) | 1e-10 rel. | 1.4e-15 | verified |
HestonKernels_Price_Array |
same (20,011 options) | 1e-9 × F | 2.9e-15 × F | verified |
SimulateSingle + Black-Scholes-Merton |
coherence | log-spot 1e-11 abs. | 8.9e-16 | verified |
SimulateSingle + Heston (variance, spot) |
coherence (existing test) | that of the test | within tolerance | verified |
SimulateSingle + Hull-White (x, ln D) |
Cuda_CurveAndHullWhiteDiscounting |
1e-11 abs. | 2.8e-17 | verified |
SimulateSingle + correlated log-normal (3 assets) |
Cuda_MultiAsset… |
1e-11 abs. | 8.9e-16 | verified |
| European, ConstantRate / Curve / HullWhite discounting | coherence | premiums 1e-12 rel. | 1.6e-13 / 2.5e-14 / 2.5e-14 | verified |
| Arithmetic / geometric Asian | coherence | 1e-12 rel. | 2.2e-13 / 1.2e-13 | verified |
| Discrete barrier / Brownian bridge | coherence | 1e-12 rel. | 4.3e-14 / 9.2e-14 | verified |
| Floating lookback | coherence | 1e-12 rel. | 1.2e-14 | verified |
| Arithmetic / geometric basket | coherence | 1e-12 rel. | 1.2e-13 / 4.4e-14 | verified |
| Rainbow worst-of put / best-of call | coherence | 1e-12 rel. | 6.9e-14 / 8.2e-14 | verified |
| WorstOfDownAndInPut | coherence | 1e-12 rel. | 3.0e-15 | verified |
| Autocallable | coherence | 1e-12 rel. | 8.8e-16 | verified |
| VarianceSwap | coherence | 1e-12 rel. | 4.8e-15 | verified |
SimulatableScheduler_SimulateGroup (2 diffusions + 1 Asian) |
Cuda_SimulateGroup… |
same | 8.9e-16 / 2.2e-13 | verified |
FullRevaluationKernels_SimulateScenarios, Philox and Sobol |
FullRevaluationCudaTests (65,536 scenarios) |
1e-10 × max|P&L| | within tolerance | verified |
BacktestKernels_SimulateConstantMixPaths |
BacktestCudaTests (100,003 paths) |
1e-12 rel. | 0, except variance 7.2e-16 | verified |
| MRG32K3A (cuRAND) | TestMethod_MRG32K3A_VsCuda (existing test) |
that of the test | within tolerance | verified |
Longstaff-Schwartz (LSEntryPoint) |
Cuda_LongstaffSchwartz_* (skipped with reason) |
— | — | failed |
Philox words, bit for bit: verified indirectly but strictly. The bootstrap draws its row indices from the Philox words; over 100,003 paths × 500 periods, final wealth, drawdown, turnover and costs are bit-identical, which requires the same words on the device. No entry point exposes the raw words.
Longstaff-Schwartz. The pricer passes its kernels delegates created on the host: the LSHelper actions, and the
map / init functions of the reductions. For each of them, HybRunner looks for a device function pointer
(Could not get function pointer for method Void applyRegressor(alignedindex)). Hybridizer generates one only with
delegate support, and the free edition (7.5.1, and 7.6.3 as re-checked later) does not allow enabling it: DelegateSupport="false" in the generated
configuration, regenerated on every run, and no command-line option. The host paths remain covered by
Test_LongstaffSchwartzCoherence. Proposed fix: replace these delegates with generic functors registered through
HybridRegisterTemplate, the pattern of the rest of the library. That is a rework of LS outside the scope of this
verification.
Known runtime race and transfers
The documented race (DoubleResidentArray.CheckRefreshHost / AllocHost unsynchronized, worked around by pre-touching
the storages) also shows up on the GPU side, in two more serious forms:
- Data loss on chained launches. Marshalling an
alignedstorage_*re-uploads the host buffer even when its status isHostNeedsRefresh(the device holds the up-to-date data). A GPU diffusion followed by a GPU payoff on the same paths replaces those paths with the stale host copy: 526 wrong European premiums out of 1009, with no error. A host read between the two launches is enough to avoid it. - CUDA error 700 on concurrent read. If several threads perform at the same time the first host read of a storage
written by the device, the device → host copies fail with
cudaErrorIllegalAddress. The error is persistent and makes the CUDA context unusable. Reproduced 20 times out of 20 (200,003 paths, 24 threads), with or without pre-touch; a first read on a single thread is always correct.
CudaLauncher refreshes the stale host copies before and after each launch, on the launching thread, which works
around both problems at the cost of one device → host copy per step. Cuda_ResultsCanBeReadByManyThreadsAfterALaunch
fails without this refresh and passes with it. The root-cause fix belongs to the runtime: a lock in CheckRefreshHost,
and respecting the status when marshalling.
4. Performance
BenchmarkDotNet, existing suites
BenchmarkDotNet 0.15.8, default job, --inProcess, .NET 8.0.31 x64 RyuJIT (AVX-512). Total duration: 16 min 33 s,
21 benchmarks. Random, Diffusion and Longstaff-Schwartz, impossible on arm64, run here.
| Suite | Method | Size | Mean |
|---|---|---|---|
| ClosedForm | BlackScholesCallPut_ScalarLoop |
1,048,576 | 51.1 ms |
| ClosedForm | NormalCdf |
1,048,576 | 20.2 ms |
| Random | Mrg32k3a_Uniform |
1,048,576 | 11.0 ms |
| Random | Mrg32k3a_Gaussian_BoxMuller2 |
1,048,576 | 19.8 ms |
| Diffusion | Diffusion_Generics_CSharp |
512 / 16,384 paths | 0.66 ms / 28.0 ms |
| Longstaff-Schwartz | AmericanPut_CSharp_SerialReduction |
4096 / 16,384 paths | 6.3 ms / 23.7 ms |
Adjoint Greeks (20-asset basket, 50 steps, 1000 paths), compared with the Mac measurement in docs/architecture.md:
| Method | Sensitivities | Windows x64 | Ratio | Mac M5 Pro | Mac ratio |
|---|---|---|---|---|---|
| Valuation | 0 | 6.38 ms | 1.00 | 4.00 ms | 1.00 |
| Adjoint | 110 | 10.48 ms | 1.66 | 5.46 ms | 1.36 |
Tangent, 1 Dual4 pass |
4 | 9.65 ms | 1.53 | 6.17 ms | 1.54 |
| Tangent, 10 passes | 40 | 99.6 ms | 15.7 | 61.8 ms | 15.4 |
| Tangent, 28 passes | 110 | 282 ms | 44.6 | 173 ms | 43.2 |
The conclusion holds: the adjoint costs about 1.7 valuations for 110 sensitivities and beats the tangent from five inputs onward. The adjoint ratio is slightly higher here than on arm64.
GPU against CPU
GpuVsCpuBenchmarks and FullRevaluationGpuBenchmarks (new, skipped without a GPU or satellite). GPU measured end to
end: HybRunner copies the arrays to the device and back on every call. Kernel-only time comes from
HybRunner.LastKernelDuration, 1 ms resolution. CPU: all cores (Parallel.For), unless noted otherwise. Each
side is checked against the other before measuring.
| Workload | CPU 1 thread | CPU 24 threads | GPU, end to end | GPU, kernel only |
|---|---|---|---|---|
Black-Scholes, 1M options (PriceArray) |
60.9 ms | 5.9 ms | 23.9 ms | 12.5 ms |
| VaR full reval, M = 100k, N = 100, F = 101 | — | 74.3 ms | 46.3 ms | 36.3 ms |
| VaR full reval, M = 1M, N = 100, F = 101 | — | 740 ms | 303 ms | 292 ms |
| Constant-mix bootstrap, 100k paths × 2520 days, 10 assets | — | 604 ms | 275 ms | 269 ms |
In full-revaluation throughput:
| Machine | M | Scenarios/s | Revaluations/s |
|---|---|---|---|
| Mac arm64, 18 cores (reference) | 100k | 2.1 M | 2.1e8 |
| Ryzen 9 7845HX, 24 threads | 100k | 1.35 M | 1.35e8 |
| Ryzen 9 7845HX, 24 threads | 1M | 1.35 M | 1.35e8 |
| RTX 4070 Laptop, end to end | 100k | 2.2 M | 2.2e8 |
| RTX 4070 Laptop, end to end | 1M | 3.3 M | 3.3e8 |
Reading:
- These kernels are in double precision, and a consumer RTX 4070 computes double at 1/64 of the single-precision throughput. The GPU therefore wins by a factor of 2.2 to 2.4 on the heavy workloads (VaR at 1M scenarios, bootstrap), not by an order of magnitude. A compute GPU (A100, H100) or a switch to single precision would change the ratio.
- On Black-Scholes, 1M options, the GPU loses to the 24 threads (23.9 ms against 5.9 ms). Half of the time goes to the transfers of eight 8 MB arrays, the other half to the kernel (exp, log, erfc in double).
- The x64 CPU revalues 1.35 M scenarios/s, against 2.1 M on the 18-core Mac: with 12 physical cores, that is about 0.11 M/s per core here against 0.12 M/s on arm64.
Workloads sized to keep the GPU busy for several seconds: LongGpuBenchmarks (1 warm-up iteration, 3 measured,
7 min 23 s in total; at initialization, GPU and CPU are compared element by element). nvidia-smi sees the GPU above
90% for the whole duration of the kernels.
| Workload | CPU 24 threads | GPU, end to end | GPU, kernel | GPU / CPU |
|---|---|---|---|---|
| VaR full reval, 20M scenarios × 100 options × 101 factors | 12.96 s | 5.73 s | 5.70 s | ×2.3 |
| Bootstrap, 1M paths × 2520 days × 10 assets | 5.27 s | 2.62 s | 2.61 s | ×2.0 |
| Heston COS, 2M options | 8.67 s | 3.88 s | 3.86 s | ×2.2 |
GPU-CPU differences: at most 9e-16 of the maximum P&L (VaR), 7e-16 relative (bootstrap), 2.4e-15 of the forward (Heston). A first cold run, outside BenchmarkDotNet, gave 30 s, 12 s and 21 s of CPU, i.e. misleading ratios of ×4 to ×5: the first CPU pass pays for JIT compilation and the laptop's frequency ramp-up. The reliable ratio is ×2 to ×2.4, on these long workloads as on the shorter ones.
5. Excel add-in
InFine.QuantFinance.Excel/smoke/Invoke-ExcelSmokeTest.ps1 loads the packed 64-bit .xll into a private, hidden
Excel instance (RegisterXLL), writes the formulas through Range.Formula2 and reads Value2. Result: 64 checks out of 64.
| Behavior | Status | Detail |
|---|---|---|
Loading of the packed .xll |
verified | RegisterXLL true, .NET 8.0.31 hosted by Excel-DNA 1.9 |
25 QF.* functions |
verified | QF.BS.PRICE 10.450583572185572 / 5.5735260222569671 (10.4506 / 5.5735); QF.IMPLIEDVOL and QF.IMPLIEDVOL.BLACK76 round trips to 1e-16; QF.CURVE.BOOTSTRAP then QF.CURVE.DF (repricing error 2.1e-15); QF.SVI.FIT then QF.SURFACE.VOL which recovers the generating smile; covariance, parametric VaR and decomposition compared with independent PowerShell computations; QF.MCVAR within 2% of delta-normal |
| Omitted optional arguments | verified | convention, compounding, interpolation, calendar, method; omitted middle argument (QF.COV.OBJ(…, 0.97, , TRUE)) |
| Function wizard | verified once, by hand | category "InFine QuantFinance", signatures with argument names, descriptions (screenshot). Cannot be driven through COM; obtained with PrintWindow and posted keystrokes. Per-argument help was not verified |
QF.MCVAR: #N/A then result |
verified | |
| Cancellation on clearing or on argument change | verified | Excel's CPU goes from 22 cores to 0 in under a second; the uncancelled control stays at 22 cores |
| Recalculation during the computation | verified | it joins the computation in progress (result at 4.3 s instead of 6.9 s) |
| Recalculation after the result | fixed (doc) | Excel-DNA restarts the computation (#N/A, then the same figures); docs/excel.md promised re-reading of the finished result |
Handle versioning, #REF! on stale or deleted handle |
verified | QF.Curve:V#1 → #2; identical recreation without a version change |
| Workbook reopening then Ctrl+Alt+F9 | verified | objects absent at opening (0 live objects), a re-entered dependency gives #REF!, CalculateFull rebuilds the objects then their users |
| Multithreaded recalculation | verified | 12,000 thread-safe cells, identical results on 1 and 24 threads (90 ms against 18 ms) |
QF.LASTERROR |
verified | QF.BS.PRICE: S: 'abc' is not a number (#VALUE!) |
Errors (#VALUE!, #NUM!, #REF!, propagation) |
verified | compared by CVErr code, Excel being localized (#VALEUR!, #NOMBRE!) |
Re-verification with Hybridizer 7.6.3 and the open-source branch
Run on 2026-10-17 on the same machine (RTX 4070 Laptop, driver 610.74, CUDA 13.0; Windows 11 build 26300; .NET SDK
8.0.425; Excel 16.0.20430.20146, 64-bit), on branch epic/11-open-source (505d702), Hybridizer tool updated from 7.5.1
to 7.6.3.
| Point | Result |
|---|---|
dotnet build QuantFinance.sln -c Release |
0 warnings, 0 errors (warnings are errors) |
CUDA generation, -p:HybridizeCuda=true, tool 7.6.3 |
succeeded; 3 nvcc warnings #20199-D unrecognized #pragma from the CUDA 13.0 curand_poisson.h header, not errors |
dotnet test with the satellite |
0 failed across 15 assemblies, about 1584 passed, 10 skipped (same 10 as before: MKL, AVX/OMP flavours, Longstaff-Schwartz on the device) |
Cuda_* coherence tests |
10 passed, 2 skipped (Longstaff-Schwartz) |
| Longstaff-Schwartz on the device, skip removed | still fails with Could not get function pointer for method Void applyRegressor(alignedindex); DelegateSupport="false" in all 11 generated configurations: the free-edition limitation is unchanged in 7.6.3 |
samples/04-MonteCarloVaR, 1M scenarios |
CPU (24 threads) 234 ms; GPU 46 ms end to end, ×5.1; same VaR 99% (30,454) and ES 99% (34,502); max |GPU − CPU| P&L 2.29e-10 (3.3e-15 relative) |
Excel smoke test, functions and the five samples/excel workbooks (-SampleWorkbooks) |
70 checks, 0 failed after one fix to the script (below) |
Excel details: every workbook recalculates with no error cell, so the QF.* names stored without the _xll. prefix
resolve correctly; the legacy array formulas fill; QF.MCVAR shows #N/A then its value (within 2% of delta-normal);
each About sheet reports the add-in loaded; the charts of workbooks 01–03 render (exported to PNG and inspected).
Fixed during this run: the smoke script failed one check with RPC_E_CALL_REJECTED (0x80010001) while polling the
asynchronous QF.MCVAR, because Excel rejects COM calls while it is busy. Invoke-ComRetry now retries those calls for
up to 60 s. Cosmetic: the smile charts of workbook 02 now start the strike axis at the lowest quoted strike instead of 0.
Remaining problems
- Longstaff-Schwartz on GPU: blocked by the free edition of Hybridizer (no delegate support). Rework into functors to be decided.
Hybridizer.Runtime.CUDAImports3.7.1 runtime: data loss on chained launches and error 700 on concurrent read. Worked around in the tests; any application code that chains launches or reads results in parallel must first refresh the host on a single thread. To be reported to the vendor.- AVX and OMP flavors unavailable in the free edition:
AmericanCall_AVX,AmericanCall_OMP,TestMethod_AmericanPut_LSPaper_AVXandTestMethod_MRG32K3A_VsOMPremain skipped. - MKL absent:
TestMethod_MRG32K3A_MKL,TestMethod_MRG32K3A_MKL_GET_NAN,AmericanCall_CSharpandAmericanCall_CUDAremain skipped (the last one would be blocked by point 1 anyway). - Satellite tied to the build GPU's architecture (nvrtc
autocubin): a multi-architecture satellite would require setting--gpu-architectureper architecture, not tested. - Not tested: the Linux branch of the MSBuild targets; other GPUs; Excel 2016/2019;
Dual/Dual4executed on device (no entry point);FixedCashFlowsPricerOptimized_*(out of scope). double.IsNegativeInfinityis still mistranslated by the shipped builtins: to be fixed inquantfinance.cuda.builtinsbefore any use in device code.