Skip to content

perf(pt): add opt-in SO2 edge-cohort inference backward 【hnu】 - #2

Draft
macqueen09 wants to merge 7 commits into
masterfrom
perf/ecs-inference-backward-20261019
Draft

macqueen09 wants to merge 7 commits into
masterfrom
perf/ecs-inference-backward-20261019

Conversation

@macqueen09

@macqueen09 macqueen09 commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Submitted by Hunan University.

Revision measured: 98a65e46d0076319f2fcb49ded77e6a93ecab6be integrated with upstream 4e1479d8. Matrix: 27 processes, 23 completed, 4 OOM (Neo4096 TORCH and Pro4096 in all three arms).

Measured Neo results

  • 1000 atoms: latency 40.817 → 39.470 ms (-3.30%; 1.034× throughput), steady allocated peak 5.464760 → 5.449051 GB (15.709 MB saved), reserved peak 6.090129 → 5.691671 GB.
  • 4096 atoms: latency 156.476 → 152.964 ms (-2.24%; 1.023× throughput), steady allocated peak 22.313645 → 22.256657 GB (56.988 MB saved), reserved peak 23.953670 → 23.282582 GB.

N1000 passes against both Torch and BASE. At N4096, energy/forces/stress pass against BASE but virial does not; BASE and ECS also fail their respective repeated-run virial check. Torch N4096 runs out of memory.

Summary

Adds an opt-in, default-off SO(2) edge-cohort backward for frozen DPA4-Neo inference. Set DP_SO2_EDGE_COHORT_INFER=1 with DP_TRITON_INFER=2 and DP_COMPILE_INFER=1 to enable it. Unsupported layouts and protected gradient paths retain the existing implementation.

Why it saves scratch memory

The native path advances each backward layer across the full edge set. This path completes the backward layers for a cohort of at most 8192 edges before advancing to the next cohort. The temporary g, gz, and sig buffers are reused across cohorts; saved activations and the returned input gradient still span all edges. This is an edge count, not an atom count. It reuses the native GEMM arithmetic and column parallelism, with separate full-graph and scratch strides for partial cohorts. Only owned scratch is overwritten; caller inputs are not mutated.

Small runtime-dispatch correction

The latest revision changes two files (35 insertions / 5 deletions): the public backward dispatcher always enters the opaque runtime operator, and a regression test traces 37 edges and then compiles/runs 37, 8193, and 16385 edges. This prevents a small make_fx/AOT example from fixing the large-input dispatch to the single-cohort trace. It does not change the numerical kernels. The complete feature diff remains two implementation files and one test file; no local benchmark data or experiment documents are included.

Validation setup

Baseline: upstream 4e1479d8a23fcc1c9baf36004d5685898ad421a8. The feature branch retains its original base; results below use its clean local integration into that upstream commit. RTX 5090; PyTorch 2.11.0+cu128; Triton 3.6.0; FP32 with TF32 and AMP disabled; expandable CUDA allocator segments; one CPU intra/inter-op thread.

TORCH = compiled PyTorch without native inference kernels; BASE = upstream native Triton inference; ECS = the updated feature enabled on the same upstream base. Each case uses the same checkpoint, device, and three deterministically perturbed diamond-carbon frames. There is one fresh process per implementation/case, three evaluations per frame for accuracy, eight timing warmups, and 30 synchronized wall-time samples. Timing includes the ASE energy/forces/stress evaluation, not just the SO(2) kernel. These are screening medians, not confidence intervals. Neo runs on GPU 0; Nano/Mini on GPU 1; Pro on GPU 0.

Checkpoints and actual dispatch

Model Parameters SHA-256
Mini 654988 13edeea63448c0f8a0b38e08fd8ba196fb1e2fad5f12486b1cd23320829da268
Nano 480268 ded047546c2b44a50e2c850680d77279e2bf3e107c10f1c2fe39a5c344e693f6
Neo 1124910 8d8b2b02b9575cb9e0af420cc1e8b1d9ffcbc04752c2203d29f48ccd0cd5f07d
Pro 22678990 446a6e893101fc52190cfccabb9d2328863d651aca36981fd8d3401c71a61221

Pro's checkpoint metadata uses an older fitting-type alias; the same local load-only compatibility mapping is applied in every arm. Neo's supported narrow layout is the optimization target; Nano, Mini, and Pro are compatibility/fallback checks, not claims of ECS acceleration.

Model Atoms Observed runtime cohorts (edges / cap / chunks)
Mini 96 fallback
Mini 4096 fallback
Nano 96 fallback
Nano 4096 fallback
Neo 96 15170 / 8192 / 2
Neo 1000 158002 / 8192 / 20
Neo 4096 647170 / 8192 / 80
Pro 96 fallback
Pro 4096 OOM

