Cleanup baseline — 2026-10-15

The restart-aware baseline provides newer CPU/CUDA measurements, explicit restart observations and profiling diagnostics. This page records the earlier cleanup baseline from 2026-10-15.

Measured compiled source: d34036f5668559612766cb5d8872a41f3097ffdc. These are the baseline measurements before the subsequent conversion/API, ownership and CUB evaluation changes. They are not measurements of every later checkout. The report commit follows the measured revision. All 52 runs were completed before tracked files changed; 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. Full input bytes are represented by recorded hashes.

On these samples, native CPU dense remains slower than NumPy/Torch at larger sizes. The explicit CPU BLAS median beats Torch in all 16 rows and NumPy in 15; unpreconditioned prepared n=2048 loses to NumPy (79.46 versus 75.61 ms), with overlapping process ranges. Native CPU CSR medians are lower than both references in all listed rows.

Native CUDA medians are below eager Torch in every listed row, including dense Jacobi API; the older native API loss is not reproduced at this revision. Explicit cuBLAS still loses all four dense Jacobi API comparisons. These observations identify resource construction as a phase to profile; subtracting API/prepared medians would not establish its isolated cost.

On the four scaled sparse GPU inputs, Jacobi reduces native updates from 575/1498/261/444 to 48/115/25/37, with 7.42–8.72× lower prepared medians. The neutral case stays at 50 updates and adds work. Some unpreconditioned GPU CG counts differ between native and Torch, as explicitly retained below. After prior factorization, CPU LU reuse has lower medians than native PCG on 3/5 prepared and 4/5 API default strategy fixtures. All these rankings are descriptive, with the two-process uncertainty limits given below.

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.0323 [0.0319–0.0326]

0.1860 [0.1853–0.1868]

0.2736 [0.2735–0.2738]

49/51

0.893697

none

prepared

dense-256

0.5733 [0.5714–0.5752]

0.5693 [0.5134–0.6251]

0.6769 [0.6727–0.6810]

67/70

0.902831

none

prepared

dense-1024

10.7700 [10.6922–10.8478]

9.0658 [8.9677–9.1639]

9.6995 [9.5407–9.8583]

70/73

0.880853

none

prepared

dense-2048

122.2988 [116.5445–128.0530]

76.2861 [71.1337–81.4385]

84.6573 [74.1511–95.1635]

70/73

0.897447

none

api

dense-64

0.0414 [0.0408–0.0419]

0.1933 [0.1931–0.1935]

0.2952 [0.2922–0.2982]

49/51

0.893697

none

api

dense-256

0.5826 [0.5642–0.6009]

0.5818 [0.5565–0.6071]

0.7433 [0.7332–0.7535]

67/70

0.902831

none

api

dense-1024

9.8246 [9.7512–9.8981]

7.5968 [7.4708–7.7229]

8.0648 [7.9393–8.1902]

70/73

0.880853

none

api

dense-2048

133.5519 [116.9792–150.1246]

87.7914 [74.1477–101.4352]

94.1355 [83.7485–104.5224]

70/73

0.897447

jacobi

prepared

dense-64

0.0071 [0.0071–0.0072]

0.0286 [0.0285–0.0286]

0.0444 [0.0435–0.0452]

5/7

0.041680

jacobi

prepared

dense-256

0.0522 [0.0521–0.0524]

0.0510 [0.0468–0.0551]

0.0654 [0.0643–0.0666]

4/6

0.173276

jacobi

prepared

dense-1024

1.0910 [1.0868–1.0952]

0.9460 [0.8960–0.9959]

1.0780 [1.0612–1.0947]

4/6

0.015961

jacobi

prepared

dense-2048

8.7216 [7.7434–9.6999]

6.4917 [5.4934–7.4901]

6.6167 [5.7315–7.5019]

3/5

0.844587

jacobi

api

dense-64

0.0156 [0.0150–0.0162]

0.0423 [0.0390–0.0456]

0.0715 [0.0681–0.0749]

