Skip to content

Commit 1679809

Browse files
committed
1. custom-op of SOG and LES, but it seems have some bugs and do not improve computational effficiency
1 parent 3223024 commit 1679809

21 files changed

Lines changed: 4256 additions & 137 deletions

PT_OP_FLOAT32_SYNC_CHANGELOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# PT NUFFT Op Float32 Sync Notes
2+
3+
Date: 2026-04-08
4+
5+
## Scope
6+
7+
Synchronized SOG and LES PyTorch NUFFT custom ops to support native float32 and float64 dispatch without wrapper-level float32->float64 casting.
8+
9+
## Code Changes
10+
11+
- SOG C++ op: native float32/float64 dispatch, precision-aware plan cache, typed setpts/execute paths.
12+
- source/op/pt/nufft_sog_op.cc
13+
- SOG CUDA kernels: templated float/double kernels and dual launchers.
14+
- source/op/pt/sog_nufft_kernels.cu
15+
- LES C++ op: native float32/float64 dispatch, precision-aware plan cache, typed setpts/execute paths.
16+
- source/op/pt/nufft_les_op.cc
17+
- LES CUDA kernels: templated float/double kernels and dual launchers.
18+
- source/op/pt/les_nufft_kernels.cu
19+
- Added LES float32 custom-op vs python-fallback benchmark script.
20+
- dp_example/nacl/bench_les_op_float32_vs_fallback.py
21+
22+
## Build Validation
23+
24+
- Build command:
25+
- `cmake --build source/build --target deepmd_op_pt -j 8`
26+
- Result:
27+
- `Built target deepmd_op_pt`
28+
29+
## Benchmark Commands
30+
31+
- `python -u dp_example/nacl/bench_sog_op_accuracy_speed.py`
32+
- `python -u dp_example/nacl/bench_les_op_accuracy_speed.py`
33+
- `python -u dp_example/nacl/bench_les_op_float32_vs_fallback.py`
34+
- SOG float32 fallback comparison snippet executed in terminal (forward+loss+backward, nloc=96).
35+
36+
## Unified Accuracy/Performance Summary
37+
38+
### Float64 custom-op vs reference (pytorch_finufft)
39+
40+
| Model | Case | Max Abs Err | Max Rel Err | Custom (ms/call) | Ref (ms/call) | Speedup |
41+
| --- | --- | ---: | ---: | ---: | ---: | ---: |
42+
| SOG | data_16_train (8f) | 6.106227e-16 | 3.054427e-15 | 4.105 | 54.538 | 13.29x |
43+
| SOG | data_17_train (8f) | 8.049117e-16 | 3.478318e-15 | 4.114 | 48.578 | 11.81x |
44+
| LES | data_16_train (8f) | 2.053913e-15 | 4.514820e-15 | 3.944 | 77.653 | 19.69x |
45+
| LES | data_17_train (8f) | 4.440892e-15 | 7.992299e-15 | 3.908 | 46.120 | 11.80x |
46+
47+
### Float32 custom-op vs python fallback (e2e)
48+
49+
| Model | Workload | Accuracy Delta | Custom (ms/iter) | Fallback (ms/iter) | Speedup |
50+
| --- | --- | --- | ---: | ---: | ---: |
51+
| SOG | forward+loss+backward, nloc=96 | energy/grad_latent max_abs_err <= 1e-7 (quick sanity check) | 31.157 | 76.533 | 2.46x |
52+
| LES | forward corr + backward latent/sigma, nloc=256 | energy/grad_latent/grad_sigma all close (<=1e-6 scale) | 9.549 | 27.402 | 2.87x |
53+
54+
## Notes
55+
56+
- Root causes found during re-check:
57+
- SOG/LES float64 benchmark references were using centered mode indexing; corrected to FFT-order indexing to match modeord=1.
58+
- LES float32 fallback benchmark mistakenly set `coord.requires_grad=True`, which forced fallback path and invalidated speed conclusion.
59+
- LES CUDA kernels used centered `n = i - nk` indexing; corrected to FFT-order mapping.
60+
- cuFINUFFT type-2 execute argument order in LES path was reversed; corrected to `(out_nonuniform, in_modes)`.
61+
- LES type-2 plan sign updated to `isign=+1` (type-1 remains `-1`) to match pytorch_finufft path.
62+
- SOG/LES plan option initialization now safely falls back to available default-opts symbol (`*_default_opts`) and still enforces `modeord=1` when defaults are available.
63+
64+
- After fixes, LES float32 path shows both correctness parity and clear e2e speedup versus fallback in the tested workload.
65+
66+
## Follow-ups
67+
68+
- Profile LES float32 e2e hot spots (plan reuse, kernel occupancy, memory traffic) for the nloc=256 workload.
69+
- Compare LES float32 performance over multiple nloc values (64/128/256/512) to identify crossover points.

