Main baseline — 2026-10-16

Measured compiled source: b96e455972b937641cec52fc7fc86e75a058f07c. These measurements precede this performance-optimization round. They are not measurements of subsequent optimizations or every later checkout. The report commit follows the measured revision. Sampling ran on 2026-10-16 from 00:17 to 00:28 Asia/Taipei (2026-10-15 16:17–16:28 UTC), with frozen packages, no concurrent project builds/tests/profilers, and no tracked source changes. The two pre-existing untracked research files were preserved. No failed runs were retried or discarded.

All 680 candidate/case results, across 26 configurations × two fresh processes, passed independent stable CPU true-residual checks before and after timing. All 110 table rows are retained below, including losses and neutral preconditioning cases. Exact inputs are represented by recorded byte hashes.

The CPU release native dense median is lower than NumPy in 6/16 rows and eager Torch in 8/16. Explicit CPU BLAS is lower than NumPy in 15/16 and Torch in 16/16; its NumPy loss is Jacobi prepared n=2048: 3.796 versus 3.686 ms (1.030×), with overlapping process ranges. Native CPU CSR is lower than both references in all 16 rows. These are within-batch comparisons: the separate CPU BLAS batch also retains its native candidate and reference timings.

Native CUDA medians are lower than eager Torch in every listed dense, CSR and scaled/neutral row. Explicit cuBLAS wins 12/16 dense comparisons; its four Jacobi API losses at n=64/256/1024/2048 are 1.290/1.425/1.503/1.460× slower than eager Torch. This is completed resident-solve wall time, including API resource lifecycle where selected. No isolated phase or bottleneck is inferred.

On the four scaled sparse GPU inputs, Jacobi reduces native updates from 575/1498/261/444 to 48/115/25/37. Native CG/PCG median ratios are 7.12–10.11× prepared and 7.93–10.28× API. The neutral input stays at 50 updates; PCG is 1.23× slower prepared and 1.40× slower API. Unpreconditioned native and Torch GPU counts differ on two scaled inputs, explicitly retained below. CPU LU reuse has a lower median than native PCG on all five strategy fixtures in both scopes; its prior factorization is excluded. No universal ranking or equal-error claim follows from these finite fixtures.

Descriptive median wins and reference/candidate ratios

Build / suite

candidate

reference

lower median rows

ratio range

cpu-release / cpu-dense-f64-v1

native

numpy-out

6/16

0.60–5.88×

cpu-release / cpu-dense-f64-v1

native

torch-eager-out

8/16

0.66–8.82×

cpu-blas / cpu-dense-f64-v1

native

numpy-out

8/16

0.59–5.94×

cpu-blas / cpu-dense-f64-v1

native

torch-eager-out

8/16

0.62–8.79×

cpu-blas / cpu-dense-f64-v1

native-blas

numpy-out

15/16

0.97–9.61×

cpu-blas / cpu-dense-f64-v1

native-blas

torch-eager-out

16/16

1.05–14.21×

cpu-release / cpu-csr-f64-v1

native

scipy-out

16/16

1.64–13.56×

cpu-release / cpu-csr-f64-v1

native

torch-eager-out

16/16

1.18–18.26×

cuda-release / cuda-dense-f64-v1

cuda-native

torch-cuda-eager

16/16

1.19–2.05×

cuda-release / cuda-dense-f64-v1

cuda-cublas

torch-cuda-eager

12/16

0.67–1.99×

cuda-release / cuda-csr-f64-v1

cuda-native

torch-cuda-eager

16/16

1.67–2.12×

cuda-release / sparse-solvers

cuda-native

torch-cuda-eager

20/20

1.77–1.94×

Ratios above 1 favor the candidate. Counts use medians only; process ranges and numerical work are retained in the full tables below. They are not confidence statements.

Hardware, builds and sampling

Host: 13th Gen Intel(R) Core(TM) i7-13700K; Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39; CPython 3.12.3. GPU: NVIDIA GeForce RTX 2060, compute capability 7.5, device 0, 6,442,123,264 reported bytes; UUID GPU-725be96d-2b67-c8ec-fa10-5d9b23a2c302. NVIDIA display driver 591.86; CUDA toolkit/compiler 12.8.93, runtime API 12080, driver API 13010, architecture 75. GPU clocks, thermal/display activity and physical-core isolation from the Windows host were not controlled.

All runs use taskset -c 0, one native/library/Torch intraop thread and one Torch interop thread. CPU builds use GNU 16.0.1; CUDA uses GNU 13.3.0. All are Release -O3 -DNDEBUG with OpenMP disabled. CPU BLAS is the private sequential LP64 OpenBLAS 0.3.30 provider with locking. No automatic kernel selection is used. NumPy 2.2.6, SciPy 1.15.3 and Torch 2.8.0+cpu / 2.8.0+cu128 use separate project-local CPU/CUDA environments.

Every configuration uses seed 20261015 and three warmups. Implementation candidates use standard CG, fused native operations, rtol=1e-8, atol=1e-12, maxiter=2000 and true-residual refresh/restart every 50 updates. Dense repeats three solves per round; CSR repeats one. Thirty requested Williams rounds remain 30 for two, three or six candidates; the four-candidate BLAS configuration completes 32 rounds. Pass one executes the configurations forward and pass two reverses them.