5/7

0.041680

jacobi

api

dense-256

0.0578 [0.0561–0.0594]

0.0646 [0.0637–0.0654]

0.0969 [0.0960–0.0978]

4/6

0.173276

jacobi

api

dense-1024

0.8833 [0.8463–0.9203]

0.7713 [0.7200–0.8227]

0.8919 [0.8670–0.9168]

4/6

0.015961

jacobi

api

dense-2048

11.2840 [9.4532–13.1149]

7.5827 [6.4733–8.6920]

8.6846 [7.0566–10.3125]

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.0115 [0.0102–0.0128]

0.1575 [0.1322–0.1828]

0.2076 [0.1691–0.2461]

26/28

0.658698

none

prepared

poisson-32

0.3764 [0.3382–0.4147]

1.4021 [1.1457–1.6584]

1.4455 [1.1062–1.7848]

129/133

0.971015

none

prepared

poisson-64

3.0041 [2.7202–3.2880]

6.7633 [5.6183–7.9083]

6.0249 [4.4421–7.6076]

290/297

0.986788

none

prepared

poisson-128

14.9528 [14.8224–15.0833]

26.3708 [26.0029–26.7387]

18.0927 [17.6816–18.5039]

395/404

0.984698

none

api

poisson-8

0.0191 [0.0174–0.0208]

0.1405 [0.1338–0.1472]

0.1839 [0.1759–0.1918]

26/28

0.658698

none

api

poisson-32

0.3732 [0.3690–0.3774]

1.2372 [1.2229–1.2515]

1.2289 [1.2160–1.2419]

129/133

0.971015

none

api

poisson-64

2.8820 [2.8635–2.9006]

5.7967 [5.5908–6.0025]

4.9247 [4.7519–5.0974]

290/297

0.986788

none

api

poisson-128

15.8451 [15.6788–16.0114]

27.7452 [27.2014–28.2889]

18.0600 [17.6772–18.4428]

395/404

0.984698

jacobi

prepared

poisson-8

0.0125 [0.0118–0.0132]

0.1606 [0.1555–0.1658]

0.2188 [0.2116–0.2259]

26/28

0.658698

jacobi

prepared

poisson-32

0.4092 [0.4017–0.4167]

1.3667 [1.3375–1.3958]

1.4121 [1.3559–1.4682]

129/133

0.971015

jacobi

prepared

poisson-64

3.2763 [3.2444–3.3081]

6.4279 [6.4046–6.4511]

5.5259 [5.2834–5.7683]

290/297

0.986788

jacobi

prepared

poisson-128

17.3979 [17.0547–17.7410]

28.6252 [28.3872–28.8633]

20.1415 [19.5747–20.7083]

395/404

0.984698

jacobi

api

poisson-8

0.0240 [0.0217–0.0262]

0.1952 [0.1753–0.2152]

0.2870 [0.2547–0.3193]

26/28

0.658698

jacobi

api

poisson-32

0.4739 [0.4365–0.5114]

1.5453 [1.3556–1.7349]

1.7329 [1.5109–1.9548]

129/133

0.971015

jacobi

api

poisson-64

3.6963 [3.4352–3.9574]

7.4631 [6.4656–8.4607]

6.7202 [5.7233–7.7171]

290/297

0.986788

jacobi

api

poisson-128

17.9828 [17.5230–18.4427]

29.9931 [29.3265–30.6596]

21.1601 [21.1576–21.1625]

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.0344 [0.0342–0.0347]

0.0223 [0.0219–0.0226]

0.2036 [0.2027–0.2045]

0.3083 [0.2988–0.3178]

49/51

0.893697

none

prepared

dense-256

0.5618 [0.5492–0.5745]

0.3224 [0.2774–0.3674]

0.5754 [0.5273–0.6234]

0.6720 [0.6470–0.6970]

67/70

0.949804

none

prepared

dense-1024

10.8858 [10.8637–10.9080]

8.6246 [8.2998–8.9495]

