mirror of https://gitee.com/openkylin/vtk9.git
651 lines
16 KiB
C++
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();
|
|
}
|