Skip to content

Commit 9244c47

Browse files
committed
Fix GPU memory MPI communication bugs for DeltaSpin/DFT+U
Three critical bugs fixed where GPU memory pointers were passed directly to MPI functions (MPI_Allreduce/MPI_Bcast), causing segfaults: 1. op_pw_nl.cpp: becp GPU memory in Nonlocal::act() - Add CPU buffer becp_h, d2h/h2d copy around MPI_Allreduce 2. diago_dav_subspace.cpp: hcc/scc GPU memory in cal_elem() - Add CPU buffers hcc_h/scc_h, d2h/h2d copy around MPI_Allreduce 3. diago_dav_subspace.cpp: vcc GPU memory in diag_zhegvx() - Add CPU buffer vcc_h for MPI_Bcast, d2h/h2d copy 4. elecstate_pw.cpp: becp GPU memory in cal_becsum() - Add CPU buffer becp_h, d2h/h2d copy around MPI_Allreduce All fixes use if constexpr to only apply GPU-specific code paths when Device is DEVICE_GPU, preserving CPU path behavior. Additionally, diag_zhegvx GPU path now falls back to CPU hegvx_op for robustness against ill-conditioned overlap matrices that caused 'heevd: failed to invert matrix' errors with GPU cusolver hegvd.
1 parent 9ba3829 commit 9244c47

6 files changed

Lines changed: 131 additions & 9 deletions

File tree

source/source_estate/elecstate_pw.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "elecstate_pw.h"
22

3+
#include <type_traits>
4+
35
#include "source_base/constants.h"
46
#include "source_base/libm/libm.h"
57
#include "source_base/math_ylmreal.h"
@@ -55,6 +57,10 @@ ElecStatePW<T, Device>::~ElecStatePW()
5557
}
5658
delmem_complex_op()(this->wfcr);
5759
delmem_complex_op()(this->wfcr_another_spin);
60+
if (this->becp_h != nullptr)
61+
{
62+
delmem_complex_h_op()(this->becp_h);
63+
}
5864
}
5965

6066
template<typename T, typename Device>
@@ -339,7 +345,26 @@ void ElecStatePW<T, Device>::cal_becsum(const psi::Psi<T, Device>& psi)
339345
becp,
340346
this->ppcell->nkb);
341347
}
342-
Parallel_Reduce::reduce_pool(becp, this->ppcell->nkb * nbands);
348+
// Copy becp from GPU to CPU for MPI reduction (only needed for GPU)
349+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
350+
{
351+
if (this->becp_h_size < this->ppcell->nkb * nbands)
352+
{
353+
if (this->becp_h != nullptr)
354+
{
355+
delmem_complex_h_op()(this->becp_h);
356+
}
357+
resmem_complex_h_op()(this->becp_h, this->ppcell->nkb * nbands, "ElecStatePW::becp_h");
358+
this->becp_h_size = this->ppcell->nkb * nbands;
359+
}
360+
syncmem_complex_d2h_op()(this->becp_h, becp, this->ppcell->nkb * nbands);
361+
Parallel_Reduce::reduce_pool(this->becp_h, this->ppcell->nkb * nbands);
362+
syncmem_complex_h2d_op()(becp, this->becp_h, this->ppcell->nkb * nbands);
363+
}
364+
else
365+
{
366+
Parallel_Reduce::reduce_pool(becp, this->ppcell->nkb * nbands);
367+
}
343368

344369
// sum over bands: \sum_i <psi_i|beta_l><beta_m|psi_i> w_i
345370
for (int it = 0; it < ucell->ntype; it++)

source/source_estate/elecstate_pw.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,16 @@ class ElecStatePW : public ElecState
100100
using setmem_complex_op = base_device::memory::set_memory_op<T, Device>;
101101
using resmem_complex_op = base_device::memory::resize_memory_op<T, Device>;
102102
using delmem_complex_op = base_device::memory::delete_memory_op<T, Device>;
103+
using resmem_complex_h_op = base_device::memory::resize_memory_op<T, base_device::DEVICE_CPU>;
104+
using delmem_complex_h_op = base_device::memory::delete_memory_op<T, base_device::DEVICE_CPU>;
105+
using syncmem_complex_d2h_op = base_device::memory::synchronize_memory_op<T, base_device::DEVICE_CPU, Device>;
106+
using syncmem_complex_h2d_op = base_device::memory::synchronize_memory_op<T, Device, base_device::DEVICE_CPU>;
103107

104108
using gemv_op = ModuleBase::gemv_op<T, Device>;
105109
using gemm_op = ModuleBase::gemm_op<T, Device>;
110+
111+
T* becp_h = nullptr;
112+
size_t becp_h_size = 0;
106113
};
107114

108115
} // namespace elecstate

source/source_hsolver/diago_dav_subspace.cpp

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "diago_dav_subspace.h"
22

3+
#include <type_traits>
4+
35
#include "diago_iter_assist.h"
46

