Cache GSL in SyncPlan and apply async Sync to Z4c_class
Major optimization: Pre-build grid segment lists (GSLs) once per Step() call via SyncPreparePlan(), then reuse them across all 4 RK4 substep SyncBegin calls via SyncBeginWithPlan(). This eliminates the O(cpusize * blocks^2) GSL rebuild cost that was incurred on every ghost zone exchange. Applied async SyncBegin/SyncEnd overlap pattern to Z4c_class.C (ABEtype==2, the default configuration), which was still using blocking Parallel::Sync. Both the regular and CPBC variants of Z4c Step() are now optimized. Co-authored-by: copilot-swe-agent[bot] <198982749+copilot@users.noreply.github.com>
This commit is contained in:
@@ -3869,6 +3869,7 @@ Parallel::SyncHandle *Parallel::SyncBegin(Patch *Pat, MyList<var> *VarList, int
|
||||
ts->VarList1 = VarList;
|
||||
ts->VarList2 = VarList;
|
||||
ts->Symmetry = Symmetry;
|
||||
ts->owns_gsl = true;
|
||||
|
||||
ts->dst = build_ghost_gsl(Pat);
|
||||
ts->src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
@@ -3909,6 +3910,7 @@ Parallel::SyncHandle *Parallel::SyncBegin(MyList<Patch> *PatL, MyList<var> *VarL
|
||||
ts->VarList1 = VarList;
|
||||
ts->VarList2 = VarList;
|
||||
ts->Symmetry = Symmetry;
|
||||
ts->owns_gsl = true;
|
||||
|
||||
ts->dst = build_ghost_gsl(Pp->data);
|
||||
ts->src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
@@ -3933,6 +3935,7 @@ Parallel::SyncHandle *Parallel::SyncBegin(MyList<Patch> *PatL, MyList<var> *VarL
|
||||
ts->VarList1 = VarList;
|
||||
ts->VarList2 = VarList;
|
||||
ts->Symmetry = Symmetry;
|
||||
ts->owns_gsl = true;
|
||||
|
||||
ts->dst = build_buffer_gsl(PatL);
|
||||
ts->src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
@@ -3961,26 +3964,150 @@ void Parallel::SyncEnd(SyncHandle *handle)
|
||||
TransferState *ts = &handle->states[i];
|
||||
transfer_end(ts);
|
||||
|
||||
// Cleanup grid segment lists
|
||||
if (ts->dst)
|
||||
ts->dst->destroyList();
|
||||
for (int node = 0; node < ts->cpusize; node++)
|
||||
// Cleanup grid segment lists only if this state owns them
|
||||
if (ts->owns_gsl)
|
||||
{
|
||||
if (ts->src[node])
|
||||
ts->src[node]->destroyList();
|
||||
if (ts->transfer_src[node])
|
||||
ts->transfer_src[node]->destroyList();
|
||||
if (ts->transfer_dst[node])
|
||||
ts->transfer_dst[node]->destroyList();
|
||||
if (ts->dst)
|
||||
ts->dst->destroyList();
|
||||
for (int node = 0; node < ts->cpusize; node++)
|
||||
{
|
||||
if (ts->src[node])
|
||||
ts->src[node]->destroyList();
|
||||
if (ts->transfer_src[node])
|
||||
ts->transfer_src[node]->destroyList();
|
||||
if (ts->transfer_dst[node])
|
||||
ts->transfer_dst[node]->destroyList();
|
||||
}
|
||||
delete[] ts->src;
|
||||
delete[] ts->transfer_src;
|
||||
delete[] ts->transfer_dst;
|
||||
}
|
||||
delete[] ts->src;
|
||||
delete[] ts->transfer_src;
|
||||
delete[] ts->transfer_dst;
|
||||
}
|
||||
|
||||
delete[] handle->states;
|
||||
delete handle;
|
||||
}
|
||||
//
|
||||
// SyncPreparePlan: Pre-build grid segment lists for a patch list.
|
||||
// The plan can be reused across multiple SyncBeginWithPlan calls
|
||||
// as long as the mesh topology does not change (no regridding).
|
||||
//
|
||||
Parallel::SyncPlan *Parallel::SyncPreparePlan(MyList<Patch> *PatL, int Symmetry)
|
||||
{
|
||||
int cpusize;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &cpusize);
|
||||
|
||||
// Count patches
|
||||
int num_patches = 0;
|
||||
MyList<Patch> *Pp = PatL;
|
||||
while (Pp) { num_patches++; Pp = Pp->next; }
|
||||
|
||||
SyncPlan *plan = new SyncPlan;
|
||||
plan->num_entries = num_patches + 1; // intra-patch + 1 inter-patch
|
||||
plan->Symmetry = Symmetry;
|
||||
plan->entries = new SyncPlanEntry[plan->num_entries];
|
||||
|
||||
// Intra-patch entries: ghost zone exchange within each patch
|
||||
int idx = 0;
|
||||
Pp = PatL;
|
||||
while (Pp)
|
||||
{
|
||||
SyncPlanEntry *pe = &plan->entries[idx];
|
||||
pe->cpusize = cpusize;
|
||||
pe->dst = build_ghost_gsl(Pp->data);
|
||||
pe->src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
pe->transfer_src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
pe->transfer_dst = new MyList<Parallel::gridseg> *[cpusize];
|
||||
for (int node = 0; node < cpusize; node++)
|
||||
{
|
||||
pe->src[node] = build_owned_gsl0(Pp->data, node);
|
||||
build_gstl(pe->src[node], pe->dst, &pe->transfer_src[node], &pe->transfer_dst[node]);
|
||||
}
|
||||
idx++;
|
||||
Pp = Pp->next;
|
||||
}
|
||||
|
||||
// Inter-patch entry: buffer zone exchange between patches
|
||||
{
|
||||
SyncPlanEntry *pe = &plan->entries[idx];
|
||||
pe->cpusize = cpusize;
|
||||
pe->dst = build_buffer_gsl(PatL);
|
||||
pe->src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
pe->transfer_src = new MyList<Parallel::gridseg> *[cpusize];
|
||||
pe->transfer_dst = new MyList<Parallel::gridseg> *[cpusize];
|
||||
for (int node = 0; node < cpusize; node++)
|
||||
{
|
||||
pe->src[node] = build_owned_gsl(PatL, node, 5, Symmetry);
|
||||
build_gstl(pe->src[node], pe->dst, &pe->transfer_src[node], &pe->transfer_dst[node]);
|
||||
}
|
||||
}
|
||||
|
||||
return plan;
|
||||
}
|
||||
//
|
||||
void Parallel::SyncFreePlan(SyncPlan *plan)
|
||||
{
|
||||
if (!plan)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < plan->num_entries; i++)
|
||||
{
|
||||
SyncPlanEntry *pe = &plan->entries[i];
|
||||
if (pe->dst)
|
||||
pe->dst->destroyList();
|
||||
for (int node = 0; node < pe->cpusize; node++)
|
||||
{
|
||||
if (pe->src[node])
|
||||
pe->src[node]->destroyList();
|
||||
if (pe->transfer_src[node])
|
||||
pe->transfer_src[node]->destroyList();
|
||||
if (pe->transfer_dst[node])
|
||||
pe->transfer_dst[node]->destroyList();
|
||||
}
|
||||
delete[] pe->src;
|
||||
delete[] pe->transfer_src;
|
||||
delete[] pe->transfer_dst;
|
||||
}
|
||||
delete[] plan->entries;
|
||||
delete plan;
|
||||
}
|
||||
//
|
||||
// SyncBeginWithPlan: Use pre-built GSLs from a SyncPlan to initiate async transfer.
|
||||
// This avoids the O(cpusize * blocks^2) cost of rebuilding GSLs on every call.
|
||||
//
|
||||
Parallel::SyncHandle *Parallel::SyncBeginWithPlan(SyncPlan *plan, MyList<var> *VarList)
|
||||
{
|
||||
return SyncBeginWithPlan(plan, VarList, VarList);
|
||||
}
|
||||
//
|
||||
Parallel::SyncHandle *Parallel::SyncBeginWithPlan(SyncPlan *plan, MyList<var> *VarList1, MyList<var> *VarList2)
|
||||
{
|
||||
SyncHandle *handle = new SyncHandle;
|
||||
handle->num_states = plan->num_entries;
|
||||
handle->states = new TransferState[handle->num_states];
|
||||
|
||||
for (int i = 0; i < plan->num_entries; i++)
|
||||
{
|
||||
SyncPlanEntry *pe = &plan->entries[i];
|
||||
TransferState *ts = &handle->states[i];
|
||||
|
||||
ts->cpusize = pe->cpusize;
|
||||
ts->VarList1 = VarList1;
|
||||
ts->VarList2 = VarList2;
|
||||
ts->Symmetry = plan->Symmetry;
|
||||
ts->owns_gsl = false; // GSLs are owned by the plan, not this handle
|
||||
|
||||
// Borrow GSL pointers from the plan (do NOT free them in SyncEnd)
|
||||
ts->transfer_src = pe->transfer_src;
|
||||
ts->transfer_dst = pe->transfer_dst;
|
||||
ts->src = pe->src;
|
||||
ts->dst = pe->dst;
|
||||
|
||||
transfer_begin(ts);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
// collect buffer grid segments or blocks for the periodic boundary condition of given patch
|
||||
// ---------------------------------------------------
|
||||
// |con | |con |
|
||||
|
||||
Reference in New Issue
Block a user