192 lines
7.7 KiB
C++
192 lines
7.7 KiB
C++
//
|
|
// ********************************************************************
|
|
// * License and Disclaimer *
|
|
// * *
|
|
// * The Geant4 software is copyright of the Copyright Holders of *
|
|
// * the Geant4 Collaboration. It is provided under the terms and *
|
|
// * conditions of the Geant4 Software License, included in the file *
|
|
// * LICENSE and available at http://cern.ch/geant4/license . These *
|
|
// * include a list of copyright holders. *
|
|
// * *
|
|
// * Neither the authors of this software system, nor their employing *
|
|
// * institutes,nor the agencies providing financial support for this *
|
|
// * work make any representation or warranty, express or implied, *
|
|
// * regarding this software system or assume any liability for its *
|
|
// * use. Please see the license in the file LICENSE and URL above *
|
|
// * for the full disclaimer and the limitation of liability. *
|
|
// * *
|
|
// * This code implementation is the result of the scientific and *
|
|
// * technical work of the GEANT4 collaboration. *
|
|
// * By using, copying, modifying or distributing the software (or *
|
|
// * any work based on the software) you agree to acknowledge its *
|
|
// * use in resulting scientific publications, and indicate your *
|
|
// * acceptance of all terms of the Geant4 Software license. *
|
|
// ********************************************************************
|
|
//
|
|
#include "G4VUserMPIrunMerger.hh"
|
|
#include <mpi.h>
|
|
#include <assert.h>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include "G4MPIutils.hh"
|
|
|
|
G4VUserMPIrunMerger::G4VUserMPIrunMerger( const G4Run* aRun ,
|
|
G4int destination ,
|
|
G4int ver) :
|
|
outputBuffer(nullptr),outputBufferSize(0),outputBufferPosition(0),
|
|
ownsBuffer(false),
|
|
destinationRank(destination),
|
|
run(const_cast<G4Run*>(aRun)),
|
|
commSize(0),
|
|
verbose(ver),
|
|
bytesSent(0) {}
|
|
|
|
#define DMSG( LVL , MSG ) { if ( verbose > LVL ) { G4cout << MSG << G4endl; } }
|
|
|
|
void G4VUserMPIrunMerger::Send(const unsigned int destination)
|
|
{
|
|
assert(run!=nullptr);
|
|
G4int nevts = run->GetNumberOfEvent();
|
|
DMSG( 1 , "G4VUserMPIrunMerger::Send() : Sending a G4run ("
|
|
<<run<<") with "<<nevts<<" events to: "<<destination);
|
|
input_userdata.clear();
|
|
Pack();//User code
|
|
InputUserData(&nevts,MPI::INT,1);
|
|
|
|
DestroyBuffer();
|
|
G4int newbuffsize = 0;
|
|
for ( const const_registered_data& el : input_userdata ) {
|
|
newbuffsize += (el.dt.Get_size()*el.count);
|
|
}
|
|
char* buffer = new char[newbuffsize];
|
|
//Avoid complains from valgrind (i'm not really sure why this is needed, but, beside the
|
|
//small cpu penalty, we can live with that).)
|
|
std::fill(buffer,buffer+newbuffsize,0);
|
|
ownsBuffer=true;
|
|
SetupOutputBuffer(buffer,newbuffsize,0);
|
|
DMSG(3,"Buffer size: "<<newbuffsize<<" bytes at: "<<(void*)outputBuffer);
|
|
|
|
//Now userdata contains all data to be send, do the real packing
|
|
for ( const const_registered_data& el : input_userdata ) {
|
|
#ifdef G4MPI_USE_MPI_PACK_NOT_CONST
|
|
MPI_Pack(const_cast<void*>(el.p_data),el.count,el.dt,
|
|
#else
|
|
MPI_Pack(el.p_data,el.count,el.dt,
|
|
#endif
|
|
outputBuffer,outputBufferSize,
|
|
&outputBufferPosition,COMM_G4COMMAND_);
|
|
}
|
|
assert(outputBufferSize==outputBufferPosition);
|
|
COMM_G4COMMAND_.Send(outputBuffer , outputBufferSize , MPI::PACKED ,
|
|
destination , G4MPImanager::kTAG_RUN);
|
|
bytesSent+=outputBufferSize;
|
|
DMSG(2 , "G4VUserMPIrunMerger::Send() : Done ");
|
|
}
|
|
|
|
|
|
void G4VUserMPIrunMerger::Receive(const unsigned int source)
|
|
{
|
|
DMSG( 1 , "G4VUserMPIrunMerger::Receive(...) , this rank : "
|
|
<<MPI::COMM_WORLD.Get_rank()<<" and receiving from : "<<source);
|
|
//DestroyBuffer();
|
|
//Receive from all but one
|
|
//for (G4int rank = 0; rank < commSize-1; ++rank)
|
|
//{
|
|
MPI::Status status;
|
|
COMM_G4COMMAND_.Probe(source, G4MPImanager::kTAG_RUN, status);
|
|
//const G4int source = status.Get_source();
|
|
const G4int newbuffsize = status.Get_count(MPI::PACKED);
|
|
DMSG(2,"Preparing to receive buffer of size: "<<newbuffsize);
|
|
char* buffer = outputBuffer;
|
|
if ( newbuffsize > outputBufferSize ) {
|
|
DMSG(3,"New larger buffer expected, resize");
|
|
//New larger buffer incoming, recreate buffer
|
|
delete[] outputBuffer;
|
|
buffer = new char[newbuffsize];
|
|
//Avoid complains from valgrind (i'm not really sure why this is needed, but, beside the
|
|
//small cpu penalty, we can live with that).)
|
|
std::fill(buffer,buffer+newbuffsize,0);
|
|
ownsBuffer = true;
|
|
}
|
|
SetupOutputBuffer(buffer,newbuffsize,0);
|
|
COMM_G4COMMAND_.Recv(buffer, newbuffsize, MPI::PACKED,source,
|
|
G4MPImanager::kTAG_RUN, status);
|
|
DMSG(3,"Buffer Size: "<<outputBufferSize<< " bytes at: "<<(void*)outputBuffer);
|
|
output_userdata.clear();
|
|
//User code, if implemented will return the concrete G4Run class
|
|
G4Run* aNewRun = UnPack();
|
|
if ( aNewRun == nullptr ) aNewRun = new G4Run;
|
|
//Add number of events counter
|
|
G4int nevets = 0;
|
|
OutputUserData(&nevets,MPI::INT,1);
|
|
//now userdata contains all data references, do the real unpacking
|
|
for ( const registered_data& el : output_userdata ) {
|
|
MPI_Unpack(outputBuffer,outputBufferSize,&outputBufferPosition,
|
|
el.p_data,el.count,el.dt,COMM_G4COMMAND_);
|
|
}
|
|
for ( G4int i = 0 ; i<nevets ; ++i ) aNewRun->RecordEvent(nullptr);
|
|
|
|
//Now merge received MPI run with global one
|
|
DMSG(2,"Before G4Run::Merge : "<<run->GetNumberOfEvent());
|
|
run->Merge( aNewRun );
|
|
DMSG(2,"After G4Run::Merge : "<<run->GetNumberOfEvent());
|
|
delete aNewRun;
|
|
//}
|
|
}
|
|
|
|
void G4VUserMPIrunMerger::Merge()
|
|
{
|
|
DMSG(0, "G4VUserMPIrunMerger::Merge called");
|
|
const unsigned int myrank = MPI::COMM_WORLD.Get_rank();
|
|
commSize = MPI::COMM_WORLD.Get_size();
|
|
if ( commSize == 1 ) {
|
|
DMSG(1,"Comm world size is 1, nothing to do");
|
|
return;
|
|
}
|
|
COMM_G4COMMAND_ = MPI::COMM_WORLD.Dup();
|
|
bytesSent = 0;
|
|
const G4double sttime = MPI::Wtime();
|
|
|
|
//Use G4MPIutils to optimize communications between ranks
|
|
typedef std::function<void(unsigned int)> handler_t;
|
|
using std::placeholders::_1;
|
|
handler_t sender = std::bind(&G4VUserMPIrunMerger::Send , this , _1);
|
|
handler_t receiver = std::bind(&G4VUserMPIrunMerger::Receive, this, _1);
|
|
std::function<void(void)> barrier =
|
|
std::bind(&MPI::Intracomm::Barrier,&COMM_G4COMMAND_);
|
|
G4mpi::Merge( sender , receiver , barrier , commSize , myrank );
|
|
|
|
//OLD Style p2p communications
|
|
/*
|
|
if ( myrank != destinationRank ) {
|
|
DMSG(0,"Comm world size: "<<commSize<<" this rank is: "
|
|
<<myrank<<" sending to rank "<<destinationRank);
|
|
Send(destinationRank);
|
|
} else {
|
|
DMSG(1,"Comm world size: "<<commSize<<" this rank is: "
|
|
<<myrank<<" receiving. ");
|
|
for ( unsigned int i = 0 ; i<commSize ; ++i) {
|
|
if ( i != myrank ) Receive(i);
|
|
}
|
|
}
|
|
*/
|
|
|
|
const G4double elapsed = MPI::Wtime() - sttime;
|
|
long total=0;
|
|
COMM_G4COMMAND_.Reduce(&bytesSent,&total,1,MPI::LONG,MPI::SUM,
|
|
destinationRank);
|
|
if ( verbose > 0 && myrank == destinationRank ) {
|
|
//Collect from ranks how much data was sent around
|
|
G4cout<<"G4VUserMPIrunMerger::Merge() - data transfer performances: "
|
|
<<double(total)/1000./elapsed<<" kB/s"
|
|
<<" (Total Data Transfer= "<<double(total)/1000<<" kB in "
|
|
<<elapsed<<" s)."<<G4endl;
|
|
}
|
|
|
|
|
|
|
|
COMM_G4COMMAND_.Free();
|
|
DMSG(0,"G4VUserMPIrunMerger::Merge done");
|
|
}
|
|
|