9.5691 [9.4619–9.6763]

9.7125 [9.6011–9.8239]

70/73

0.880853

none

prepared

dense-2048

125.6368 [111.9356–139.3380]

79.4552 [72.3696–86.5407]

75.6052 [69.3558–81.8547]

87.5200 [76.2406–98.7994]

70/73

0.897447

none

api

dense-64

0.0387 [0.0377–0.0396]

0.0275 [0.0274–0.0277]

0.1938 [0.1919–0.1958]

0.2849 [0.2809–0.2890]

49/51

0.893697

none

api

dense-256

0.5435 [0.5378–0.5492]

0.3092 [0.2619–0.3566]

0.5524 [0.5246–0.5802]

0.7044 [0.6611–0.7477]

67/70

0.949804

none

api

dense-1024

9.4791 [9.1492–9.8089]

6.5780 [6.4167–6.7392]

7.1725 [6.9727–7.3723]

7.6103 [7.4793–7.7413]

70/73

0.880853

none

api

dense-2048

124.3239 [123.2999–125.3480]

78.6205 [76.3474–80.8935]

80.2853 [79.4052–81.1654]

81.8049 [81.2807–82.3291]

70/73

0.897447

jacobi

prepared

dense-64

0.0072 [0.0069–0.0074]

0.0058 [0.0056–0.0060]

0.0309 [0.0302–0.0316]

0.0486 [0.0466–0.0505]

5/7

0.041680

jacobi

prepared

dense-256

0.0512 [0.0492–0.0531]

0.0269 [0.0258–0.0280]

0.0572 [0.0551–0.0594]

0.0680 [0.0644–0.0716]

4/6

0.173276

jacobi

prepared

dense-1024

1.1496 [1.1215–1.1778]

0.9352 [0.9118–0.9585]

0.9681 [0.9402–0.9961]

1.0800 [1.0759–1.0842]

4/6

0.015961

jacobi

prepared

dense-2048

9.1624 [8.3778–9.9469]

5.7542 [5.5466–5.9618]

6.5701 [6.0389–7.1014]

6.6466 [6.0483–7.2449]

3/5

0.844587

jacobi

api

dense-64

0.0130 [0.0129–0.0131]

0.0126 [0.0125–0.0127]

0.0409 [0.0405–0.0414]

0.0738 [0.0721–0.0755]

5/7

0.041680

jacobi

api

dense-256

0.0575 [0.0554–0.0596]

0.0375 [0.0341–0.0409]

0.0633 [0.0613–0.0653]

0.0973 [0.0942–0.1004]

4/6

0.173276

jacobi

api

dense-1024

0.8729 [0.8489–0.8969]

0.6624 [0.6376–0.6872]

0.7592 [0.7467–0.7718]

0.8882 [0.8595–0.9170]

4/6

0.015961

jacobi

api

dense-2048

8.4539 [7.6997–9.2080]

5.0473 [4.7342–5.3603]

5.4693 [5.2213–5.7174]

6.3584 [5.2693–7.4476]

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

6.0612 [5.9523–6.1701]

6.2410 [6.0727–6.4094]

12.0772 [11.7963–12.3581]

49/51

0.805850

none

prepared

dense-256

9.2882 [9.0993–9.4771]

9.0463 [8.8700–9.2226]

18.6506 [18.4365–18.8647]

67/70

0.944865

none

prepared

dense-1024

10.9658 [10.4475–11.4841]

10.0428 [9.5056–10.5801]

17.5017 [16.1631–18.8403]

70/73

0.880853

none

prepared

dense-2048

19.9544 [19.8562–20.0525]

18.2762 [17.9892–18.5632]

25.6436 [25.1801–26.1071]

70/73

0.897447

none

api

dense-64

6.6795 [6.3204–7.0386]

8.4697 [8.3047–8.6346]

12.9628 [12.4572–13.4684]

49/51

0.805850

none

api

dense-256

9.0779 [8.8774–9.2784]

11.0377 [10.7450–11.3303]

