/*========================================================================= 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 #if !defined(_WIN32) #include #include #else #include #include #include 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 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 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(malloc(worldSize * sizeof(int))); disp = static_cast(malloc(worldSize * sizeof(int))); } int bufferSize = static_cast(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(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(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(wallt.tv_sec) + static_cast(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(wallt.tv_sec) + static_cast(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(); }