57
#include "source_base/module_device/device.h"
@@ -103,6 +105,8 @@ Diago_DavSubspace<T, Device>::~Diago_DavSubspace()
103105
delmem_real_op()(this->d_precondition);
104106
delmem_complex_op()(this->d_scc);
105107
delmem_real_op()(this->d_eigenvalue);
108+
delmem_complex_h_op()(this->hcc_h);
109+
delmem_complex_h_op()(this->scc_h);
106110
}
107111
#endif
108112
}
@@ -585,8 +589,30 @@ void Diago_DavSubspace<T, Device>::cal_elem(const int& dim,
585589
mtfunc::dsp_dav_subspace_reduce(hcc, scc, nbase, this->nbase_x, this->notconv, this->diag_comm.comm);
586590
#else
587591
assert(this->diag_comm.comm == POOL_WORLD);
588-
Parallel_Reduce::reduce_pool(hcc + nbase * this->nbase_x, notconv * this->nbase_x);
589-
Parallel_Reduce::reduce_pool(scc + nbase * this->nbase_x, notconv * this->nbase_x);
592+
// For GPU, need to copy to CPU before MPI reduction
593+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
594+
{
595+
// Allocate CPU buffers if needed
596+
if (this->hcc_h == nullptr)
597+
{
598+
resmem_complex_h_op()(this->hcc_h, this->nbase_x * this->nbase_x, "DAV::hcc_h");
599+
resmem_complex_h_op()(this->scc_h, this->nbase_x * this->nbase_x, "DAV::scc_h");
600+
}
601+
// Copy from GPU to CPU
602+
syncmem_d2h_op()(this->hcc_h, hcc + nbase * this->nbase_x, notconv * this->nbase_x);
603+
syncmem_d2h_op()(this->scc_h, scc + nbase * this->nbase_x, notconv * this->nbase_x);
604+
// Reduce on CPU
605+
Parallel_Reduce::reduce_pool(this->hcc_h, notconv * this->nbase_x);
606+
Parallel_Reduce::reduce_pool(this->scc_h, notconv * this->nbase_x);
607+
// Copy back to GPU
608+
syncmem_h2d_op()(hcc + nbase * this->nbase_x, this->hcc_h, notconv * this->nbase_x);
609+
syncmem_h2d_op()(scc + nbase * this->nbase_x, this->scc_h, notconv * this->nbase_x);
610+
}
611+
else
612+
{
613+
Parallel_Reduce::reduce_pool(hcc + nbase * this->nbase_x, notconv * this->nbase_x);
614+
Parallel_Reduce::reduce_pool(scc + nbase * this->nbase_x, notconv * this->nbase_x);
615+
}
590616
#endif
591617
}
592618
#endif
@@ -615,9 +641,24 @@ void Diago_DavSubspace<T, Device>::diag_zhegvx(const int& nbase,
615641
#if defined(__CUDA) || defined(__ROCM)
616642
if (this->diag_comm.rank == 0)
617643
{
618-
syncmem_complex_op()(this->d_scc, scc, nbase * this->nbase_x);
619-
ct::kernels::lapack_hegvd<T, ct_Device>()(nbase, this->nbase_x, this->hcc, this->d_scc, this->d_eigenvalue, this->vcc);
620-
syncmem_var_d2h_op()((*eigenvalue_iter).data(), this->d_eigenvalue, this->nbase_x);
644+
// Copy hcc and scc from GPU to CPU for robust eigensolving
645+
std::vector<T> hcc_h(nbase * this->nbase_x, *this->zero);
646+
std::vector<T> scc_h(nbase * this->nbase_x, *this->zero);
647+
syncmem_d2h_op()(hcc_h.data(), this->hcc, nbase * this->nbase_x);
648+
syncmem_d2h_op()(scc_h.data(), scc, nbase * this->nbase_x);
649+
650+
// Solve on CPU using hegvx (more robust than hegvd for ill-conditioned overlap)
651+
std::vector<T> vcc_h(nbase * this->nbase_x, *this->zero);
652+
hegvx_op<T, base_device::DEVICE_CPU>()(this->cpu_ctx,
653+
nbase,
654+
this->nbase_x,
655+
hcc_h.data(),
656+
scc_h.data(),
657+
nband,
658+
(*eigenvalue_iter).data(),
659+
vcc_h.data());
660+
// Copy eigenvectors back to GPU
661+
syncmem_h2d_op()(this->vcc, vcc_h.data(), nbase * this->nbase_x);
621662
}
622663
#endif
623664
}
@@ -715,9 +756,25 @@ void Diago_DavSubspace<T, Device>::diag_zhegvx(const int& nbase,
715756
if (this->diag_comm.nproc > 1)
716757
{
717758
// vcc: nbase * nband
718-
for (int i = 0; i < nband; i++)
759+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
719760
{
720-
MPI_Bcast(&vcc[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
761+
// Copy vcc from GPU to CPU for MPI broadcast
762+
T* vcc_h = nullptr;
763+
resmem_complex_h_op()(vcc_h, nband * this->nbase_x, "DAV::vcc_h");
764+
syncmem_d2h_op()(vcc_h, vcc, nband * this->nbase_x);
765+
for (int i = 0; i < nband; i++)
766+
{
767+
MPI_Bcast(&vcc_h[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
768+
}
769+
syncmem_h2d_op()(vcc, vcc_h, nband * this->nbase_x);
770+
delmem_complex_h_op()(vcc_h);
771+
}
772+
else
773+
{
774+
for (int i = 0; i < nband; i++)
775+
{
776+
MPI_Bcast(&vcc[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
777+
}
721778
}
722779
MPI_Bcast((*eigenvalue_iter).data(), nband, MPI_DOUBLE, 0, this->diag_comm.comm);
723780
}

source/source_hsolver/diago_dav_subspace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,18 @@ class Diago_DavSubspace
190190
using syncmem_h2d_op = base_device::memory::synchronize_memory_op<T, Device, base_device::DEVICE_CPU>;
191191
using syncmem_d2h_op = base_device::memory::synchronize_memory_op<T, base_device::DEVICE_CPU, Device>;
192192

193+
using resmem_complex_h_op = base_device::memory::resize_memory_op<T, base_device::DEVICE_CPU>;
194+
using delmem_complex_h_op = base_device::memory::delete_memory_op<T, base_device::DEVICE_CPU>;
195+
193196
// Note that ct_Device is different from base_device!
194197
using ct_Device = typename ct::PsiToContainer<Device>::type;
195198
// using hegvd_op = container::kernels::lapack_hegvd<T, ct_Device>;
196199

197200
const T *one = nullptr, *zero = nullptr, *neg_one = nullptr;
198201
const T one_ = static_cast<T>(1.0), zero_ = static_cast<T>(0.0), neg_one_ = static_cast<T>(-1.0);
202+
203+
T* hcc_h = nullptr;
204+
T* scc_h = nullptr;
199205
};
200206

201207
} // namespace hsolver

source/source_pw/module_pwdft/op_pw_nl.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "op_pw_nl.h"
22

3+
#include <type_traits>
4+
35
#include "source_io/module_parameter/parameter.h"
46
#include "source_base/timer.h"
57
#include "source_base/parallel_reduce.h"
@@ -34,6 +36,7 @@ template<typename T, typename Device>
3436
Nonlocal<OperatorPW<T, Device>>::~Nonlocal() {
3537
delmem_complex_op()(this->ps);
3638
delmem_complex_op()(this->becp);
39+
delmem_complex_h_op()(this->becp_h);
3740
}
3841

3942
template<typename T, typename Device>
@@ -285,7 +288,25 @@ void Nonlocal<OperatorPW<T, Device>>::act(
285288
);
286289
}
287290

288-
Parallel_Reduce::reduce_pool(becp, nkb * nbands);
291+
// Copy becp from GPU to CPU for MPI reduction (only needed for GPU)
292+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
293+
{
294+
if (this->nkb_m_h < nkb * nbands)
295+
{
296+
resmem_complex_h_op()(this->becp_h, nkb * nbands, "Nonlocal<PW>::becp_h");
297+
this->nkb_m_h = nkb * nbands;
298+
}
299+
syncmem_complex_d2h_op()(this->becp_h, this->becp, nkb * nbands);
300+
301+
Parallel_Reduce::reduce_pool(becp_h, nkb * nbands);
302+
303+
// Copy back to GPU
304+
syncmem_complex_h2d_op()(this->becp, this->becp_h, nkb * nbands);
305+
}
306+
else
307+
{
308+
Parallel_Reduce::reduce_pool(becp, nkb * nbands);
309+
}
289310

290311
this->add_nonlocal_pp(tmhpsi, becp, nbands);
291312
}

source/source_pw/module_pwdft/op_pw_nl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,15 @@ class Nonlocal<OperatorPW<T, Device>> : public OperatorPW<T, Device>
9898
using delmem_complex_op = base_device::memory::delete_memory_op<T, Device>;
9999
#endif
100100
using syncmem_complex_h2d_op = base_device::memory::synchronize_memory_op<T, Device, base_device::DEVICE_CPU>;
101+
using syncmem_complex_d2h_op = base_device::memory::synchronize_memory_op<T, base_device::DEVICE_CPU, Device>;
102+
using resmem_complex_h_op = base_device::memory::resize_memory_op<T, base_device::DEVICE_CPU>;
103+
using delmem_complex_h_op = base_device::memory::delete_memory_op<T, base_device::DEVICE_CPU>;
101104

102105
T one{1, 0};
103106
T zero{0, 0};
107+
108+
mutable T *becp_h = nullptr;
109+
mutable size_t nkb_m_h = 0;
104110
};
105111

106112
} // namespace hamilt

0 commit comments

Comments
 (0)