End-to-end latency

Negative change means lower latency. Compare BASE and ECS for incremental PR benefit; Torch is an additional reference.

Model Atoms Torch ms BASE ms ECS ms ECS vs BASE
Mini 96 12.1395 11.2088 11.0013 -1.85%
Mini 4096 121.8182 60.9331 60.8809 -0.09%
Nano 96 11.1547 10.8338 10.8563 +0.21%
Nano 4096 56.5371 33.7142 33.5977 -0.35%
Neo 96 13.2083 18.2731 16.1760 -11.48%
Neo 1000 88.8057 40.8172 39.4703 -3.30%
Neo 4096 OOM 156.4762 152.9638 -2.24%
Pro 96 126.0326 66.9730 67.0494 +0.11%
Pro 4096 OOM OOM OOM —

Peak GPU memory

Decimal GB (10^9 bytes). Full-window allocated includes the first inference/compile window and timed evaluations. Steady allocated resets the peak after warmup. Reserved measures allocator-held capacity, not live tensors. Caches are shared per model, in TORCH → BASE → ECS order: cold compilation/autotuning can affect full-window peaks, so fallback-model first-window differences are not ECS savings. Use the steady allocated comparison to isolate runtime capacity. OOM runs have no valid steady timing or acceptance comparison.

Model Atoms Torch full allocated GB BASE full allocated GB ECS full allocated GB ECS vs BASE
Mini 96 0.4005 0.2863 0.1905 -33.46%
Mini 4096 12.1157 7.4532 7.4532 +0.00%
Nano 96 0.2675 0.2048 0.1042 -49.14%
Nano 4096 6.6781 3.9105 3.9105 +0.00%
Neo 96 1.1185 0.6464 0.5663 -12.39%
Neo 1000 10.3907 5.4648 5.4491 -0.29%
Neo 4096 OOM 22.3136 22.2567 -0.26%
Pro 96 11.4436 5.1397 5.0390 -1.96%
Pro 4096 OOM OOM OOM —
Model Atoms BASE steady allocated GB ECS steady allocated GB BASE reserved GB ECS reserved GB
Mini 96 0.1905 0.1905 0.3355 0.2307
Mini 4096 7.4532 7.4532 8.0950 8.0950
Nano 96 0.1042 0.1042 0.2328 0.1279
Nano 4096 3.9105 3.9105 4.1691 4.1691
Neo 96 0.5457 0.5519 0.6942 0.6501
Neo 1000 5.4648 5.4491 6.0901 5.6917
Neo 4096 22.3136 22.2567 23.9537 23.2826
Pro 96 5.0390 5.0390 5.2513 5.1464
Pro 4096 OOM OOM OOM OOM

Numerical consistency

Unchanged acceptance rule: every element must satisfy abs(candidate-reference) <= 1e-5 + 1e-4*abs(reference). The table reports the worst error/tolerance ratio across energy, forces, virial, stress and all same-frame repeat pairs; ≤1 passes. Self-comparisons expose native reduction variability. These test implementation agreement, not physical accuracy against DFT. N1000 is the primary Neo accuracy case; N4096 is also reported without suppressing failures.