18.0043 [17.7393–18.2692]

67/70

0.944865

none

api

dense-1024

11.7241 [11.6856–11.7626]

12.4333 [12.4001–12.4666]

17.9361 [17.1552–18.7170]

70/73

0.880853

none

api

dense-2048

19.8588 [19.3429–20.3747]

20.2758 [20.2266–20.3250]

25.3478 [25.0289–25.6667]

70/73

0.897447

jacobi

prepared

dense-64

1.0608 [0.9910–1.1306]

1.0496 [0.9559–1.1432]

2.0045 [1.8566–2.1524]

5/7

0.041680

jacobi

prepared

dense-256

0.8901 [0.7850–0.9951]

0.9478 [0.8182–1.0773]

1.6860 [1.4406–1.9315]

4/6

0.173276

jacobi

prepared

dense-1024

1.2610 [1.0965–1.4254]

1.0804 [1.0031–1.1576]

1.6880 [1.4839–1.8920]

4/6

0.015961

jacobi

prepared

dense-2048

1.4435 [1.3705–1.5165]

1.3054 [1.1914–1.4195]

1.9233 [1.6815–2.1652]

3/5

0.844587

jacobi

api

dense-64

1.1687 [1.1327–1.2046]

3.2810 [3.2184–3.3436]

2.5633 [2.4624–2.6642]

5/7

0.041680

jacobi

api

dense-256

1.0022 [0.9803–1.0240]

3.0166 [2.9945–3.0387]

2.1523 [2.1482–2.1563]

4/6

0.173276

jacobi

api

dense-1024

1.3311 [1.3228–1.3395]

2.9725 [2.9211–3.0238]

2.1146 [2.0652–2.1639]

4/6

0.015961

jacobi

api

dense-2048

1.5593 [1.4566–1.6621]

3.2110 [3.1193–3.3027]

2.3082 [2.2479–2.3684]

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.9719 [2.9612–2.9826]

6.1768 [5.7531–6.6005]

26/28

0.658698

none

prepared

poisson-32

16.2430 [15.9087–16.5774]

31.4184 [30.5676–32.2692]

129/133

0.971015

none

prepared

poisson-64

35.5777 [33.6075–37.5479]

68.2418 [64.5363–71.9474]

290/297

0.986788

none

prepared

poisson-128

50.2899 [48.8504–51.7293]

95.6425 [89.5245–101.7606]

395/404

0.984698

none

api

poisson-8

3.2958 [3.0347–3.5570]

6.1661 [5.3882–6.9440]

26/28

0.658698

none

api

poisson-32

15.5457 [15.1345–15.9569]

30.5565 [28.5029–32.6101]

129/133

0.971015

none

api

poisson-64

35.3437 [32.7193–37.9681]

69.6143 [67.5248–71.7039]

290/297

0.986788

none

api

poisson-128

49.5050 [49.4595–49.5505]

91.4707 [90.3118–92.6296]

395/404

0.984698

jacobi

prepared

poisson-8

4.9214 [4.2910–5.5519]

8.8884 [8.6003–9.1765]

26/28

0.658698

jacobi

prepared

poisson-32

24.0287 [22.6674–25.3899]

43.2289 [39.5559–46.9020]

129/133

0.971015

jacobi

prepared

poisson-64

50.2152 [49.3427–51.0878]

95.5511 [93.1101–97.9922]

290/297

0.986788

jacobi

prepared

poisson-128

73.6294 [72.9193–74.3394]

139.0250 [136.8366–141.2133]

395/404

0.984698

jacobi

api

poisson-8

4.4023 [4.3984–4.4062]

8.4792 [8.4125–8.5458]

26/28

0.658698

jacobi

api

poisson-32

24.7613 [22.9605–26.5621]

45.0635 [42.2730–47.8540]

129/133

0.971015

jacobi

api

poisson-64

51.6640 [47.3984–55.9295]

98.9125 [93.9397–103.8854]

290/297

0.986788

jacobi

api

poisson-128

72.5927 [72.1054–73.0801]

138.8207 [138.5700–139.0713]

395/404

0.984698

CUDA — scaled sparse CG/PCG

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. Native/Torch CG have different counts on two inputs, while PCG counts match; both pass the independent true-residual gate.

Completed solve milliseconds; process-median range

M

scope

case

N

T

iter/matvec

max R/tol

none

prepared

scaled-poisson-2d-16

69.5534 [68.4905–70.6164]

137.2447 [130.1899–144.2994]

N:575/588; T:574/587

0.949605

none

prepared

scaled-poisson-2d-32

185.0965 [182.2070–187.9860]

370.3119 [358.9713–381.6525]

1498/1529

0.984252

none

prepared

scaled-poisson-3d-6

33.0213 [31.2609–34.7816]

60.2981 [58.4771–62.1191]

N:261/268; T:249/255

0.998645

none

prepared

scaled-poisson-3d-10

54.4952 [54.4154–54.5750]

104.8242 [102.4973–107.1510]

444/454

0.974858

none

prepared

poisson-2d-16-neutral

6.2013 [5.8387–6.5639]

11.9717 [11.1624–12.7809]

50/52

0.764204

none

api

scaled-poisson-2d-16

69.9795 [66.5822–73.3768]

131.0953 [122.9758–139.2149]

N:575/588; T:574/587

0.949605

none

api

scaled-poisson-2d-32

190.0993 [184.5412–195.6574]

371.9122 [370.8897–372.9348]

1498/1529

0.984252

none

api

scaled-poisson-3d-6

32.2357 [30.6563–33.8150]

62.2525 [61.5884–62.9166]

N:261/268; T:249/255

0.998645

none

api

scaled-poisson-3d-10

51.9963 [51.9131–52.0795]

103.4289 [102.4915–104.3663]

444/454

0.974858

none

api

poisson-2d-16-neutral

6.3670 [5.6834–7.0505]

13.4360 [12.2513–14.6208]

50/52

0.764204

jacobi

prepared

scaled-poisson-2d-16

9.0347 [8.8500–9.2195]

18.8391 [17.0609–20.6173]

48/50

0.911256

jacobi

prepared

scaled-poisson-2d-32

21.2359 [21.0164–21.4555]

39.0692 [38.0208–40.1176]

115/119

0.929037

jacobi

prepared

scaled-poisson-3d-6

4.4503 [4.4227–4.4779]

9.2636 [8.8980–9.6292]

25/27

0.439964

jacobi

prepared

scaled-poisson-3d-10

6.6303 [6.4405–6.8200]

12.6303 [12.0156–13.2451]

37/39

0.969066

jacobi

prepared

poisson-2d-16-neutral

9.0027 [8.5240–9.4814]

18.0454 [17.4063–18.6845]

50/52

0.764204

jacobi

api

scaled-poisson-2d-16

8.6395 [7.9783–9.3007]

17.1381 [16.5369–17.7393]

48/50

0.911256

jacobi

api

scaled-poisson-2d-32

20.9024 [20.5442–21.2607]

40.3409 [37.8065–42.8754]

115/119

0.929037

jacobi

api

scaled-poisson-3d-6

4.4296 [4.2419–4.6173]

9.0008 [8.6184–9.3831]

25/27

0.439964

jacobi

api

scaled-poisson-3d-10

6.9896 [6.0050–7.9743]

13.9987 [11.3864–16.6111]

37/39

0.969066

jacobi

api

poisson-2d-16-neutral

9.7720 [9.7092–9.8347]

18.8995 [16.9554–20.8436]

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-4 / 1.36862e-4 versus LU’s 3.44175e-12, all below the requested 1.47316e-4 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.4460 [0.4031–0.4890]

0.0594 [0.0527–0.0662]

2.6482 [2.1932–3.1031]

0.5342 [0.4350–0.6334]

0.3948 [0.3661–0.4236]

0.0379 [0.0333–0.0426]

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