build_pt_op.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
set -e
3+
source /home/zyjin/anaconda3/etc/profile.d/conda.sh
4+
conda activate dp_devel
5+
export DP_VARIANT=cuda
6+
7+
cd source
8+
mkdir -p build && cd build
9+
PT_PATH=$(python -c 'import torch;print(torch.utils.cmake_prefix_path)')
10+
cmake -DENABLE_PYTORCH=ON -DCMAKE_PREFIX_PATH=${PT_PATH} -DUSE_TF_BACKEND=OFF -DUSE_PT_BACKEND=ON -DCMAKE_CXX_STANDARD=17 -DPYTHON_EXECUTABLE=$(which python) -DOP_CXX_ABI_PT=0 -DCMAKE_INSTALL_PREFIX=../.. -DCMAKE_CUDA_ARCHITECTURES="120" -DTORCH_CUDA_ARCH_LIST='12.0' ..
11+
make -j4 deepmd_op_pt

deepmd/pt/cxx_op.py

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
import os
23
import platform
34
from ctypes import (
45
CDLL,
@@ -7,6 +8,9 @@
78
from importlib import (
89
metadata,
910
)
11+
from pathlib import (
12+
Path,
13+
)
1014

1115
import torch
1216
from packaging.version import (
@@ -19,6 +23,36 @@
1923
)
2024

2125

26+
_OP_LIB_OVERRIDE_ENV = "DEEPMD_OP_PT_LIB"
27+
_ALLOW_PRELOADED_ENV = "DEEPMD_OP_PT_ALLOW_PRELOADED"
28+
29+
30+
def _is_truthy_env(name: str) -> bool:
31+
value = os.environ.get(name, "")
32+
return value.strip().lower() in {"1", "true", "yes", "on"}
33+
34+
35+
def _resolve_module_file(
36+
module_name: str,
37+
prefix: str,
38+
ext: str,
39+
) -> tuple[Path, bool]:
40+
override_path = os.environ.get(_OP_LIB_OVERRIDE_ENV)
41+
if override_path:
42+
return Path(override_path).expanduser().resolve(), True
43+
return (SHARED_LIB_DIR / (prefix + module_name)).with_suffix(ext).resolve(), False
44+
45+
46+
def _loaded_library_hints() -> list[str]:
47+
loaded_libraries = sorted(torch.ops.loaded_libraries)
48+
deepmd_libraries = [
49+
lib
50+
for lib in loaded_libraries
51+
if "deepmd_op_pt" in lib or "libdeepmd_op_pt" in lib
52+
]
53+
return deepmd_libraries if deepmd_libraries else loaded_libraries
54+
55+
2256
def load_library(module_name: str) -> bool:
2357
"""Load OP library.
2458
@@ -39,69 +73,85 @@ def load_library(module_name: str) -> bool:
3973
ext = ".so"
4074
prefix = "lib"
4175

42-
module_file = (SHARED_LIB_DIR / (prefix + module_name)).with_suffix(ext).resolve()
76+
module_file, from_env_override = _resolve_module_file(module_name, prefix, ext)
4377

44-
if module_file.is_file():
45-
# Skip if this library was already loaded by torch.ops.load_library.
46-
if str(module_file) in torch.ops.loaded_libraries:
47-
return True
48-
# Skip if ops were already registered via C++ shared-library linkage
49-
# (e.g. LAMMPS plugin links libdeepmd_op_pt.so at the C++ level).
50-
# TORCH_LIBRARY(deepmd, m) in print_summary.cc registers "enable_mpi"
51-
# as the first op; if it's accessible, the library is already loaded.
52-
# Calling torch.ops.load_library again would abort() the process.
53-
if hasattr(torch.ops, "deepmd") and hasattr(torch.ops.deepmd, "enable_mpi"):
78+
if not module_file.is_file():
79+
if from_env_override:
80+
raise RuntimeError(
81+
f"Environment variable {_OP_LIB_OVERRIDE_ENV} points to a non-existent file: {module_file}"
82+
)
83+
return False
84+
85+
# Skip if this exact library path was already loaded by torch.ops.load_library.
86+
if str(module_file) in torch.ops.loaded_libraries:
87+
return True
88+
89+
# If deepmd ops are already registered before this call, abort by default
90+
# to avoid silently using an unexpected preloaded library.
91+
if hasattr(torch.ops, "deepmd") and hasattr(torch.ops.deepmd, "enable_mpi"):
92+
if _is_truthy_env(_ALLOW_PRELOADED_ENV):
5493
return True
55-
try:
56-
torch.ops.load_library(module_file)
57-
except OSError as e:
58-
# check: CXX11_ABI_FLAG; version
59-
# from our op
60-
PT_VERSION = GLOBAL_CONFIG["pt_version"]
61-
PT_CXX11_ABI_FLAG = int(GLOBAL_CONFIG["pt_cxx11_abi_flag"])
62-
# from torch
63-
# strip the local version
64-
pt_py_version = Version(torch.__version__).public
65-
pt_cxx11_abi_flag = int(torch.compiled_with_cxx11_abi())
66-
67-
if PT_CXX11_ABI_FLAG != pt_cxx11_abi_flag:
68-
raise RuntimeError(
69-
"This deepmd-kit package was compiled with "
70-
f"CXX11_ABI_FLAG={PT_CXX11_ABI_FLAG}, but PyTorch runtime was compiled "
71-
f"with CXX11_ABI_FLAG={pt_cxx11_abi_flag}. These two library ABIs are "
72-
f"incompatible and thus an error is raised when loading {module_name}. "
73-
"You need to rebuild deepmd-kit against this PyTorch "
74-
"runtime."
75-
) from e
76-
77-
# different versions may cause incompatibility, see TF
78-
if PT_VERSION != pt_py_version:
79-
raise RuntimeError(
80-
"The version of PyTorch used to compile this "
81-
f"deepmd-kit package is {PT_VERSION}, but the version of PyTorch "
82-
f"runtime you are using is {pt_py_version}. These two versions are "
83-
f"incompatible and thus an error is raised when loading {module_name}. "
84-
f"You need to install PyTorch {PT_VERSION}, or rebuild deepmd-kit "
85-
f"against PyTorch {pt_py_version}.\nIf you are using a wheel from "
86-
"PyPI, you may consider to install deepmd-kit execuating "
87-
"`DP_ENABLE_PYTORCH=1 pip install deepmd-kit --no-binary deepmd-kit` "
88-
"instead."
89-
) from e
90-
error_message = (
91-
"This deepmd-kit package is inconsistent with PyTorch "
92-
f"Runtime, thus an error is raised when loading {module_name}. "
94+
loaded_hints = _loaded_library_hints()
95+
hint_text = "\n".join(loaded_hints) if loaded_hints else "(none reported by torch.ops.loaded_libraries)"
96+
raise RuntimeError(
97+
"DeepMD custom ops are already registered before deepmd.pt.cxx_op.load_library() "
98+
"could load the expected library path. This can indicate a mismatched or stale "
99+
"libdeepmd_op_pt.so in the current process.\n"
100+
f"Expected library path: {module_file}\n"
101+
f"Environment override ({_OP_LIB_OVERRIDE_ENV}): {os.environ.get(_OP_LIB_OVERRIDE_ENV, '(unset)')}\n"
102+
f"Loaded-library hints:\n{hint_text}\n"
103+
f"If this preloaded setup is intentional, set {_ALLOW_PRELOADED_ENV}=1 to bypass this check."
104+
)
105+
106+
try:
107+
torch.ops.load_library(module_file)
108+
except OSError as e:
109+
# check: CXX11_ABI_FLAG; version
110+
# from our op
111+
PT_VERSION = GLOBAL_CONFIG["pt_version"]
112+
PT_CXX11_ABI_FLAG = int(GLOBAL_CONFIG["pt_cxx11_abi_flag"])
113+
# from torch
114+
# strip the local version
115+
pt_py_version = Version(torch.__version__).public
116+
pt_cxx11_abi_flag = int(torch.compiled_with_cxx11_abi())
117+
118+
if PT_CXX11_ABI_FLAG != pt_cxx11_abi_flag:
119+
raise RuntimeError(
120+
"This deepmd-kit package was compiled with "
121+
f"CXX11_ABI_FLAG={PT_CXX11_ABI_FLAG}, but PyTorch runtime was compiled "
122+
f"with CXX11_ABI_FLAG={pt_cxx11_abi_flag}. These two library ABIs are "
123+
f"incompatible and thus an error is raised when loading {module_name}. "
93124
"You need to rebuild deepmd-kit against this PyTorch "
94125
"runtime."
126+
) from e
127+
128+
# different versions may cause incompatibility, see TF
129+
if PT_VERSION != pt_py_version:
130+
raise RuntimeError(
131+
"The version of PyTorch used to compile this "
132+
f"deepmd-kit package is {PT_VERSION}, but the version of PyTorch "
133+
f"runtime you are using is {pt_py_version}. These two versions are "
134+
f"incompatible and thus an error is raised when loading {module_name}. "
135+
f"You need to install PyTorch {PT_VERSION}, or rebuild deepmd-kit "
136+
f"against PyTorch {pt_py_version}.\nIf you are using a wheel from "
137+
"PyPI, you may consider to install deepmd-kit execuating "
138+
"`DP_ENABLE_PYTORCH=1 pip install deepmd-kit --no-binary deepmd-kit` "
139+
"instead."
140+
) from e
141+
error_message = (
142+
"This deepmd-kit package is inconsistent with PyTorch "
143+
f"Runtime, thus an error is raised when loading {module_name}. "
144+
"You need to rebuild deepmd-kit against this PyTorch "
145+
"runtime."
146+
)
147+
if PT_CXX11_ABI_FLAG == 1:
148+
# #1791
149+
error_message += (
150+
"\nWARNING: devtoolset on RHEL6 and RHEL7 does not support _GLIBCXX_USE_CXX11_ABI=1. "
151+
"See https://bugzilla.redhat.com/show_bug.cgi?id=1546704"
95152
)
96-
if PT_CXX11_ABI_FLAG == 1:
97-
# #1791
98-
error_message += (
99-
"\nWARNING: devtoolset on RHEL6 and RHEL7 does not support _GLIBCXX_USE_CXX11_ABI=1. "
100-
"See https://bugzilla.redhat.com/show_bug.cgi?id=1546704"
101-
)
102-
raise RuntimeError(error_message) from e
103-
return True
104-
return False
153+
raise RuntimeError(error_message) from e
154+
return True
105155

106156

107157
def load_mpi_library() -> None:

0 commit comments

Comments
 (0)