diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp index 30fa9bebc54..17fea8b93c8 100644 --- a/lib/Support/Timer.cpp +++ b/lib/Support/Timer.cpp @@ -15,6 +15,12 @@ #include #include +// getNumBytesToNotCount - This function is supposed to return the number of +// bytes that are to be considered not allocated, even though malloc thinks they +// are allocated. +// +static unsigned getNumBytesToNotCount(); + static TimerGroup *DefaultTimerGroup = 0; static TimerGroup *getDefaultTimerGroup() { if (DefaultTimerGroup) return DefaultTimerGroup; @@ -22,13 +28,13 @@ static TimerGroup *getDefaultTimerGroup() { } Timer::Timer(const std::string &N) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), Name(N), + : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), Started(false), TG(getDefaultTimerGroup()) { TG->addTimer(); } Timer::Timer(const std::string &N, TimerGroup &tg) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), Name(N), + : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), Started(false), TG(&tg) { TG->addTimer(); } @@ -58,42 +64,70 @@ Timer::~Timer() { } } +static long getMemUsage() { + struct mallinfo MI = mallinfo(); + return MI.uordblks/*+MI.hblkhd-getNumBytesToNotCount()*/; +} + struct TimeRecord { double Elapsed, UserTime, SystemTime; long MemUsed; }; -static TimeRecord getTimeRecord() { +static TimeRecord getTimeRecord(bool Start) { struct rusage RU; struct timeval T; + long MemUsed; + if (Start) { + MemUsed = getMemUsage(); + if (getrusage(RUSAGE_SELF, &RU)) + perror("getrusage call failed: -time-passes info incorrect!"); + } gettimeofday(&T, 0); - if (getrusage(RUSAGE_SELF, &RU)) { - perror("getrusage call failed: -time-passes info incorrect!"); + + if (!Start) { + MemUsed = getMemUsage(); + if (getrusage(RUSAGE_SELF, &RU)) + perror("getrusage call failed: -time-passes info incorrect!"); } TimeRecord Result; Result.Elapsed = T.tv_sec + T.tv_usec/1000000.0; Result.UserTime = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0; Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0; - Result.MemUsed = mallinfo().uordblks; + Result.MemUsed = MemUsed; + return Result; } +static std::vector ActiveTimers; + void Timer::startTimer() { Started = true; - TimeRecord TR = getTimeRecord(); + TimeRecord TR = getTimeRecord(true); Elapsed -= TR.Elapsed; UserTime -= TR.UserTime; SystemTime -= TR.SystemTime; MemUsed -= TR.MemUsed; + PeakMemBase = TR.MemUsed; + ActiveTimers.push_back(this); } void Timer::stopTimer() { - TimeRecord TR = getTimeRecord(); + TimeRecord TR = getTimeRecord(false); Elapsed += TR.Elapsed; UserTime += TR.UserTime; SystemTime += TR.SystemTime; MemUsed += TR.MemUsed; + + if (ActiveTimers.back() == this) { + ActiveTimers.pop_back(); + } else { + std::vector::iterator I = + std::find(ActiveTimers.begin(), ActiveTimers.end(), this); + assert(I != ActiveTimers.end() && "stop but no startTimer?"); + ActiveTimers.erase(I); + } } void Timer::sum(const Timer &T) { @@ -101,8 +135,22 @@ void Timer::sum(const Timer &T) { UserTime += T.UserTime; SystemTime += T.SystemTime; MemUsed += T.MemUsed; + PeakMem += T.PeakMem; } +/// addPeakMemoryMeasurement - This method should be called whenever memory +/// usage needs to be checked. It adds a peak memory measurement to the +/// currently active timers, which will be printed when the timer group prints +/// +void Timer::addPeakMemoryMeasurement() { + long MemUsed = getMemUsage(); + + for (std::vector::iterator I = ActiveTimers.begin(), + E = ActiveTimers.end(); I != E; ++I) + (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase); +} + + //===----------------------------------------------------------------------===// // TimerGroup Implementation //===----------------------------------------------------------------------===// @@ -127,6 +175,12 @@ void Timer::print(const Timer &Total) { if (Total.MemUsed) fprintf(stderr, " %8ld ", MemUsed); + if (Total.PeakMem) { + if (PeakMem) + fprintf(stderr, " %8ld ", PeakMem); + else + fprintf(stderr, " "); + } std::cerr << Name << "\n"; Started = false; // Once printed, don't print again @@ -168,6 +222,8 @@ void TimerGroup::removeTimer() { std::cerr << " ---Wall Time---"; if (Total.getMemUsed()) std::cerr << " ---Mem---"; + if (Total.getPeakMem()) + std::cerr << " -PeakMem-"; std::cerr << " --- Name ---\n"; // Loop through all of the timing data, printing it out... @@ -188,3 +244,60 @@ void TimerGroup::removeTimer() { DefaultTimerGroup = 0; } } + + + +#if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && (__GNUC_PATCHLEVEL__ == 0) +// If we have GCC 3.2.0, we can calculate pool allocation bookkeeping info +#define HAVE_POOL +extern "C" { + // Taken from GCC 3.2's stl_alloc.h file: + enum {_ALIGN = 8, _MAX_BYTES = 128, NFREE = _MAX_BYTES / _ALIGN}; + struct FreeList { FreeList *Next; }; + + FreeList *_ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE[NFREE]; + char *_ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; + char *_ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; + size_t _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; + + // Make the symbols possible to use... + FreeList* (&TheFreeList)[NFREE] = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE; + char * &StartFree = _ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; + char * &EndFree = _ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; + size_t &HeapSize = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; +} +#endif + +// getNumBytesToNotCount - This function is supposed to return the number of +// bytes that are to be considered not allocated, even though malloc thinks they +// are allocated. +// +static unsigned getNumBytesToNotCount() { +#ifdef HAVE_POOL + // If we have GCC 3.2.0, we can subtract off pool allocation bookkeeping info + + // Size of the free slab section... + unsigned FreePoolMem = (unsigned)(EndFree-StartFree); + + // Walk all of the free lists, adding memory to the free counter whenever we + // have a free bucket. + for (unsigned i = 0; i != NFREE; ++i) { + unsigned NumEntries = 0; + for (FreeList *FL = TheFreeList[i]; FL; ++NumEntries, FL = FL->Next) + /*empty*/ ; + +#if 0 + if (NumEntries) + std::cerr << " For Size[" << (i+1)*_ALIGN << "]: " << NumEntries + << " Free entries\n"; +#endif + FreePoolMem += NumEntries*(i+1)*_ALIGN; + } + return FreePoolMem; + +#else +#warning "Don't know how to avoid pool allocation accounting overhead for this" +#warning " compiler: Space usage numbers (with -time-passes) may be off!" + return 0; +#endif +} diff --git a/support/lib/Support/Timer.cpp b/support/lib/Support/Timer.cpp index 30fa9bebc54..17fea8b93c8 100644 --- a/support/lib/Support/Timer.cpp +++ b/support/lib/Support/Timer.cpp @@ -15,6 +15,12 @@ #include #include +// getNumBytesToNotCount - This function is supposed to return the number of +// bytes that are to be considered not allocated, even though malloc thinks they +// are allocated. +// +static unsigned getNumBytesToNotCount(); + static TimerGroup *DefaultTimerGroup = 0; static TimerGroup *getDefaultTimerGroup() { if (DefaultTimerGroup) return DefaultTimerGroup; @@ -22,13 +28,13 @@ static TimerGroup *getDefaultTimerGroup() { } Timer::Timer(const std::string &N) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), Name(N), + : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), Started(false), TG(getDefaultTimerGroup()) { TG->addTimer(); } Timer::Timer(const std::string &N, TimerGroup &tg) - : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), Name(N), + : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), Started(false), TG(&tg) { TG->addTimer(); } @@ -58,42 +64,70 @@ Timer::~Timer() { } } +static long getMemUsage() { + struct mallinfo MI = mallinfo(); + return MI.uordblks/*+MI.hblkhd-getNumBytesToNotCount()*/; +} + struct TimeRecord { double Elapsed, UserTime, SystemTime; long MemUsed; }; -static TimeRecord getTimeRecord() { +static TimeRecord getTimeRecord(bool Start) { struct rusage RU; struct timeval T; + long MemUsed; + if (Start) { + MemUsed = getMemUsage(); + if (getrusage(RUSAGE_SELF, &RU)) + perror("getrusage call failed: -time-passes info incorrect!"); + } gettimeofday(&T, 0); - if (getrusage(RUSAGE_SELF, &RU)) { - perror("getrusage call failed: -time-passes info incorrect!"); + + if (!Start) { + MemUsed = getMemUsage(); + if (getrusage(RUSAGE_SELF, &RU)) + perror("getrusage call failed: -time-passes info incorrect!"); } TimeRecord Result; Result.Elapsed = T.tv_sec + T.tv_usec/1000000.0; Result.UserTime = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0; Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0; - Result.MemUsed = mallinfo().uordblks; + Result.MemUsed = MemUsed; + return Result; } +static std::vector ActiveTimers; + void Timer::startTimer() { Started = true; - TimeRecord TR = getTimeRecord(); + TimeRecord TR = getTimeRecord(true); Elapsed -= TR.Elapsed; UserTime -= TR.UserTime; SystemTime -= TR.SystemTime; MemUsed -= TR.MemUsed; + PeakMemBase = TR.MemUsed; + ActiveTimers.push_back(this); } void Timer::stopTimer() { - TimeRecord TR = getTimeRecord(); + TimeRecord TR = getTimeRecord(false); Elapsed += TR.Elapsed; UserTime += TR.UserTime; SystemTime += TR.SystemTime; MemUsed += TR.MemUsed; + + if (ActiveTimers.back() == this) { + ActiveTimers.pop_back(); + } else { + std::vector::iterator I = + std::find(ActiveTimers.begin(), ActiveTimers.end(), this); + assert(I != ActiveTimers.end() && "stop but no startTimer?"); + ActiveTimers.erase(I); + } } void Timer::sum(const Timer &T) { @@ -101,8 +135,22 @@ void Timer::sum(const Timer &T) { UserTime += T.UserTime; SystemTime += T.SystemTime; MemUsed += T.MemUsed; + PeakMem += T.PeakMem; } +/// addPeakMemoryMeasurement - This method should be called whenever memory +/// usage needs to be checked. It adds a peak memory measurement to the +/// currently active timers, which will be printed when the timer group prints +/// +void Timer::addPeakMemoryMeasurement() { + long MemUsed = getMemUsage(); + + for (std::vector::iterator I = ActiveTimers.begin(), + E = ActiveTimers.end(); I != E; ++I) + (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase); +} + + //===----------------------------------------------------------------------===// // TimerGroup Implementation //===----------------------------------------------------------------------===// @@ -127,6 +175,12 @@ void Timer::print(const Timer &Total) { if (Total.MemUsed) fprintf(stderr, " %8ld ", MemUsed); + if (Total.PeakMem) { + if (PeakMem) + fprintf(stderr, " %8ld ", PeakMem); + else + fprintf(stderr, " "); + } std::cerr << Name << "\n"; Started = false; // Once printed, don't print again @@ -168,6 +222,8 @@ void TimerGroup::removeTimer() { std::cerr << " ---Wall Time---"; if (Total.getMemUsed()) std::cerr << " ---Mem---"; + if (Total.getPeakMem()) + std::cerr << " -PeakMem-"; std::cerr << " --- Name ---\n"; // Loop through all of the timing data, printing it out... @@ -188,3 +244,60 @@ void TimerGroup::removeTimer() { DefaultTimerGroup = 0; } } + + + +#if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && (__GNUC_PATCHLEVEL__ == 0) +// If we have GCC 3.2.0, we can calculate pool allocation bookkeeping info +#define HAVE_POOL +extern "C" { + // Taken from GCC 3.2's stl_alloc.h file: + enum {_ALIGN = 8, _MAX_BYTES = 128, NFREE = _MAX_BYTES / _ALIGN}; + struct FreeList { FreeList *Next; }; + + FreeList *_ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE[NFREE]; + char *_ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; + char *_ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; + size_t _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; + + // Make the symbols possible to use... + FreeList* (&TheFreeList)[NFREE] = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE; + char * &StartFree = _ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE; + char * &EndFree = _ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE; + size_t &HeapSize = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE; +} +#endif + +// getNumBytesToNotCount - This function is supposed to return the number of +// bytes that are to be considered not allocated, even though malloc thinks they +// are allocated. +// +static unsigned getNumBytesToNotCount() { +#ifdef HAVE_POOL + // If we have GCC 3.2.0, we can subtract off pool allocation bookkeeping info + + // Size of the free slab section... + unsigned FreePoolMem = (unsigned)(EndFree-StartFree); + + // Walk all of the free lists, adding memory to the free counter whenever we + // have a free bucket. + for (unsigned i = 0; i != NFREE; ++i) { + unsigned NumEntries = 0; + for (FreeList *FL = TheFreeList[i]; FL; ++NumEntries, FL = FL->Next) + /*empty*/ ; + +#if 0 + if (NumEntries) + std::cerr << " For Size[" << (i+1)*_ALIGN << "]: " << NumEntries + << " Free entries\n"; +#endif + FreePoolMem += NumEntries*(i+1)*_ALIGN; + } + return FreePoolMem; + +#else +#warning "Don't know how to avoid pool allocation accounting overhead for this" +#warning " compiler: Space usage numbers (with -time-passes) may be off!" + return 0; +#endif +}