4.1633 [3.9357–4.3908]

0.4109 [0.4022–0.4196]

8.2819 [6.6399–9.9239]

1.3170 [1.2415–1.3925]

1.6149 [1.5384–1.6914]

0.1349 [0.1228–0.1471]

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.1944 [0.1727–0.2160]

0.0367 [0.0298–0.0436]

1.8500 [1.4986–2.2015]

0.3190 [0.2628–0.3752]

0.4183 [0.3671–0.4695]

0.0377 [0.0263–0.0491]

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.4514 [1.3790–1.5238]

0.1794 [0.1641–0.1947]

3.8982 [3.5285–4.2679]

0.6182 [0.5373–0.6991]

3.5389 [3.2384–3.8394]

0.2174 [0.1683–0.2666]

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.0446 [0.0427–0.0465]

0.0523 [0.0495–0.0551]

0.4662 [0.4241–0.5083]

0.4915 [0.4309–0.5521]

0.2875 [0.2621–0.3130]

0.0256 [0.0233–0.0280]

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.4252 [0.4200–0.4304]

0.0799 [0.0744–0.0853]

2.2640 [2.2230–2.3051]

0.5028 [0.4730–0.5326]

0.3992 [0.3490–0.4494]

0.0404 [0.0364–0.0444]

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.8981 [3.8690–3.9272]

0.4147 [0.4072–0.4222]

6.8638 [6.7902–6.9373]

1.2605 [1.2374–1.2835]

1.4512 [1.4183–1.4842]

0.1331 [0.1317–0.1345]

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.1875 [0.1858–0.1892]

0.0452 [0.0440–0.0464]

1.4909 [1.4623–1.5195]

0.2844 [0.2765–0.2924]

0.3711 [0.3676–0.3747]

0.0295 [0.0293–0.0297]

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.4325 [1.4170–1.4480]

0.2108 [0.1984–0.2233]

3.4814 [3.4745–3.4884]

0.5910 [0.5834–0.5987]

3.4231 [3.3453–3.5008]

0.2587 [0.2238–0.2937]

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.0657 [0.0540–0.0775]

0.0785 [0.0662–0.0909]

0.5676 [0.4295–0.7058]

0.6250 [0.4830–0.7671]

0.3192 [0.2752–0.3631]

0.0335 [0.0260–0.0410]

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-cleanup/baseline/. They are not stored in Git. The complete 52-command forward/reverse declaration and per-file SHA-256/bytes are in build/performance-cleanup/manifest.json. Its SHA-256 is:

b6168caa597ea4a1f15901adbf281b4c738b002bbaf6056ecb9bdbe0800d5c21.

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

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

  • cuda-release: _core.cpython-312-x86_64-linux-gnu.so → f668f6f4ef3671ba656a3e5c175e37506218098ddeea9bcb611c229da34260d4. _cuda.cpython-312-x86_64-linux-gnu.so → 2917f821463d3aecdb50c5548f6e122458d52b1180f8d9f3875d8fa53c995820.

The original staged cgpy/cgbench packages, binaries and CMake caches were copied before sampling to build/performance-cleanup/binaries/<preset>/. Copied CMake caches still name the live checkout: when rerunning a saved binary after source changes, use the manifest’s immutable compiled revision, rather than relabeling it with the live checkout HEAD. Binary hashes identify what was actually loaded; rebuilds on another host need not have identical bytes.

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-cleanup-baseline
cd cgpy-cleanup-baseline
git checkout --detach d34036f5668559612766cb5d8872a41f3097ffdc
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 standalone driver reproduces all 52 fixed profiles. It 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-cleanup/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-cleanup/reproduced/cuda-release-sparse-solvers-jacobi-prepared-r01.json \
    --output build/performance-cleanup/replayed-scaled-pcg.json

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

For later cleanup regression, 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. CUB adoption needs its own immediate pre/post-change baseline, correctness evidence and predeclared uncertainty/regression budget. No automatic crossover or backend mode is inferred.