Each cell is the median of the two process medians, in milliseconds, followed by [minimum–maximum process median]. Each process median uses round-average completed wall times. The bracket is neither a confidence interval nor individual-call tail latency. Two launches are limited evidence of process variance; no statistical significance or universal speedup is claimed.

Native and matching-policy prepared candidates reuse output/workspace/Jacobi and charge every x0 reset. API charges per-call output/workspace/Jacobi allocation and setup. Official SciPy CG allocates iterative workspace in both scopes and follows its own residual policy. CUDA inputs are resident, with completion fences; uploads/snapshots, canonical CSR conversion and correctness downloads are excluded. Torch uses eager public operations implementing this project’s matching-policy CG, not an official Torch CG solver, torch.compile or CUDA Graphs. SuperLU total/reuse and official SciPy CG/PCG remain strategy comparisons with their own stopping and allocation policies. LU reuse excludes factorization in both scopes. Do not subtract separately measured API/prepared medians to isolate a phase. See Methodology and reproduction for the full timing, storage and accuracy contracts.

The work column gives iteration/matvec counts. A single value is shared by all candidates and both processes; otherwise labels identify the differences. N=native, B=BLAS/cuBLAS, NP=NumPy, S=SciPy-out, T=Torch; NCG/NPCG and SCG/SPCG denote native and official SciPy CG/PCG; LUT/LUR denote LU total/reuse. max R/tol is the largest independent residual/threshold ratio among that row’s candidates and both processes. Different counts mean different numerical work despite shared recurrence; those are times to accepted solutions, not equal-work kernel speedups.

CPU release — dense

Completed solve milliseconds; process-median range

M

scope

case

N

NP

T

iter/matvec

max R/tol

none

prepared

dense-64

0.0348 [0.0346–0.0349]

0.2044 [0.2016–0.2073]

0.3065 [0.2931–0.3200]

49/51

0.893697

none

prepared

dense-256

0.5417 [0.5326–0.5509]

0.5415 [0.4920–0.5910]

0.6563 [0.6549–0.6577]

67/70

0.902831

none

prepared

dense-1024

9.9177 [9.5123–10.3231]

7.5079 [7.1157–7.9001]

7.8991 [7.7873–8.0108]

70/73

0.880853

none

prepared

dense-2048

86.8585 [82.3764–91.3406]

51.7299 [50.4608–52.9989]

57.6699 [55.5751–59.7647]

70/73

0.897447

none

api

dense-64

0.0383 [0.0373–0.0392]

0.1846 [0.1845–0.1847]

0.2809 [0.2777–0.2840]

49/51

0.893697

none

api

dense-256

0.5583 [0.5538–0.5627]

0.5936 [0.5830–0.6042]

0.7558 [0.7365–0.7750]

67/70

0.902831

none

api

dense-1024

9.3471 [9.2502–9.4441]

7.0990 [7.0093–7.1886]

7.4344 [7.3291–7.5396]

70/73

0.880853

none

api

dense-2048

77.5117 [75.1834–79.8399]

51.1813 [51.1473–51.2154]

52.8660 [51.6184–54.1136]

70/73

0.897447

jacobi

prepared

dense-64

0.0072 [0.0071–0.0072]

0.0280 [0.0275–0.0285]

0.0464 [0.0454–0.0473]

5/7

0.041680

jacobi

prepared

dense-256

0.0523 [0.0514–0.0532]

0.0515 [0.0455–0.0575]

0.0666 [0.0642–0.0690]

4/6

0.173276

jacobi

prepared

dense-1024

0.9633 [0.9244–1.0023]

0.7181 [0.7107–0.7255]

0.7726 [0.7705–0.7746]

4/6

0.015961

jacobi

prepared

dense-2048

5.8248 [5.5941–6.0555]

3.6703 [3.5553–3.7853]

4.1525 [4.1163–4.1886]

3/5

0.844587

jacobi

api

dense-64

0.0133 [0.0132–0.0133]

0.0386 [0.0379–0.0392]

0.0678 [0.0673–0.0683]

5/7

0.041680

jacobi

api

dense-256

0.0591 [0.0581–0.0602]

0.0619 [0.0582–0.0657]

0.0974 [0.0973–0.0976]

4/6

0.173276

jacobi

api

dense-1024

0.8269 [0.7838–0.8701]

0.7099 [0.6216–0.7982]

0.7914 [0.7096–0.8733]

4/6

0.015961

jacobi

api

dense-2048

5.4705 [5.4010–5.5399]

3.5940 [3.5573–3.6307]

3.8331 [3.7269–3.9393]

3/5

0.844587

CPU release — CSR

The Poisson diagonal is constant 4: Jacobi is scalar scaling and has no conditioning benefit on this suite.

Completed solve milliseconds; process-median range

M

scope

case

N

S

T

iter/matvec

max R/tol

none

prepared

poisson-8

0.0109 [0.0103–0.0115]

0.1462 [0.1379–0.1544]

0.1852 [0.1768–0.1935]

26/28

