vtk9/Rendering/ParallelLIC/vtkParallelTimer.cxx

651 lines
16 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkParallelTimer.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkParallelTimer.h"
#if defined(_WIN32)
// The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name
#pragma warning(disable : 4996)
#endif
#include "vtkMPI.h"
#include "vtkObjectFactory.h"
#include "vtksys/FStream.hxx"
using std::cerr;
using std::endl;
using std::ostringstream;
using std::string;
using std::vector;
#include <ctime>
#if !defined(_WIN32)
#include <sys/time.h>
#include <unistd.h>
#else
#include <Winsock2.h>
#include <ctime>
#include <process.h>
static int gettimeofday(struct timeval* tv, void*)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
__int64 tmpres = 0;
tmpres = ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/*converting file time to unix epoch*/
const __int64 DELTA_EPOCH_IN_MICROSECS = 11644473600000000;
tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tv->tv_sec = (__int32)(tmpres * 0.000001);
tv->tv_usec = (tmpres % 1000000);
return 0;
}
#endif
using std::ios_base;
/*
For singleton pattern
**/
vtkParallelTimer* vtkParallelTimer::GlobalInstance = nullptr;
vtkParallelTimer::vtkParallelTimerDestructor vtkParallelTimer::GlobalInstanceDestructor;
//------------------------------------------------------------------------------
vtkParallelTimer::vtkParallelTimerDestructor::~vtkParallelTimerDestructor()
{
if (this->Log)
{
this->Log->Delete();
this->Log = nullptr;
}
}
// .NAME vtkParallelTimerBuffer -- A parallel buffer
//
// .SECTION Description
// A parallel buffer for logging events and other data during an MPI
// run. This is an implementation class you should not use it directly.
// Use vtkParallelTimer instead.
class vtkParallelTimerBuffer
{
public:
vtkParallelTimerBuffer();
~vtkParallelTimerBuffer();
vtkParallelTimerBuffer(const vtkParallelTimerBuffer& other);
vtkParallelTimerBuffer& operator=(const vtkParallelTimerBuffer& other);
// Description:
// Access state and internal data.
const char* GetData() const { return this->Data; }
char* GetData() { return this->Data; }
size_t GetSize() const { return this->At; }
size_t GetCapacity() const { return this->Size; }
// Description:
// Clear the buffer but don't release memory.
void Clear() { this->At = 0; }
// Description:
// Clear the buffer and release all resources.
void ClearForReal();
// Description:
// Stream insertion operators for adding data to the buffer.
vtkParallelTimerBuffer& operator<<(int v);
vtkParallelTimerBuffer& operator<<(long long v);
vtkParallelTimerBuffer& operator<<(double v);
vtkParallelTimerBuffer& operator<<(const char* v);
template <size_t N>
vtkParallelTimerBuffer& operator<<(const char v[N]);
// Description:
// Stream extraction operator for getting formatted data out.
vtkParallelTimerBuffer& operator>>(std::ostringstream& s);
// Description:
// Gather buffer to a root process. This is a collective
// operation.
void Gather(int rootRank);
protected:
// Description:
// Push n bytes onto the buffer, resizing if necessary.
void PushBack(const void* data, size_t n);
// Description:
// resize to at least newSize bytes.
void Resize(size_t newSize);
private:
size_t Size;
size_t At;
size_t GrowBy;
char* Data;
};
//------------------------------------------------------------------------------
template <size_t N>
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator<<(const char v[N])
{
const char c = 's';
this->PushBack(&c, 1);
this->PushBack(&v[0], N);
return *this;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer::vtkParallelTimerBuffer()
: Size(0)
, At(0)
, GrowBy(4096)
, Data(nullptr)
{
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer::~vtkParallelTimerBuffer()
{
free(this->Data);
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer::vtkParallelTimerBuffer(const vtkParallelTimerBuffer& other)
: Size(0)
, At(0)
, GrowBy(4096)
, Data(nullptr)
{
*this = other;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator=(const vtkParallelTimerBuffer& other)
{
if (this == &other)
{
return *this;
}
this->Clear();
this->Resize(other.GetSize());
memcpy(this->Data, other.Data, other.GetSize());
return *this;
}
//------------------------------------------------------------------------------
void vtkParallelTimerBuffer::ClearForReal()
{
this->At = 0;
this->Size = 0;
free(this->Data);
this->Data = nullptr;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator<<(int v)
{
const char c = 'i';
this->PushBack(&c, 1);
this->PushBack(&v, sizeof(int));
return *this;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator<<(long long v)
{
const char c = 'l';
this->PushBack(&c, 1);
this->PushBack(&v, sizeof(long long));
return *this;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator<<(double v)
{
const char c = 'd';
this->PushBack(&c, 1);
this->PushBack(&v, sizeof(double));
return *this;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator<<(const char* v)
{
const char c = 's';
this->PushBack(&c, 1);
size_t n = strlen(v) + 1;
this->PushBack(v, n);
return *this;
}
//------------------------------------------------------------------------------
vtkParallelTimerBuffer& vtkParallelTimerBuffer::operator>>(ostringstream& s)
{
size_t i = 0;
while (i < this->At)
{
char c = this->Data[i];
++i;
switch (c)
{
case 'i':
{
int temp;
size_t n = sizeof(temp);
memcpy(&temp, this->Data + i, n);
s << temp;
i += n;
}
break;
case 'l':
{
long long temp;
size_t n = sizeof(temp);
memcpy(&temp, this->Data + i, n);
s << temp;
i += n;
}
break;
case 'd':
{
double temp;
size_t n = sizeof(temp);
memcpy(&temp, this->Data + i, n);
s << temp;
i += n;
}
break;
case 's':
{
s << this->Data + i;
size_t n = strlen(this->Data + i) + 1;
i += n;
}
break;
default:
cerr << "Bad case at " << i - 1 << " " << c << ", " << (int)c;
return *this;
}
}
return *this;
}
//------------------------------------------------------------------------------
void vtkParallelTimerBuffer::Gather(int rootRank)
{
int mpiOk;
MPI_Initialized(&mpiOk);
if (!mpiOk)
{
return;
}
int worldRank;
MPI_Comm_rank(MPI_COMM_WORLD, &worldRank);
int worldSize;
MPI_Comm_size(MPI_COMM_WORLD, &worldSize);
// in serial this is a no-op
if (worldSize > 1)
{
int* bufferSizes = nullptr;
int* disp = nullptr;
if (worldRank == rootRank)
{
bufferSizes = static_cast<int*>(malloc(worldSize * sizeof(int)));
disp = static_cast<int*>(malloc(worldSize * sizeof(int)));
}
int bufferSize = static_cast<int>(this->GetSize());
MPI_Gather(&bufferSize, 1, MPI_INT, bufferSizes, 1, MPI_INT, rootRank, MPI_COMM_WORLD);
char* log = nullptr;
int cumSize = 0;
if (worldRank == rootRank)
{
for (int i = 0; i < worldSize; ++i)
{
disp[i] = cumSize;
cumSize += bufferSizes[i];
}
log = static_cast<char*>(malloc(cumSize));
}
MPI_Gatherv(
this->Data, bufferSize, MPI_CHAR, log, bufferSizes, disp, MPI_CHAR, rootRank, MPI_COMM_WORLD);
if (worldRank == rootRank)
{
this->Clear();
this->PushBack(log, cumSize);
free(bufferSizes);
free(disp);
free(log);
}
else
{
this->Clear();
}
}
}
//------------------------------------------------------------------------------
void vtkParallelTimerBuffer::PushBack(const void* data, size_t n)
{
size_t nextAt = this->At + n;
this->Resize(nextAt);
memcpy(this->Data + this->At, data, n);
this->At = nextAt;
}
//------------------------------------------------------------------------------
void vtkParallelTimerBuffer::Resize(size_t newSize)
{
#if defined(vtkParallelTimerBufferDEBUG)
size_t oldSize = this->Size;
#endif
if (newSize <= this->Size)
{
return;
}
while (this->Size < newSize)
{
this->Size += this->GrowBy;
}
this->Data = static_cast<char*>(realloc(this->Data, this->Size));
#if defined(vtkParallelTimerBufferDEBUG)
memset(this->Data + oldSize, -1, this->Size - oldSize);
#endif
}
//------------------------------------------------------------------------------
vtkStandardNewMacro(vtkParallelTimer);
//------------------------------------------------------------------------------
vtkParallelTimer::vtkParallelTimer()
: GlobalLevel(0)
, WorldRank(0)
, WriterRank(0)
, FileName(nullptr)
, WriteOnClose(0)
, Log(nullptr)
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::vtkParallelTimer" << endl;
#endif
MPI_Initialized(&this->Initialized);
if (this->Initialized)
{
MPI_Comm_rank(MPI_COMM_WORLD, &this->WorldRank);
}
this->StartTime.reserve(256);
this->Log = new vtkParallelTimerBuffer;
}
//------------------------------------------------------------------------------
vtkParallelTimer::~vtkParallelTimer()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::~vtkParallelTimer" << endl;
#endif
// Alert the user that he left events on the stack,
// this is usually a sign of trouble.
if (!this->StartTime.empty())
{
vtkErrorMacro(<< "Start time stack has " << this->StartTime.size() << " remaining.");
}
#if vtkParallelTimerDEBUG < 0
if (!this->EventId.empty())
{
size_t nIds = this->EventId.size();
vtkErrorMacro(<< "Event id stack has " << nIds << " remaining.");
for (size_t i = 0; i < nIds; ++i)
{
cerr << "EventId[" << i << "]=" << this->EventId[i] << endl;
}
}
#endif
this->SetFileName(nullptr);
delete this->Log;
}
//------------------------------------------------------------------------------
vtkParallelTimer* vtkParallelTimer::GetGlobalInstance()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::GetGlobalInstance" << endl;
#endif
if (vtkParallelTimer::GlobalInstance == nullptr)
{
vtkParallelTimer* log = vtkParallelTimer::New();
ostringstream oss;
#ifdef _WIN32
oss << GetCurrentProcessId() << ".log";
#else
oss << getpid() << ".log";
#endif
log->SetFileName(oss.str().c_str());
vtkParallelTimer::GlobalInstance = log;
vtkParallelTimer::GlobalInstanceDestructor.SetLog(log);
}
return vtkParallelTimer::GlobalInstance;
}
//------------------------------------------------------------------------------
void vtkParallelTimer::DeleteGlobalInstance()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::GetGlobalInstance" << endl;
#endif
if (vtkParallelTimer::GlobalInstance)
{
vtkParallelTimer::GlobalInstance->Delete();
vtkParallelTimer::GlobalInstance = nullptr;
vtkParallelTimer::GlobalInstanceDestructor.SetLog(nullptr);
}
}
//------------------------------------------------------------------------------
void vtkParallelTimer::Clear()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::Clear" << endl;
#endif
this->Log->Clear();
this->HeaderBuffer.str("");
}
//------------------------------------------------------------------------------
void vtkParallelTimer::StartEvent(int rank, const char* event)
{
#if vtkParallelTimerDEBUG > 2
cerr << "=====vtkParallelTimer::StartEvent" << endl;
#endif
if (this->WorldRank != rank)
{
return;
}
this->StartEvent(event);
}
//------------------------------------------------------------------------------
void vtkParallelTimer::StartEvent(const char* event)
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::StartEvent" << endl;
#endif
timeval wallt;
gettimeofday(&wallt, nullptr);
double walls = static_cast<double>(wallt.tv_sec) + static_cast<double>(wallt.tv_usec) / 1.0E6;
#if vtkParallelTimerDEBUG < 0
this->EventId.emplace_back(event);
#endif
this->StartTime.push_back(walls);
}
//------------------------------------------------------------------------------
void vtkParallelTimer::EndEvent(int rank, const char* event)
{
#if vtkParallelTimerDEBUG > 2
cerr << "=====vtkParallelTimer::EndEvent" << endl;
#endif
if (this->WorldRank != rank)
{
return;
}
this->EndEvent(event);
}
//------------------------------------------------------------------------------
void vtkParallelTimer::EndEvent(const char* event)
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::EndEvent" << endl;
#endif
timeval wallt;
gettimeofday(&wallt, nullptr);
double walle = static_cast<double>(wallt.tv_sec) + static_cast<double>(wallt.tv_usec) / 1.0E6;
#if vtkParallelTimerDEBUG > 0
if (this->StartTime.empty())
{
vtkErrorMacro("No event to end! " << event);
return;
}
#endif
double walls = this->StartTime.back();
this->StartTime.pop_back();
*this->Log << this->WorldRank << " " << event << " " << walls << " " << walle << " "
<< walle - walls << "\n";
#if vtkParallelTimerDEBUG < 0
const string& sEventId = this->EventId.back();
const string eEventId = event;
if (sEventId != eEventId)
{
vtkErrorMacro(<< "Event mismatch " << sEventId.c_str() << " != " << eEventId.c_str());
}
this->EventId.pop_back();
#endif
}
//------------------------------------------------------------------------------
void vtkParallelTimer::EndEventSynch(int rank, const char* event)
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::EndEventSynch" << endl;
#endif
if (this->Initialized)
{
MPI_Barrier(MPI_COMM_WORLD);
}
if (this->WorldRank != rank)
{
return;
}
this->EndEvent(event);
}
//------------------------------------------------------------------------------
void vtkParallelTimer::EndEventSynch(const char* event)
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::EndEventSynch" << endl;
#endif
if (this->Initialized)
{
MPI_Barrier(MPI_COMM_WORLD);
}
this->EndEvent(event);
}
//------------------------------------------------------------------------------
void vtkParallelTimer::Update()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::Update" << endl;
#endif
if (this->Initialized)
{
this->Log->Gather(this->WriterRank);
}
}
//------------------------------------------------------------------------------
int vtkParallelTimer::Write()
{
#if vtkParallelTimerDEBUG > 1
cerr << "=====vtkParallelTimer::Write" << endl;
#endif
if ((this->WorldRank == this->WriterRank) && this->Log->GetSize())
{
cerr << "Wrote " << this->FileName << endl;
ostringstream oss;
*this->Log >> oss;
vtksys::ofstream f(this->FileName, ios_base::out | ios_base::app);
if (!f.good())
{
vtkErrorMacro(<< "Failed to open " << this->FileName << " for writing.");
return -1;
}
time_t t;
time(&t);
f << "# " << ctime(&t) << this->HeaderBuffer.str() << oss.str();
f.close();
}
return 0;
}
//------------------------------------------------------------------------------
void vtkParallelTimer::PrintSelf(ostream& os, vtkIndent)
{
time_t t;
time(&t);
os << "# " << ctime(&t);
if (this->WorldRank == this->WriterRank)
{
os << this->HeaderBuffer.str();
}
ostringstream oss;
*this->Log >> oss;
os << oss.str();
}