Skip to content

Commit 7e9c93b

Browse files
dzzz2001claude
andcommitted
perf(gint): optimize phi_operator kernels
- set_phi_dphi_kernel: add WantPhi non-type template parameter and dispatch from the launch site. The dphi-only callers (gint_tau) pass phi=nullptr; with WantPhi==false the compiler drops the phi[] stores and the per-iw `phi != nullptr` branch entirely. - phi_dot_dphi_kernel / phi_dot_dphi_r_kernel: replace the shared- memory tree reduce with a single-warp warpReduceSum and drop the dynamic shared-memory allocation at the launch sites. Launch configuration is pinned at blockDim.x == 32; a comment guards the invariant. - Plain `if` (not `if constexpr`) on WantPhi keeps the code C++11-compliant — ABACUS targets C++11 and nvcc otherwise emits warning deepmodeling#2912-D. WantPhi is still a non-type template parameter, so the compiler folds the constant and eliminates the dead branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f09748a commit 7e9c93b

3 files changed

Lines changed: 103 additions & 101 deletions

File tree

source/source_lcao/module_gint/kernel/phi_operator_gpu.cu

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "phi_operator_kernel.cuh"
33
#include "dgemm_vbatch.h"
44
#include <cuda_runtime.h>
5+
#include <type_traits>
56
#include "source_base/module_device/device_check.h"
67