Model Atoms Comparison Worst error/tolerance Result
Mini 96 TORCH vs TORCH 0.615681 PASS
Mini 96 BASE vs BASE 0.695680 PASS
Mini 96 ECS vs ECS 1.337985 FAIL
Mini 96 BASE vs TORCH 0.764672 PASS
Mini 96 ECS vs TORCH 1.427887 FAIL
Mini 96 ECS vs BASE 1.398952 FAIL
Mini 4096 TORCH vs TORCH 8.128800 FAIL
Mini 4096 BASE vs BASE 5.211110 FAIL
Mini 4096 ECS vs ECS 6.999590 FAIL
Mini 4096 BASE vs TORCH 7.803240 FAIL
Mini 4096 ECS vs TORCH 10.276332 FAIL
Mini 4096 ECS vs BASE 7.762472 FAIL
Nano 96 TORCH vs TORCH 5.759477 FAIL
Nano 96 BASE vs BASE 6.251727 FAIL
Nano 96 ECS vs ECS 4.453711 FAIL
Nano 96 BASE vs TORCH 8.291340 FAIL
Nano 96 ECS vs TORCH 8.085204 FAIL
Nano 96 ECS vs BASE 4.721403 FAIL
Nano 4096 TORCH vs TORCH 3.349890 FAIL
Nano 4096 BASE vs BASE 3.316048 FAIL
Nano 4096 ECS vs ECS 1.344817 FAIL
Nano 4096 BASE vs TORCH 3.746734 FAIL
Nano 4096 ECS vs TORCH 2.501009 FAIL
Nano 4096 ECS vs BASE 2.724966 FAIL
Neo 96 TORCH vs TORCH 0.246918 PASS
Neo 96 BASE vs BASE 0.428411 PASS
Neo 96 ECS vs ECS 0.592591 PASS
Neo 96 BASE vs TORCH 0.502854 PASS
Neo 96 ECS vs TORCH 0.493832 PASS
Neo 96 ECS vs BASE 0.589677 PASS
Neo 1000 TORCH vs TORCH 0.300730 PASS
Neo 1000 BASE vs BASE 0.282585 PASS
Neo 1000 ECS vs ECS 0.337024 PASS
Neo 1000 BASE vs TORCH 0.487905 PASS
Neo 1000 ECS vs TORCH 0.493978 PASS
Neo 1000 ECS vs BASE 0.374288 PASS
Neo 4096 BASE vs BASE 2.139025 FAIL
Neo 4096 ECS vs ECS 3.961358 FAIL
Neo 4096 ECS vs BASE 3.444328 FAIL
Pro 96 TORCH vs TORCH 0.179889 PASS
Pro 96 BASE vs BASE 0.214099 PASS
Pro 96 ECS vs ECS 0.184589 PASS
Pro 96 BASE vs TORCH 0.264063 PASS
Pro 96 ECS vs TORCH 0.262394 PASS
Pro 96 ECS vs BASE 0.212429 PASS
Per-output absolute and relative errors

Relative error uses denominator max(abs(reference), 1e-8) and is descriptive, not the acceptance criterion. Energy/virial: eV; force: eV/Å; stress: eV/ų.