0.658698

none

prepared

poisson-32

0.3608 [0.3465–0.3751]

1.2358 [1.2066–1.2650]

1.2070 [1.1681–1.2460]

129/133

0.971015

none

prepared

poisson-64

3.4757 [2.9440–4.0074]

5.7123 [5.5182–5.9064]

4.7035 [4.5581–4.8489]

290/297

0.986788

none

prepared

poisson-128

14.6987 [14.0417–15.3558]

25.7277 [24.9229–26.5325]

17.9967 [16.9750–19.0183]

395/404

0.984698

none

api

poisson-8

0.0195 [0.0180–0.0210]

0.1543 [0.1522–0.1564]

0.2051 [0.1925–0.2177]

26/28

0.658698

none

api

poisson-32

0.3629 [0.3468–0.3790]

1.2436 [1.2031–1.2842]

1.2076 [1.1854–1.2297]

129/133

0.971015

none

api

poisson-64

2.8609 [2.7056–3.0162]

5.7804 [5.5080–6.0528]

4.7130 [4.4859–4.9400]

290/297

0.986788

none

api

poisson-128

14.7477 [14.2527–15.2427]

27.0862 [25.7181–28.4544]

17.9088 [17.6215–18.1960]

395/404

0.984698

jacobi

prepared

poisson-8

0.0115 [0.0114–0.0116]

0.1554 [0.1539–0.1569]

0.2093 [0.2038–0.2148]

26/28

0.658698

jacobi

prepared

poisson-32

0.3974 [0.3930–0.4018]

1.2740 [1.2559–1.2921]

1.3185 [1.2973–1.3396]

129/133

0.971015

jacobi

prepared

poisson-64

3.1014 [3.0706–3.1323]

6.1046 [6.0240–6.1853]

5.1368 [5.1015–5.1722]

290/297

0.986788

jacobi

prepared

poisson-128

16.4049 [16.1433–16.6666]

28.9058 [28.5347–29.2768]

19.5903 [19.2421–19.9386]

395/404

0.984698

jacobi

api

poisson-8

0.0221 [0.0189–0.0254]

0.1773 [0.1627–0.1918]

0.2418 [0.2364–0.2472]

26/28

0.658698

jacobi

api

poisson-32

0.4062 [0.3950–0.4174]

1.2871 [1.2102–1.3640]

1.3363 [1.3010–1.3716]

129/133

0.971015

jacobi

api

poisson-64

3.1817 [3.1413–3.2221]

6.0879 [5.9875–6.1883]

5.3103 [5.2219–5.3987]

290/297

0.986788

jacobi

api

poisson-128

16.9179 [16.3216–17.5142]

29.1480 [28.3800–29.9159]

19.9285 [19.2725–20.5845]

395/404

0.984698

CPU BLAS build — dense

Completed solve milliseconds; process-median range

M

scope

case

N

B

NP

T

iter/matvec

max R/tol

none

prepared

dense-64

0.0324 [0.0318–0.0330]

0.0200 [0.0197–0.0204]

0.1926 [0.1874–0.1977]

0.2848 [0.2750–0.2945]

49/51

0.893697

none

prepared

dense-256

0.5408 [0.5293–0.5522]

0.2441 [0.2396–0.2485]

0.5787 [0.5629–0.5945]

0.6219 [0.6017–0.6421]

67/70

0.949804

none

prepared

dense-1024

9.9177 [9.8546–9.9808]

7.4851 [7.1146–7.8556]

7.6477 [7.3467–7.9487]

8.6396 [8.3152–8.9640]

70/73

0.880853

none

prepared

dense-2048

86.6400 [81.1186–92.1615]

50.6597 [48.1160–53.2034]

51.4872 [50.6147–52.3598]

53.2960 [52.7088–53.8832]

70/73

0.897447

none

api

dense-64

0.0415 [0.0360–0.0470]

0.0299 [0.0258–0.0340]

0.2239 [0.1883–0.2596]

0.3425 [0.2832–0.4018]

49/51

0.893697

none

api

dense-256

0.6019 [0.5421–0.6617]

0.3396 [0.3249–0.3543]

0.6288 [0.5699–0.6876]

0.8851 [0.7240–1.0463]

67/70

0.949804

none

api

dense-1024

10.3480 [9.5486–11.1474]

7.1999 [6.5284–7.8715]

8.1586 [7.2495–9.0678]

9.1273 [8.0416–10.2130]

70/73

0.880853

none

api

dense-2048

80.5515 [80.2618–80.8412]

49.3381 [49.0968–49.5794]

51.9802 [51.6551–52.3052]

54.1210 [53.4930–54.7490]

70/73

0.897447

jacobi

prepared

dense-64

0.0095 [0.0072–0.0118]

0.0065 [0.0058–0.0072]

0.0412 [0.0314–0.0511]

0.0693 [0.0520–0.0865]

5/7

0.041680

jacobi

prepared

dense-256

0.0577 [0.0510–0.0644]

0.0300 [0.0264–0.0336]

0.0709 [0.0562–0.0855]

0.0874 [0.0650–0.1098]

4/6

0.173276

jacobi

prepared

dense-1024