78
namespace ModuleGint
@@ -124,30 +125,41 @@ void PhiOperatorGpu<Real>::set_phi_dphi(double* phi_d, double* dphi_x_d, double*
124125
{
125126
dim3 grid_dim(mgrids_num_, bgrid_batch_->get_batch_size());
126127
dim3 threads_per_block(64);
127-
set_phi_dphi_kernel<<<grid_dim, threads_per_block, 0, stream_>>>(
128-
gint_gpu_vars_->nwmax,
129-
mgrids_num_,
130-
gint_gpu_vars_->nr_max,
131-
gint_gpu_vars_->dr_uniform,
132-
gint_gpu_vars_->ucell_atom_nwl_d,
133-
gint_gpu_vars_->atom_iw2_new_d,
134-
gint_gpu_vars_->atom_iw2_ylm_d,
135-
gint_gpu_vars_->atom_iw2_l_d,
136-
gint_gpu_vars_->atom_nw_d,
137-
gint_gpu_vars_->iat2it_d,
138-
gint_gpu_vars_->rcut_d,
139-
gint_gpu_vars_->psi_u_d,
140-
gint_gpu_vars_->dpsi_u_d,
141-
gint_gpu_vars_->mgrids_pos_d,
142-
atoms_iat_.get_device_ptr(),
143-
atoms_bgrids_rcoords_.get_device_ptr(),
144-
atoms_num_info_.get_device_ptr(),
145-
atom_phi_start_.get_device_ptr(),
146-
bgrid_phi_len_.get_device_ptr(),
147-
phi_d,
148-
dphi_x_d,
149-
dphi_y_d,
150-
dphi_z_d);
128+
// Dispatch the WantPhi template based on whether phi is requested.
129+
// Lets the compiler drop the phi[] stores entirely in the dphi-only case
130+
// (gint_tau) without paying a per-iw `phi != nullptr` branch in the loop.
131+
auto launch = [&](auto want_phi) {
132+
constexpr bool WantPhi = decltype(want_phi)::value;
133+
set_phi_dphi_kernel<WantPhi><<<grid_dim, threads_per_block, 0, stream_>>>(
134+
gint_gpu_vars_->nwmax,
135+
mgrids_num_,
136+
gint_gpu_vars_->nr_max,
137+
gint_gpu_vars_->dr_uniform,
138+
gint_gpu_vars_->ucell_atom_nwl_d,
139+
gint_gpu_vars_->atom_iw2_new_d,
140+
gint_gpu_vars_->atom_iw2_ylm_d,
141+
gint_gpu_vars_->atom_iw2_l_d,
142+
gint_gpu_vars_->atom_nw_d,
143+
gint_gpu_vars_->iat2it_d,
144+
gint_gpu_vars_->rcut_d,
145+
gint_gpu_vars_->psi_u_d,
146+
gint_gpu_vars_->dpsi_u_d,
147+
gint_gpu_vars_->mgrids_pos_d,
148+
atoms_iat_.get_device_ptr(),
149+
atoms_bgrids_rcoords_.get_device_ptr(),
150+
atoms_num_info_.get_device_ptr(),
151+
atom_phi_start_.get_device_ptr(),
152+
bgrid_phi_len_.get_device_ptr(),
153+
phi_d,
154+
dphi_x_d,
155+
dphi_y_d,
156+
dphi_z_d);
157+
};
158+
if (phi_d != nullptr) {
159+
launch(std::true_type{});
160+
} else {
161+
launch(std::false_type{});
162+
}
151163
CHECK_LAST_CUDA_ERROR("kernel launch");
152164
}
153165

@@ -427,8 +439,9 @@ void PhiOperatorGpu<Real>::phi_dot_dphi(
427439
{
428440
dim3 grid_dim(bgrid_batch_->get_max_atoms_per_bgrid(),
429441
bgrid_batch_->get_batch_size());
442+
// Kernel reduce is single-warp; blockDim.x MUST stay 32.
430443
dim3 threads_per_block(32);
431-
phi_dot_dphi_kernel<<<grid_dim, threads_per_block, sizeof(double) * 32 * 3, stream_>>>(
444+
phi_dot_dphi_kernel<<<grid_dim, threads_per_block, 0, stream_>>>(
432445
phi_d,
433446
dphi_x_d,
434447
dphi_y_d,
@@ -454,8 +467,9 @@ void PhiOperatorGpu<Real>::phi_dot_dphi_r(
454467
{
455468
dim3 grid_dim(mgrids_num_,
456469
bgrid_batch_->get_batch_size());
470+
// Kernel reduce is single-warp; blockDim.x MUST stay 32.
457471
dim3 threads_per_block(32);
458-
phi_dot_dphi_r_kernel<<<grid_dim, threads_per_block, sizeof(double) * 32 * 6, stream_>>>(
472+
phi_dot_dphi_r_kernel<<<grid_dim, threads_per_block, 0, stream_>>>(
459473
phi_d,
460474
dphi_x_d,
461475
dphi_y_d,

source/source_lcao/module_gint/kernel/phi_operator_kernel.cu

Lines changed: 61 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ template __global__ void set_phi_kernel<float>(
105105
const int*, const double3*, const int2*, const int*, const int*,
106106
float*);
107107

108+
template<bool WantPhi>
108109
__global__ void set_phi_dphi_kernel(
109110
const int nwmax,
110111
const int mgrids_num,
@@ -156,6 +157,7 @@ __global__ void set_phi_dphi_kernel(
156157
grad_rl_sph_harm(nwl, coord.x, coord.y, coord.z, rly, grly);
157158

158159
// interpolation
160+
const double inv_dist = 1.0 / dist; // hoisted: re-used by every iw below
159161
const double pos = dist / dr_uniform;
160162
const int ip = static_cast<int>(pos);
161163
const double x0 = pos - ip;
@@ -182,15 +184,17 @@ __global__ void set_phi_dphi_kernel(
182184
const int iw_l = atom_iw2_l[it_nw + iw];
183185
const int idx_ylm = atom_iw2_ylm [it_nw + iw];
184186
const double rl = pow_int(dist, iw_l);
185-
const double tmprl = tmp / rl;
187+
const double inv_rl = 1.0 / rl;
188+
const double tmprl = tmp * inv_rl;
186189

187-
// if phi == nullptr, it means that we only need dphi.
188-
if(phi != nullptr)
190+
if (WantPhi)
189191
{
190192
phi[phi_idx + iw] = tmprl * rly[idx_ylm];
191193
}
192194
// derivative of wave functions with respect to atom positions.
193-
const double tmpdphi_rly = (dtmp - tmp * iw_l / dist) / rl * rly[idx_ylm] / dist;
195+
// (dtmp - tmp*iw_l/dist) / rl * rly / dist == (dtmp*inv_dist - tmp*iw_l*inv_dist^2) * inv_rl * rly
196+
const double tmpdphi_rly = (dtmp * inv_dist - tmp * iw_l * inv_dist * inv_dist)
197+
* inv_rl * rly[idx_ylm];
194198

195199
dphi_x[phi_idx + iw] = tmpdphi_rly * coord.x + tmprl * grly[idx_ylm * 3 + 0];
196200
dphi_y[phi_idx + iw] = tmpdphi_rly * coord.y + tmprl * grly[idx_ylm * 3 + 1];
@@ -203,7 +207,7 @@ __global__ void set_phi_dphi_kernel(
203207
bgrid_phi_len[bgrid_id] * mgrid_id;
204208
for (int iw = 0; iw < atom_nw[atom_type]; iw++)
205209
{
206-
if(phi != nullptr)
210+
if (WantPhi)
207211
{
208212
phi[phi_idx + iw] = 0.0;
209213
}
@@ -215,6 +219,20 @@ __global__ void set_phi_dphi_kernel(
215219
}
216220
}
217221

222+
// Explicit instantiations for set_phi_dphi_kernel
223+
template __global__ void set_phi_dphi_kernel<true>(
224+
const int, const int, const int, const double,
225+
const int*, const bool*, const int*, const int*, const int*, const int*,
226+
const double*, const double*, const double*, const double3*,
227+
const int*, const double3*, const int2*, const int*, const int*,
228+
double*, double*, double*, double*);
229+
template __global__ void set_phi_dphi_kernel<false>(
230+
const int, const int, const int, const double,
231+
const int*, const bool*, const int*, const int*, const int*, const int*,
232+
const double*, const double*, const double*, const double3*,
233+
const int*, const double3*, const int2*, const int*, const int*,
234+
double*, double*, double*, double*);
235+
218236
// The code for `set_ddphi_kernel` is quite difficult to understand.
219237
// To grasp it, you better refer to the CPU function `set_ddphi`
220238
__global__ void set_ddphi_kernel(
@@ -264,7 +282,8 @@ __global__ void set_ddphi_kernel(
264282
bgrid_phi_len[bgrid_id] * mgrid_id;
265283
for(int i = 0; i < 6; i++)
266284
{
267-
coord[i/2] += std::pow(-1, i%2) * 0.0001;
285+
const double eps = (i & 1) ? -0.0001 : 0.0001;
286+
coord[i/2] += eps;
268287
double dist = norm3d(coord[0], coord[1], coord[2]);
269288
if (dist < 1.0E-9)
270289
{ dist += 1.0E-9; }
@@ -276,6 +295,7 @@ __global__ void set_ddphi_kernel(
276295
grad_rl_sph_harm(nwl, coord[0], coord[1], coord[2], rly, grly);
277296

278297
// interpolation
298+
const double inv_dist = 1.0 / dist; // hoisted: re-used by every iw
279299
const double pos = dist / dr_uniform;
280300
const int ip = static_cast<int>(pos);
281301
const double x0 = pos - ip;
@@ -300,8 +320,10 @@ __global__ void set_ddphi_kernel(
300320
const int iw_l = atom_iw2_l[it_nw + iw];
301321
const int idx_ylm = atom_iw2_ylm [it_nw + iw];
302322
const double rl = pow_int(dist, iw_l);
303-
const double tmprl = tmp / rl;
304-
const double tmpdphi_rly = (dtmp - tmp * iw_l / dist) / rl * rly[idx_ylm] / dist;
323+
const double inv_rl = 1.0 / rl;
324+
const double tmprl = tmp * inv_rl;
325+
const double tmpdphi_rly = (dtmp * inv_dist - tmp * iw_l * inv_dist * inv_dist)
326+
* inv_rl * rly[idx_ylm];
305327

306328
double dphi[3];
307329
dphi[0] = tmpdphi_rly * coord[0] + tmprl * grly[idx_ylm * 3 + 0];
@@ -340,7 +362,7 @@ __global__ void set_ddphi_kernel(
340362
ddphi_zz[phi_idx + iw] -= dphi[2];
341363
}
342364
}
343-
coord[i/2] -= std::pow(-1, i%2) * 0.0001; // recover coord
365+
coord[i/2] -= eps; // recover coord
344366
}
345367

346368
for (int iw = 0; iw < atom_nw[atom_type]; iw++)
@@ -460,14 +482,14 @@ __global__ void phi_dot_dphi_kernel(
460482
const int* __restrict__ atom_nw,
461483
double* force)
462484
{
463-
__shared__ double s_data[32 * 3]; // the length of s_data equals the max warp num of a block times 3
485+
// NOTE: this kernel assumes blockDim.x == 32 (a single warp). If the launch
486+
// configuration is ever changed, the reduce below needs a shared-memory stage.
464487
const int bgrid_id = blockIdx.y;
465488
const int atoms_num = atoms_num_info[bgrid_id].x;
466489
const int pre_atoms_num = atoms_num_info[bgrid_id].y;
467490
const int b_phi_len = bgrid_phi_len[bgrid_id];
468491
const int tid = threadIdx.x;
469-
const int warp_id = tid / 32;
470-
const int lane_id = tid % 32;
492+
const int lane_id = tid; // blockDim.x == 32
471493

472494
for (int atom_id = blockIdx.x; atom_id < atoms_num; atom_id += gridDim.x)
473495
{
@@ -481,44 +503,23 @@ __global__ void phi_dot_dphi_kernel(
481503
for (int iw = tid; iw < nw; iw += blockDim.x)
482504
{
483505
int phi_idx = phi_start + iw;
484-
f[0] += phi[phi_idx] * dphi_x[phi_idx];
485-
f[1] += phi[phi_idx] * dphi_y[phi_idx];
486-
f[2] += phi[phi_idx] * dphi_z[phi_idx];
506+
const double p = phi[phi_idx];
507+
f[0] += p * dphi_x[phi_idx];
508+
f[1] += p * dphi_y[phi_idx];
509+
f[2] += p * dphi_z[phi_idx];
487510
}
488511
}
489512

490-
// reduce the force in each block
491-
for (int i = 0; i < 3; i++)
492-
{
493-
f[i] = warpReduceSum(f[i]);
494-
}
513+
// single-warp reduce
514+
f[0] = warpReduceSum(f[0]);
515+
f[1] = warpReduceSum(f[1]);
516+
f[2] = warpReduceSum(f[2]);
495517

496518
if (lane_id == 0)
497519
{
498-
for (int i = 0; i < 3; i++)
499-
{
500-
s_data[warp_id * 3 + i] = f[i];
501-
}
502-
}
503-
__syncthreads();
504-
505-
for (int i = 0; i < 3; i++)
506-
{
507-
f[i] = (tid < blockDim.x / 32) ? s_data[tid * 3 + i] : 0;
508-
}
509-
if (warp_id == 0)
510-
{
511-
for (int i = 0; i < 3; i++)
512-
{
513-
f[i] = warpReduceSum(f[i]);
514-
}
515-
}
516-
if (tid == 0)
517-
{
518-
for (int i = 0; i < 3; i++)
519-
{
520-
atomicAdd(&force[iat * 3 + i], f[i] * 2);
521-
}
520+
atomicAdd(&force[iat * 3 + 0], f[0] * 2);
521+
atomicAdd(&force[iat * 3 + 1], f[1] * 2);
522+
atomicAdd(&force[iat * 3 + 2], f[2] * 2);
522523
}
523524
}
524525
}
@@ -539,14 +540,14 @@ __global__ void phi_dot_dphi_r_kernel(
539540
const int* __restrict__ atom_nw,
540541
double* __restrict__ svl)
541542
{
542-
__shared__ double s_data[32 * 6]; // the length of s_data equals the max warp num of a block times 6
543+
// NOTE: this kernel assumes blockDim.x == 32 (a single warp). If the launch
544+
// configuration is ever changed, the reduce below needs a shared-memory stage.
543545
const int tid = threadIdx.x;
544546
const int bgrid_id = blockIdx.y;
545547
const int atoms_num = atoms_num_info[bgrid_id].x;
546548
const int pre_atoms_num = atoms_num_info[bgrid_id].y;
547549
const int b_phi_len = bgrid_phi_len[bgrid_id];
548-
const int warp_id = tid / 32;
549-
const int lane_id = tid % 32;
550+
const int lane_id = tid; // blockDim.x == 32
550551

551552
double stress[6]{0.0};
552553
for (int mgrid_id = blockIdx.x; mgrid_id < mgrids_per_bgrid; mgrid_id += gridDim.x)
@@ -564,44 +565,29 @@ __global__ void phi_dot_dphi_r_kernel(
564565
for (int iw = tid; iw < nw; iw += blockDim.x)
565566
{
566567
int phi_idx = phi_start + iw;
567-
stress[0] += phi[phi_idx] * dphi_x[phi_idx] * coord.x;
568-
stress[1] += phi[phi_idx] * dphi_x[phi_idx] * coord.y;
569-
stress[2] += phi[phi_idx] * dphi_x[phi_idx] * coord.z;
570-
stress[3] += phi[phi_idx] * dphi_y[phi_idx] * coord.y;
571-
stress[4] += phi[phi_idx] * dphi_y[phi_idx] * coord.z;
572-
stress[5] += phi[phi_idx] * dphi_z[phi_idx] * coord.z;
568+
const double p = phi[phi_idx];
569+
const double pdx = p * dphi_x[phi_idx];
570+
const double pdy = p * dphi_y[phi_idx];
571+
const double pdz = p * dphi_z[phi_idx];
572+
stress[0] += pdx * coord.x;
573+
stress[1] += pdx * coord.y;
574+
stress[2] += pdx * coord.z;
575+
stress[3] += pdy * coord.y;
576+
stress[4] += pdy * coord.z;
577+
stress[5] += pdz * coord.z;
573578
}
574579
}
575580
}
576581

577-
// reduce the stress in each block
582+
// single-warp reduce
583+
#pragma unroll
578584
for (int i = 0; i < 6; i++)
579585
{
580586
stress[i] = warpReduceSum(stress[i]);
581587
}
582-
583588
if (lane_id == 0)
584589
{
585-
for (int i = 0; i < 6; i++)
586-
{
587-
s_data[warp_id * 6 + i] = stress[i];
588-
}
589-
}
590-
__syncthreads();
591-
592-
for (int i = 0; i < 6; i++)
593-
{
594-
stress[i] = (tid < blockDim.x / 32) ? s_data[tid * 6 + i] : 0;
595-
}
596-
if (warp_id == 0)
597-
{
598-
for (int i = 0; i < 6; i++)
599-
{
600-
stress[i] = warpReduceSum(stress[i]);
601-
}
602-
}
603-
if (tid == 0)
604-
{
590+
#pragma unroll
605591
for (int i = 0; i < 6; i++)
606592
{
607593
atomicAdd(&svl[i], stress[i] * 2);

source/source_lcao/module_gint/kernel/phi_operator_kernel.cuh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ __global__ void set_phi_kernel(
2828
const int* __restrict__ bgrid_phi_len,
2929
Real* __restrict__ phi);
3030

31+
// WantPhi == false: skip phi[] writes entirely (callers like gint_tau pass nullptr).
32+
template<bool WantPhi>
3133
__global__ void set_phi_dphi_kernel(
3234
const int nwmax,
3335
const int mgrids_num,

0 commit comments

Comments
 (0)