Model Atoms Comparison Output Max absolute error Max relative error Error/tolerance
Mini 96 BASE vs TORCH energy 1.561642e-05 1.793399e-08 0.000179
Mini 96 BASE vs TORCH forces 9.164459e-06 1.993326e-03 0.764672
Mini 96 BASE vs TORCH virial 5.954336e-05 2.904747e-04 0.724568
Mini 96 BASE vs TORCH stress 1.093309e-07 2.743889e-04 0.009074
Mini 96 ECS vs TORCH energy 1.698732e-05 1.950834e-08 0.000195
Mini 96 ECS vs TORCH forces 1.014790e-05 2.654609e-03 0.977426
Mini 96 ECS vs TORCH virial 5.903790e-05 5.724303e-04 1.427887
Mini 96 ECS vs TORCH stress 1.084028e-07 5.508654e-04 0.008985
Mini 96 ECS vs BASE energy 1.859665e-05 2.135650e-08 0.000214
Mini 96 ECS vs BASE forces 7.366963e-06 1.107072e-03 0.623415
Mini 96 ECS vs BASE virial 4.403243e-05 5.608353e-04 1.398952
Mini 96 ECS vs BASE stress 8.085039e-08 5.441129e-04 0.006681
Mini 4096 BASE vs TORCH energy 1.347065e-04 3.625742e-09 0.000036
Mini 4096 BASE vs TORCH forces 1.293050e-05 4.641266e-01 0.969827
Mini 4096 BASE vs TORCH virial 1.236864e-03 2.346682e-03 7.803240
Mini 4096 BASE vs TORCH stress 5.322830e-08 2.122169e-03 0.004394
Mini 4096 ECS vs TORCH energy 1.285672e-04 3.460671e-09 0.000035
Mini 4096 ECS vs TORCH forces 1.183595e-05 3.320838e-01 0.905794
Mini 4096 ECS vs TORCH virial 1.257736e-03 3.090420e-03 10.276332
Mini 4096 ECS vs TORCH stress 5.412654e-08 2.910563e-03 0.004468
Mini 4096 ECS vs BASE energy 9.328127e-05 2.510747e-09 0.000025
Mini 4096 ECS vs BASE forces 8.865208e-06 3.350353e-01 0.678851
Mini 4096 ECS vs BASE virial 2.497743e-04 2.335810e-03 7.762472
Mini 4096 ECS vs BASE stress 1.074901e-08 2.323637e-03 0.000927
Nano 96 BASE vs TORCH energy 1.823902e-05 2.090281e-08 0.000209
Nano 96 BASE vs TORCH forces 1.206264e-05 4.972439e-03 0.812996
Nano 96 BASE vs TORCH virial 8.977322e-05 2.564721e-02 8.291340
Nano 96 BASE vs TORCH stress 1.648376e-07 2.564721e-02 0.016481
Nano 96 ECS vs TORCH energy 2.533197e-05 2.904281e-08 0.000290
Nano 96 ECS vs TORCH forces 9.787469e-06 8.827396e-03 0.823692
Nano 96 ECS vs TORCH virial 8.754131e-05 2.453173e-02 8.085204
Nano 96 ECS vs TORCH stress 1.607395e-07 2.453173e-02 0.016072
Nano 96 ECS vs BASE energy 3.272295e-05 3.750210e-08 0.000375
Nano 96 ECS vs BASE forces 1.116579e-05 5.334655e-03 0.791467
Nano 96 ECS vs BASE virial 5.137011e-05 2.495454e-02 4.721403
Nano 96 ECS vs BASE stress 9.432350e-08 2.495454e-02 0.009382
Nano 4096 BASE vs TORCH energy 4.506111e-04 1.210841e-08 0.000121
Nano 4096 BASE vs TORCH forces 1.404241e-05 6.769194e-02 1.053942
Nano 4096 BASE vs TORCH virial 7.144431e-04 4.335409e-04 3.746734
Nano 4096 BASE vs TORCH stress 3.074598e-08 4.277479e-04 0.003064
Nano 4096 ECS vs TORCH energy 4.935265e-04 1.326159e-08 0.000133
Nano 4096 ECS vs TORCH forces 1.448540e-05 4.958943e-02 1.165918
Nano 4096 ECS vs TORCH virial 7.956436e-04 2.708310e-04 2.501009
Nano 4096 ECS vs TORCH stress 3.424044e-08 2.595841e-04 0.003414
Nano 4096 ECS vs BASE energy 1.282692e-04 3.446732e-09 0.000034
Nano 4096 ECS vs BASE forces 1.408198e-05 5.518264e-02 1.187300
Nano 4096 ECS vs BASE virial 5.284883e-04 3.152909e-04 2.724966
Nano 4096 ECS vs BASE stress 2.274344e-08 3.043243e-04 0.002269
Neo 96 BASE vs TORCH energy 7.748604e-06 8.908634e-09 0.000089
Neo 96 BASE vs TORCH forces 5.952976e-06 2.230875e-03 0.355487
Neo 96 BASE vs TORCH virial 4.529947e-05 9.568817e-05 0.502854
Neo 96 BASE vs TORCH stress 8.317686e-08 9.374343e-05 0.007127
Neo 96 ECS vs TORCH energy 5.304813e-06 6.098988e-09 0.000061
Neo 96 ECS vs TORCH forces 5.856414e-06 8.017910e-03 0.373506
Neo 96 ECS vs TORCH virial 5.094045e-05 9.396938e-05 0.493832
Neo 96 ECS vs TORCH stress 9.353459e-08 8.864950e-05 0.008014
Neo 96 ECS vs BASE energy 8.463860e-06 9.730969e-09 0.000097
Neo 96 ECS vs BASE forces 5.063990e-06 6.448498e-03 0.299279
Neo 96 ECS vs BASE virial 2.493053e-05 1.122024e-04 0.589677
Neo 96 ECS vs BASE stress 4.577633e-08 9.504777e-05 0.003926
Neo 1000 BASE vs TORCH energy 2.640486e-05 2.914324e-09 0.000029
Neo 1000 BASE vs TORCH forces 7.663981e-06 4.736570e-02 0.487905
Neo 1000 BASE vs TORCH virial 2.462989e-04 5.002020e-05 0.436885
Neo 1000 BASE vs TORCH stress 4.341533e-08 4.034755e-05 0.003713
Neo 1000 ECS vs TORCH energy 1.507998e-05 1.664388e-09 0.000017
Neo 1000 ECS vs TORCH forces 7.073846e-06 3.928089e-02 0.493978
Neo 1000 ECS vs TORCH virial 2.700571e-04 4.024785e-05 0.355441
Neo 1000 ECS vs TORCH stress 4.760321e-08 3.536726e-05 0.004072
Neo 1000 ECS vs BASE energy 2.437830e-05 2.690958e-09 0.000027
Neo 1000 ECS vs BASE forces 4.837813e-06 2.716897e-02 0.281892
Neo 1000 ECS vs BASE virial 5.240005e-05 4.285355e-05 0.374288
Neo 1000 ECS vs BASE stress 9.236606e-09 3.742531e-05 0.000789
Neo 4096 ECS vs BASE energy 4.243851e-05 1.143567e-09 0.000011
Neo 4096 ECS vs BASE forces 5.454211e-06 4.470043e-02 0.387467
Neo 4096 ECS vs BASE virial 1.621621e-04 5.858780e-04 3.444328
Neo 4096 ECS vs BASE stress 6.978628e-09 5.731754e-04 0.000596
Pro 96 BASE vs TORCH energy 5.275011e-06 6.064021e-09 0.000061
Pro 96 BASE vs TORCH forces 1.805925e-06 1.514653e-03 0.081400
Pro 96 BASE vs TORCH virial 1.746506e-05 4.815835e-05 0.264063
Pro 96 BASE vs TORCH stress 3.206857e-08 2.745501e-05 0.002625
Pro 96 ECS vs TORCH energy 5.066395e-06 5.824201e-09 0.000058
Pro 96 ECS vs TORCH forces 1.574739e-06 1.860346e-03 0.095593
Pro 96 ECS vs TORCH virial 1.436145e-05 4.785383e-05 0.262394
Pro 96 ECS vs TORCH stress 2.636986e-08 2.926973e-05 0.002159
Pro 96 ECS vs BASE energy 2.890825e-06 3.323221e-09 0.000033
Pro 96 ECS vs BASE forces 1.152785e-06 6.858512e-04 0.078825
Pro 96 ECS vs BASE virial 7.182093e-06 3.874143e-05 0.212429
Pro 96 ECS vs BASE stress 1.318744e-08 2.113096e-05 0.001080