1.1201 [0.9901–1.2501]

0.8716 [0.7278–1.0153]

0.8990 [0.6920–1.1060]

1.0168 [0.7999–1.2336]

4/6

0.015961

jacobi

prepared

dense-2048

5.8228 [5.6824–5.9632]

3.7961 [3.6323–3.9600]

3.6861 [3.6625–3.7096]

4.0211 [3.9337–4.1084]

3/5

0.844587

jacobi

api

dense-64

0.0127 [0.0125–0.0129]

0.0121 [0.0117–0.0125]

0.0405 [0.0399–0.0410]

0.0720 [0.0691–0.0749]

5/7

0.041680

jacobi

api

dense-256

0.0546 [0.0542–0.0551]

0.0405 [0.0403–0.0406]

0.0641 [0.0633–0.0649]

0.0940 [0.0923–0.0957]

4/6

0.173276

jacobi

api

dense-1024

0.8088 [0.7933–0.8243]

0.5734 [0.5664–0.5804]

0.6136 [0.6132–0.6140]

0.7255 [0.7161–0.7349]

4/6

0.015961

jacobi

api

dense-2048

5.3566 [5.3213–5.3919]

3.3971 [3.3703–3.4238]

3.5550 [3.5281–3.5818]

3.8341 [3.7533–3.9150]

3/5

0.844587

CUDA — dense

Completed solve milliseconds; process-median range

M

scope

case

N

B

T

iter/matvec

max R/tol

none

prepared

dense-64

5.0959 [4.8704–5.3214]

5.1538 [5.0045–5.3032]

10.2439 [9.9542–10.5337]

49/51

0.805850

none

prepared

dense-256

7.3061 [7.2872–7.3249]

7.1443 [7.1171–7.1716]

13.7016 [13.6421–13.7611]

67/70

0.944865

none

prepared

dense-1024

10.4540 [10.4142–10.4939]

9.0494 [8.9806–9.1183]

15.0417 [14.8594–15.2240]

70/73

0.880853

none

prepared

dense-2048

16.6067 [16.5648–16.6486]

15.1287 [15.0309–15.2264]

20.8693 [20.8239–20.9148]

70/73

0.897447

none

api

dense-64

5.2027 [5.0604–5.3450]

6.9780 [6.9468–7.0093]

10.3494 [10.3067–10.3921]

49/51

0.805850

none

api

dense-256

7.3970 [7.2684–7.5256]

8.7663 [8.5465–8.9861]

13.6243 [13.1214–14.1273]

67/70

0.944865

none

api

dense-1024

10.7520 [10.1117–11.3924]

10.8072 [10.1954–11.4189]

15.3794 [14.5124–16.2464]

70/73

0.880853

none

api

dense-2048

16.1070 [15.9303–16.2837]

16.3415 [16.0699–16.6131]

20.2376 [20.0411–20.4341]

70/73

0.897447

jacobi

prepared

dense-64

0.8195 [0.8009–0.8381]

0.8608 [0.8448–0.8767]

1.6766 [1.6431–1.7101]

5/7

0.041680

jacobi

prepared

dense-256

0.7988 [0.7820–0.8155]

0.7858 [0.7796–0.7919]

1.4095 [1.3811–1.4379]

4/6

0.173276

jacobi

prepared

dense-1024

1.0930 [1.0642–1.1218]

1.0043 [0.9736–1.0351]

1.4700 [1.4312–1.5088]

4/6

0.015961

jacobi

prepared

dense-2048

1.3815 [1.3792–1.3837]

1.2457 [1.2083–1.2832]

1.6421 [1.6346–1.6496]

3/5

0.844587

jacobi

api

dense-64

0.9993 [0.9424–1.0563]

2.5904 [2.5007–2.6801]

2.0088 [1.9398–2.0779]

5/7

0.041680

jacobi

api

dense-256

0.9076 [0.8881–0.9271]

2.5007 [2.4585–2.5428]

1.7554 [1.7159–1.7950]

4/6

0.173276

jacobi

api

dense-1024

1.2038 [1.1737–1.2339]

2.7209 [2.6952–2.7467]

1.8108 [1.7864–1.8351]

4/6

0.015961

jacobi

api

dense-2048

1.4431 [1.4407–1.4456]

2.7925 [2.7217–2.8632]

1.9124 [1.8498–1.9750]

3/5

0.844587

CUDA — constant-diagonal CSR

The Poisson diagonal is constant 4: Jacobi is scalar scaling and has no conditioning benefit on this suite.

Completed solve milliseconds; process-median range

M

scope

case

N

T

iter/matvec

max R/tol

none

prepared

poisson-8

2.6263 [2.3094–2.9433]

5.5649 [4.9008–6.2291]

26/28

0.658698

none

prepared

poisson-32

13.6881 [13.4163–13.9600]

27.1583 [24.6738–29.6428]

129/133

0.971015

none

prepared

poisson-64

30.2525 [30.2094–30.2956]

57.7702 [57.6785–57.8619]

290/297

0.986788

none

prepared

poisson-128

46.2655 [44.4136–48.1174]

83.3017 [80.0833–86.5201]

395/404

0.984698

none

api

poisson-8

2.9891 [2.7624–3.2158]

5.8818 [5.0752–6.6884]

26/28

0.658698

none

api

poisson-32

13.8221 [13.2523–14.3918]

26.3923 [24.9435–27.8411]

129/133

0.971015

none

api

poisson-64

31.2799 [31.1208–31.4390]

59.6415 [58.2887–60.9943]

290/297

0.986788

none

api

poisson-128

48.3587 [48.2708–48.4466]

87.2058 [80.6580–93.7536]

395/404

0.984698

jacobi

prepared

poisson-8

4.6279 [4.1221–5.1336]

8.7218 [7.2471–10.1965]

26/28

0.658698

jacobi

prepared

poisson-32

20.6805 [20.3843–20.9766]

37.7189 [36.1291–39.3087]

129/133

0.971015

jacobi

prepared

poisson-64

50.4811 [45.8506–55.1115]

93.0594 [84.4538–101.6649]

290/297

0.986788

jacobi

prepared

poisson-128

73.5206 [65.9821–81.0591]

135.4548 [120.2361–150.6736]

395/404

0.984698

jacobi

api

poisson-8

4.3500 [3.9496–4.7505]

8.2950 [7.1960–9.3940]

26/28

0.658698

jacobi

api

poisson-32

20.4547 [18.8469–22.0625]

37.3356 [34.0327–40.6386]

129/133

0.971015

jacobi

api

poisson-64

49.8249 [42.9101–56.7397]

89.3411 [77.5660–101.1162]

290/297

0.986788

jacobi

api

poisson-128

69.8293 [67.4848–72.1737]

116.7591 [116.4921–117.0261]

395/404

0.984698

CUDA — scaled sparse and neutral input

Scaled inputs use A = D P D, D spanning 1..20, with one neutral input. The CG count differences for scaled 2D-16 and 3D-6 are numerical work differences; those rows compare time to an accepted solution.

Completed solve milliseconds; process-median range

M

scope

case

N

T

iter/matvec

max R/tol

none

prepared

scaled-poisson-2d-16

59.2093 [58.2447–60.1740]

109.1776 [106.9429–111.4124]

N:575/588; T:574/587

0.949605

none

prepared

scaled-poisson-2d-32

183.8893 [177.5090–190.2697]

336.8948 [325.3254–348.4641]

1498/1529

0.984252

none

prepared

scaled-poisson-3d-6

28.7142 [28.4786–28.9497]

51.4626 [50.6978–52.2275]

N:261/268; T:249/255

0.998645

none

prepared

scaled-poisson-3d-10

54.0099 [51.1716–56.8483]

97.0283 [89.8480–104.2087]

444/454

0.974858

none

prepared

poisson-2d-16-neutral

6.2516 [6.0873–6.4159]

11.2043 [10.2099–12.1988]

50/52

0.764204

none

api

scaled-poisson-2d-16

63.9252 [59.3216–68.5288]

122.8429 [111.1805–134.5054]

N:575/588; T:574/587

0.949605

none

api

scaled-poisson-2d-32

187.8049 [186.0416–189.5683]

341.1451 [337.6237–344.6665]

1498/1529

0.984252

none

api

scaled-poisson-3d-6

32.4331 [30.0606–34.8056]

58.2484 [54.7601–61.7367]

N:261/268; T:249/255

0.998645

none

api

scaled-poisson-3d-10

58.1063 [55.3724–60.8402]

102.9425 [95.8435–110.0416]

444/454

0.974858

none

api

poisson-2d-16-neutral

5.6826 [5.2162–6.1490]

10.4019 [9.5777–11.2262]

50/52

0.764204

jacobi

prepared

scaled-poisson-2d-16

7.4533 [7.4226–7.4841]

13.8046 [13.7228–13.8865]

48/50

0.911256

jacobi

prepared

scaled-poisson-2d-32

18.1834 [17.2321–19.1346]

34.5713 [32.3253–36.8173]

115/119

0.929037

jacobi

prepared

scaled-poisson-3d-6

4.0314 [3.7905–4.2724]

7.8223 [6.9565–8.6880]

25/27

0.439964

jacobi

prepared

scaled-poisson-3d-10

5.8132 [5.5081–6.1184]

10.7880 [10.1761–11.3998]

37/39

0.969066

jacobi

prepared

poisson-2d-16-neutral

7.6916 [7.5810–7.8023]

14.6514 [14.3339–14.9690]

50/52

0.764204

jacobi

api

scaled-poisson-2d-16

7.4695 [7.4256–7.5135]

14.1439 [13.9923–14.2956]

48/50

0.911256

jacobi

api

scaled-poisson-2d-32

18.2639 [18.2084–18.3194]

34.5085 [33.8132–35.2037]

115/119

0.929037

jacobi

api

scaled-poisson-3d-6

4.0898 [4.0792–4.1004]

7.7523 [7.7342–7.7705]

25/27

0.439964

jacobi

api

scaled-poisson-3d-10

5.9165 [5.9125–5.9204]

10.8291 [10.7791–10.8791]

37/39

0.969066

jacobi

api

poisson-2d-16-neutral

7.9763 [7.9644–7.9882]

15.1801 [14.6499–15.7103]

50/52

0.764204

CPU — official sparse solver strategies

Scaled cases use A = D P D with positive D spanning 1..20, plus a constant-diagonal neutral case. These parameters are not exact condition numbers. Official SciPy and direct strategies can have different counts/accepted errors; LU can achieve substantially tighter residuals.

For scaled-poisson-2d-32 prepared, the first process records native CG/PCG residuals 1.45605e-04 / 1.36862e-04 versus LU’s 3.44175e-12, all below the requested 1.47316e-04 threshold. This is not an equal-error comparison.

Completed solve milliseconds; process-median range

M

scope

case

NCG

NPCG

SCG

SPCG

LUT

LUR

iter/matvec

max R/tol

by candidate

prepared

scaled-poisson-2d-16

0.4002 [0.4001–0.4003]

0.0507 [0.0504–0.0510]

2.2235 [2.2215–2.2255]

0.4308 [0.4266–0.4350]

0.3302 [0.3205–0.3398]

0.0273 [0.0272–0.0273]

NCG:555/568; NPCG:48/50; SCG:282/283; SPCG:48/49; LUT:0/1; LUR:0/1

0.980265

by candidate

prepared

scaled-poisson-2d-32

3.7953 [3.7729–3.8176]

0.3563 [0.3495–0.3632]

6.5297 [6.5050–6.5544]

1.1269 [1.1094–1.1443]

1.3454 [1.3265–1.3644]

0.0951 [0.0934–0.0968]

NCG:1498/1529; NPCG:115/119; SCG:609/610; SPCG:92/93; LUT:0/1; LUR:0/1

0.988386

by candidate

prepared

scaled-poisson-3d-6

0.1811 [0.1787–0.1834]

0.0305 [0.0301–0.0308]

1.4851 [1.4640–1.5062]

0.2522 [0.2510–0.2535]

0.3625 [0.3584–0.3666]

0.0259 [0.0256–0.0262]

NCG:252/259; NPCG:25/27; SCG:185/186; SPCG:25/26; LUT:0/1; LUR:0/1

0.978981

by candidate

prepared

scaled-poisson-3d-10

1.3623 [1.3537–1.3708]

0.1467 [0.1465–0.1469]

3.3281 [3.2774–3.3789]

0.4888 [0.4842–0.4934]

3.0616 [3.0123–3.1109]

0.1295 [0.1226–0.1365]

NCG:444/454; NPCG:37/39; SCG:304/305; SPCG:37/38; LUT:0/1; LUR:0/1

0.974337

by candidate

prepared

poisson-2d-16-neutral

0.0416 [0.0411–0.0422]

0.0477 [0.0473–0.0480]

0.4100 [0.4039–0.4161]

0.4283 [0.4106–0.4461]

0.2584 [0.2547–0.2621]

0.0209 [0.0203–0.0214]

NCG:50/52; NPCG:50/52; SCG:50/51; SPCG:50/51; LUT:0/1; LUR:0/1

0.764204

by candidate

api

scaled-poisson-2d-16

0.4130 [0.4058–0.4202]

0.0626 [0.0620–0.0632]

2.1764 [2.1676–2.1853]

0.4464 [0.4447–0.4481]

0.3088 [0.2891–0.3286]

0.0265 [0.0265–0.0265]

NCG:555/568; NPCG:48/50; SCG:282/283; SPCG:48/49; LUT:0/1; LUR:0/1

0.980265

by candidate

api

scaled-poisson-2d-32

3.7480 [3.7303–3.7657]

0.3682 [0.3657–0.3708]

6.6100 [6.5649–6.6551]

1.1843 [1.1796–1.1890]

1.3198 [1.3136–1.3260]

0.0869 [0.0854–0.0884]

NCG:1498/1529; NPCG:115/119; SCG:609/610; SPCG:92/93; LUT:0/1; LUR:0/1

0.988386

by candidate

api

scaled-poisson-3d-6

0.1935 [0.1920–0.1950]

0.0445 [0.0440–0.0450]

1.4596 [1.4495–1.4698]

0.2760 [0.2720–0.2800]

0.3609 [0.3558–0.3659]

0.0270 [0.0262–0.0278]

NCG:252/259; NPCG:25/27; SCG:185/186; SPCG:25/26; LUT:0/1; LUR:0/1

0.978981

by candidate

api

scaled-poisson-3d-10

1.4048 [1.3930–1.4166]

0.1728 [0.1726–0.1730]

3.3041 [3.2973–3.3109]

0.5306 [0.5266–0.5346]

3.0264 [3.0200–3.0328]

0.1209 [0.1152–0.1266]

NCG:444/454; NPCG:37/39; SCG:304/305; SPCG:37/38; LUT:0/1; LUR:0/1

0.974337

by candidate

api

poisson-2d-16-neutral

0.0511 [0.0511–0.0511]

0.0614 [0.0595–0.0632]

0.4095 [0.4089–0.4102]

0.4431 [0.4367–0.4496]

0.2585 [0.2576–0.2594]

0.0214 [0.0208–0.0219]

NCG:50/52; NPCG:50/52; SCG:50/51; SPCG:50/51; LUT:0/1; LUR:0/1

0.764204

Raw evidence and immutable compiled identity

All raw JSON, logs and actual argv remain local under build/performance-optimization/baseline/raw/. They are not stored in Git. The complete 52-command forward/reverse declaration, environment/option alignment checks, input hashes and per-file SHA-256/bytes are in build/performance-optimization/baseline/manifest.json. Its SHA-256 is:

f23623c1414779ebbbdb949c6366ab220379fec32088df75339f8b95746958ff.

The 52 raw files total 2,655,471 bytes. All 680 results are measured and converged; maximum independent R/tol is 0.998644905381452.

  • cpu-release: _core.cpython-312-x86_64-linux-gnu.so → fd4e3a62e057a70a18ad8dd60656d5fefb51bee559243171d0d84de45516746a.

  • cpu-blas: _core.cpython-312-x86_64-linux-gnu.so → cddcdc72d612d16a3e5f8a02b9339ede818d02ebcb7eac298e0317ebb1c2bb09.

  • cuda-release: _core.cpython-312-x86_64-linux-gnu.so → 7e68ab3075f92801e2c2d53e58405f1cccf4f1dc8d2e3147b65c7159b2ecf853. _cuda.cpython-312-x86_64-linux-gnu.so → 419b4718d7eed0b9544805fe8f89bf4675f29f000794a259a714d4198450454c.

The original staged cgpy/cgbench packages, binaries and CMake caches were copied before sampling to build/performance-optimization/baseline-stages/<preset>/. Each stage’s identity.json records compiled revision b96e455972b937641cec52fc7fc86e75a058f07c and complete staged file hashes. The original capture directly loaded these frozen packages; no Make/rebuild ran inside sampling. Actual loaded module paths/hashes match the stages. Copied caches still name the live checkout: when rerunning a saved binary after source changes, use the immutable compiled revision rather than the live checkout HEAD. Rebuilds elsewhere need not have byte-identical binaries.

Reproduce

Use a fresh checkout, CPython 3.12, a compatible NVIDIA driver and existing GNU C++20 toolchains. These commands use project-local dependencies and toolkit. Choose compatible architecture/affinity/compiler values on another host; keep that changed environment visible in its JSON.

git clone https://github.com/ThreeMonth03/Conjugate-Gradient-Method-library.git cgpy-main-baseline
cd cgpy-main-baseline
git checkout --detach b96e455972b937641cec52fc7fc86e75a058f07c
python3.12 -m venv .venv
.venv/bin/python -m pip install --cache-dir .cache/pip -r requirements/benchmark.lock
python3.12 -m venv .venv-cuda
.venv-cuda/bin/python -m pip install --cache-dir .cache/pip -r requirements/cuda-benchmark.lock
.venv-cuda/bin/python scripts/setup_cuda.py --prefix .deps/cuda-12.8
make build PRESET=cpu-release JOBS=2
make build PRESET=cpu-blas JOBS=2
make build PRESET=cuda-release PYTHON="$PWD/.venv-cuda/bin/python" JOBS=2 \
    CUDA_HOST_COMPILER=/usr/bin/g++-13 CUDA_ARCHITECTURES=75

The following public make pyprof commands reproduce all 52 fixed profiles without requiring the local collection driver. They rebuild as necessary before launching each worker; use the fixed checkout and keep the measurement host idle. The original collection used the equivalent frozen harness/spec without per-profile rebuilds. The loop stops on a failed command. Inspect every saved candidate status, true residual, count, input hash and binary identity before using a performance comparison.

from pathlib import Path
import json
import math
import subprocess
import sys

root = Path.cwd()
groups = [
    ("cpu-release", "cpu-dense-f64-v1", "native,numpy-out,torch-eager-out", "cpu", 3),
    ("cpu-release", "cpu-csr-f64-v1", "native,scipy-out,torch-eager-out", "cpu", 1),
    ("cpu-blas", "cpu-dense-f64-v1", "native,native-blas,numpy-out,torch-eager-out", "cpu", 3),
    ("cuda-release", "cuda-dense-f64-v1", "cuda-native,cuda-cublas,torch-cuda-eager", "cuda", 3),
    ("cuda-release", "cuda-csr-f64-v1", "cuda-native,torch-cuda-eager", "cuda", 1),
    ("cuda-release", "sparse-solvers", "cuda-native,torch-cuda-eager", "cuda", 1),
    ("cpu-release", "sparse-solvers", "native-cg,native-pcg,scipy-cg,scipy-pcg,scipy-lu-total,scipy-lu-reuse", "cpu", 1),
]
configs = [
    (*group, m, scope)
    for group in groups
    for m in (("none",) if group[0] == "cpu-release" and group[1] == "sparse-solvers" else ("none", "jacobi"))
    for scope in ("prepared", "api")
]
for pass_id in (1, 2):
    for preset, suite, candidates, device, repetitions, m, scope in (configs if pass_id == 1 else reversed(configs)):
        python = root / (".venv-cuda" if device == "cuda" else ".venv") / "bin/python"
        name = f"{preset}-{suite}-{m}-{scope}-r{pass_id:02}"
        output = root / "build/performance-optimization/reproduced" / f"{name}.json"
        flags = (
            f"--implementations {candidates} --seed 20261015 --warmups 3 "
            f"--algorithm standard --fused --preconditioner {m} "
            "--rtol 1e-8 --atol 1e-12 --maxiter 2000 --residual-refresh 50"
        )
        if device == "cuda":
            flags += " --device cuda --cuda-device 0"
        command = [
            "taskset", "-c", "0", "make", "pyprof", f"PRESET={preset}",
            f"PYTHON={python}", "JOBS=2", f"PYPROF_SUITE={suite}",
            "PYPROF_THREADS=1", f"PYPROF_SCOPE={scope}", "PYPROF_ROUNDS=30",
            f"PYPROF_REPETITIONS={repetitions}", f"PYPROF_OUTPUT={output}",
            f"PYPROF_ARGS={flags}",
        ]
        if device == "cuda":
            command += ["CUDA_HOST_COMPILER=/usr/bin/g++-13", "CUDA_ARCHITECTURES=75"]
        subprocess.run(command, check=True)
        artifact = json.loads(output.read_text())
        for case in artifact["cases"]:
            assert {entry["name"] for entry in case["results"]} == set(candidates.split(","))
            for entry in case["results"]:
                assert entry["status"] == "measured" and entry["solver_status"] == "converged"
                assert math.isfinite(entry["true_residual_norm"])
                assert entry["true_residual_norm"] <= entry["residual_threshold"] * (1 + 64 * sys.float_info.epsilon)

Replay a newly generated artifact with its saved fixture hashes, candidate set and actual sampling schedule. The new environment/binary identity is recorded; identical latency is not expected. Replay is a separate validation and is not pooled into baseline timings.

taskset -c 0 env PYTHONPATH="$PWD/build/cuda-release/python" \
    .venv-cuda/bin/python -m cgbench replay \
    build/performance-optimization/reproduced/cuda-release-sparse-solvers-jacobi-prepared-r01.json \
    --output build/performance-optimization/replayed-scaled-pcg.json

For the originally collected evidence on this host, use the corresponding file under build/performance-optimization/baseline/raw/ instead.

For later regression checks, keep both original process files and compare matching new files with scripts/compare_performance.py; its strict input/options/count gates must remain intact. A change in reduction order can legitimately change iterations: disclose those time-to-tolerance cases separately rather than weakening the equal-work gate. Subsequent optimization or profiling measurements belong in separate reports. No automatic crossover or backend mode is inferred. See Methodology and reproduction for shared contracts.

Baseline profiling observations

Separate Nsight Systems 2025.5.2 captures used the same frozen CUDA binary, seed, float64 dense-1024 fixture and Jacobi policy. Each captured 10 calls after three warmups. All before/after independent residual gates passed; each solve performed four updates and six matvecs. These instrumented diagnostics are not part of the 52 framework artifacts or their rankings.

Completed diagnostic wall medians, milliseconds

Kernel

probe / scope

median ms

native

workspace construction + destruction

0.897

cuBLAS

workspace construction + destruction

2.415

native

solve / prepared

1.350

cuBLAS

solve / prepared

1.061

cuBLAS

solve / API

3.670

The native workspace capture recorded 70 cudaMalloc and 70 cudaFree calls, plus 10 cudaMallocHost / cudaFreeHost pairs. The cuBLAS workspace capture recorded 100 / 110 device allocation/free calls and 160 event creation/destruction pairs, including provider internals. This measures real per-call lifecycle; reusing a prepared workspace changes the scope.

In the cuBLAS API solve capture, cudaMallocHost consumed 7.108 ms and cudaFreeHost 3.006 ms across ten calls, with per-call medians 0.583 and 0.265 ms. Together they accounted for 26.3% of recorded CUDA API duration. Their eight-byte storage was used only for scalar readback followed immediately by a stream fence. This motivates testing workspace-owned pageable scalar storage while retaining explicit completion and lifetime guarantees; pageable cudaMemcpyAsync may itself block, as described in the CUDA 12.8.1 API synchronization documentation.

Both prepared captures recorded 170 cudaMemcpyAsync and 190 cudaStreamSynchronize calls for ten solves, including the profiler driver’s completion fences. No allocation/free calls occurred in these prepared ranges. Recorded stream synchronization consumed 72.6% / 75.4% of CUDA API duration for native / cuBLAS. This motivates an explicit device control mode; it does not establish an achievable speedup. CUDA API waiting overlaps GPU work, and these API durations, kernel durations and independently sampled phase medians must not be added or subtracted as a latency decomposition.

Captures and diagnostic JSON are retained under build/performance-optimization/profiles/baseline-*.{json,nsys-rep,sqlite}, with *_cuda_api_sum.csv and *_cuda_gpu_kern_sum.csv exports. Use Benchmarks for the public profiling command. CPU disassembly of the baseline generic GNU 16 -O3 dense kernel shows SSE2 packed double operations; the optimization question is vector reuse and row blocking, not whether the baseline is entirely scalar. No CPU hardware-counter measurement is claimed.