Regression coverage

  • 61 focused CUDA tests passed on the feature branch; 61 passed on its latest-master integration.
  • New symbolic make_fx → torch.compile test verifies that large runtime inputs keep the opaque cohort route and match the native backward bit-for-bit for the checked operands.
  • Existing tests cover cohort boundaries, ownership/non-mutation, unsupported layouts, parameter/saved-output gradient fallback, and higher-order protection.
  • Ruff lint/format and git diff whitespace checks pass on the feature branch.

Run the focused suite with the project's PyTorch/CUDA test environment:

pytest source/tests/pt_expt/kernels/test_so2_edge_cohort.py -q

The feature remains default-off and this PR remains Draft. Standalone AOTInductor/C++ deployment is not validated by the online-Python inference tests above. Multi-model failures/OOM and small-input overhead are reported rather than counted as improvements.

Reproducing the model comparison

Use a CUDA build of this revision and the same checkpoint. Run each arm in a fresh process, setting DP_COMPILE_INFER=1, DP_CUDA_INFER=0, DP_TF32_INFER=0, DP_AMP_INFER=0, DP_INTRA_OP_PARALLELISM_THREADS=1, DP_INTER_OP_PARALLELISM_THREADS=1, and PYTORCH_ALLOC_CONF=expandable_segments:True before importing DeePMD. Set DP_TRITON_INFER=0 for TORCH, or 2 for BASE/ECS; only ECS sets DP_SO2_EDGE_COHORT_INFER=1.

import numpy as np
import torch
from ase.build import bulk
from deepmd.calculator import DP

torch.set_num_threads(1)
torch.set_float32_matmul_precision('highest')
torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False
atoms = bulk('C', 'diamond', a=3.567, cubic=True).repeat((5, 5, 5))
# (2,2,3): 96 atoms; (5,5,5): 1000; (8,8,8): 4096
rng = np.random.default_rng(0)
frames = [atoms.positions + 0.02 * rng.standard_normal(atoms.positions.shape)
          for _ in range(3)]
calc = DP(model='DPA4-Neo-OMat24-v20260627.pt')
def evaluate(pos):
    calc.reset()  # do not benchmark ASE's result cache
    sample = atoms.copy()
    sample.positions = pos
    sample.calc = calc
    forces = sample.get_forces().copy()
    return (sample.get_potential_energy(), forces,
            np.asarray(calc.results['virial']).copy(), sample.get_stress().copy())

Collect three repeats of all three frames for each arm; compare corresponding frames with the tolerance above. For timing, warm up eight evaluations, synchronize CUDA, then time 30 evaluations with synchronization after each. Record torch.cuda.max_memory_allocated() and max_memory_reserved() from before the first evaluation; separately reset peak statistics after warmup and replay 30 evaluations for steady peaks. Raw measurements and checkpoint hashes are retained locally; the tables above are the review evidence, not additional repository files.

Use a compiled single-cohort branch for small edge sets and retain a streamed
runtime path for larger frozen Neo input gradients. Keep native fallback and
explicit stride defaults, add 60 CUDA checks and cold-first-peak evidence.

The switch remains disabled by default; multi-model and AOT deployment are
not validated. N64 cold allocated peak falls 3.62%; no speedup is claimed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant