////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Copyright -C- 1997, by Richard E. Pattis // // Class Name : Timer // // Classification : Class // // Files : Declaration - timer.h // Definition - timer.cpp // Driver - dtimer.cpp // // Author : Richard E. Pattis // Computer Science Department // Carnegie Mellon University // 5000 Forbes Avenue // Pittsburgh, PA 15213-3891 // e-mail: pattis@cs.cmu.edu // // Maintainer : Author // // // Description: // // Timer defines a class for timing executing code. Each Timer can keep time // in one of two modes: CPU or Wall time. For CPU time, the timer accumulates // time only while the code is running, so it is useful for determining the // speed of code. For Wall time, the timer accumulates time according to a // normal "clock on the wall", so it is useful for timing how long a user // takes to answer a question at the terminal (typically no CPU time elapses // while the computer is waiting for user input). Timers can be run either // forward or backward, either increasing or decreasing the time that they // accumulate (so, it is possible for a Timer to accumulate negative time). // // On a computer with one user (e.g., a personal computer), CPU time and // Wall time may be the same; but under advanced PC operating systems (those // with true multiprocessing capabilities) and large timesharing systems, the // CPU time accumulates more slowely than Wall time, because the machine is // sharing its processor among many processes. // // The member functions Fmt and Speed provide a convenient (and safe in // the case of Speed - where 0 time may have elapsed) way to convert times // and speeds into strings that automatically contain the Mode (CPU/Wall) and // Unit (nSecs..Hrs) of the time measurement. See their postconditions for // more details, especially concerning the use of Self_Select as a Unit_Enum. // // Known Bugs : None // // Future Plans : None // // Header History: // 11/ 6/96: R. Pattis - Operational // 11/ 8/96: R. Pattis - Added Speed function to help avoid divison by 0 // 11/12/96: R. Pattis - Fixed Speed function for Reporting = Self_Select // 11/17/96: R. Pattis - Improved formating of the value in Fmt and Speed // by replacing sprintf by strstream operations // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Class Declaration // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #ifndef __Timer__ //Do not duplicate in a compilation #define __Timer__ #include using namespace std; class Timer { /////// public: /////// //Enumerations enum State_Enum {On, Off}; enum Direction_Enum {Forward, Backward, Current_Direction}; enum Mode_Enum {CPU, Wall}; enum Units_Enum {nSecs, uSecs, mSecs, Secs, Mins, Hrs, Days, Self_Select}; //Constructors Timer (Mode_Enum Measuring, State_Enum Starts = Off, Direction_Enum Running = Forward); Timer (const Timer& T); //Copy constructor //Assignment Timer& operator= (const Timer& RHS); //LHS = RHS; //Mutators void Reset (State_Enum Starts = Off, Direction_Enum Running = Forward); void Set (State_Enum Now, Direction_Enum Running = Current_Direction); //Accessors Mode_Enum Mode () const; State_Enum State () const; Direction_Enum Direction () const; double Elapsed (Units_Enum Reporting = Secs) const; double Tick (Units_Enum Reporting = Secs) const; //Formators string Fmt (Units_Enum Reporting = Secs, double Minimum = 1.0) const; string Speed (int Item_Count, string Item_Description, Units_Enum Reporting = Secs, double Minimum = 1.0) const; //////// private: //////// //long (instead of int) works for 16 bit (DOS)/32 bit machines Mode_Enum my_Mode; //my_Mode set by constructor/= State_Enum my_State; Direction_Enum my_Direction; long my_Accumulated_Time; long my_Last_On_Time; //Meaningful only when my_State=On //Private Member Function long Elapsed_Since_On () const; }; #endif ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Member Semantics // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Enumeration Note: // // Outside the definitions of the members functions in the class, we must // write the fullly qualified names for all enumeration names and their literals. // Such fully quallified names are prefixed by Timer:: as in Timer::CPU // // // Timer (Mode_Enum Measuring, // State_Enum Starts = Off, // Direction_Enum Running = Forward); // Pre : None // Post : Object constructed with specified Mode, State, and Direction // Elapsed(...) = 0.0 // Example: Timer T(Timer::CPU); // T: CPU, Off, Forward // Timer T(Timer::CPU, Timer::On); // T: CPU, On, Forward // // // Timer (const Timer& T); //Copy constructor // Pre : None // Post : Object is copy of T at the time of construction // // // Timer& operator= (const Timer& RHS); //LHS = RHS; // Pre : None // Post : LHS object is a copy of RHS at the time of assignment // // // void Reset (State_Enum Starts = Off, // Direction_Enum Running = Forward); // Pre : None // Post : Timer is reset in specified State and Direction // Elapsed(...) = 0.0 // Note : The default arguments for reset are identical to construction // The post condition is also identical to construction // // // void Set (State_Enum Now, // Direction_Enum Running = Current_Direction); // Pre : None // Post : Accumulate time if Now is On (no default for this parameter) // Time will increment/decrement if Running is Forward/Backward // If Running = Current_Direction (the default), the direction // specified during construction (or the most recent call to // Reset or Set) remains unchanged // Note : A Timer can be Set On or Off many times during a program; it // accumulates time (in the specified direction) only while On // // // Mode_Enum Mode () const; // Pre : None // Post : Returns the current mode (CPU or Wall) of a Timer // // // State_Enum State () const; // Pre : None // Post : Returns the current state (On or Off) of a Timer // // // Direction_Enum Direction () const; // Pre : None // Post : Returns the current direction (Forward or Backward) of a Timer // (even a Timer that is Off has a direction) // // // double Elapsed (Units_Enum Reporting = Secs) const; // Pre : Reporting != Self_Select; throws invalid_argument // Post : Returns the time accumulated: the parameter controls the Units // of the returned value // Note : When Elapsed is called, the State of a timer can be On or Off; // the correct time is returned // Example: T.Elapsed() returns the time (by default, in seconds) that has // accumulated on the Timer named T since it was constructed or // last Reset // // // double Tick (Units_Enum Reporting = Secs) const; // Pre : Reporting != Self_Select; throws invalid_argument // Post : Returns the smallest amount of time that is measurable, according // to the accuracy of the system clock being used // Example: For Timer T(Timer::CPU); T.Tick(Timer::Secs) returns the // smallest amount of CPU time (in seconds) that is measurable // For Pentimum PCs, this value is 0.001 seconds; for fast machines // it may be smaller; for older PCs, it was 1/60th of a second // // // string Fmt (Units_Enum Reporting = Secs, // double Minimum = 1.0) const; // Pre : None // Post : Returns the time accumulated as a string (see examples) // Self_Select means choose the largest Unit whose returned value // is >= Minimum // (Minimum has no effect on Fmt unless Reporting = Self_Select) // Example: If, T.Elapsed(Timer::Secs) = 2.15678, // T.Fmt (Timer::Secs)= "2.15678 CPU seconds" // In this case, T.Fmt(Timer::Self_Select, 10.0) returns the string // "2157 CPU milliseconds" using the largest Unit whose returned // value exceeds 10.0 // // // string Speed (int Item_Count, // string Item_Description, // Units_Enum Reporting = Secs, // double Minimum = 1.0) const; // Pre : None // Post : Returns a string that includes the processing speed, computed // as Item_Count/Elapsed(Reporting), with the specified // Item_Description: e.g., "50 widgets per CPU second" // If T.Elapsed(...) = 0, this processing speed appear as // "unmeasurable widgets per CPU second (0 time elapsed)" // Self_Select means choose the largest Unit whose returned value // is >= Minimum // (Minimum has no effect on Fmt unless Reporting = Self_Select) // Example: If Count = 1,000 and T.Elapsed(Timer::Secs) = 2, then // T.Speed (Count, "widgets") = "50 Widgets per CPU second" // In this case T.Speed(Count, "widgets", Timer::Self_Select, 100.0) // returns "50000 widgets per CPU millisecond" using the largest // Unit whose calculated speed exceeds 10.0 // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Extended Examples // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // To measure the amount of CPU time spent in a C++ code fragment, use the // following scheme // // Timer T(Timer::CPU); //Construct a CPU Timer; Off // // // Other declarations/code // // T.Set(Timer::On); //Turn it On // // // Put code fragment to time here // // T.Set(Timer::Off); //Turn it Off // ... T.Elapsed(Timer::Secs) ... //Get time in Secs // // Note: If there are no intervening declarations (between construction of the // Timer and the code to be timed), we can more simply declare // Timer T(Timer::CPU, Timer::On); // which constructs a Timer that is initially turned on. // // // To measure the amount of CPU time spent in a fast (<< T.Tick()) C++ code // fragment, put the code to be timed inside a loop, time it many times, and // then subtract out the overhead in looping, using the following scheme // // Timer T(Timer::CPU); //Construct a CPU Timer; Off // // // Other declarations/code // // T.Set(Timer::On); //Turn it On // // loop_for (1000) { //Do code 1,000 times (see syntax.h) // // Put code fragment to time here // } // // T.Set(Timer::On, Timer::Backwards); //Set to run Backwards // // loop_for(1000); //Subtract loop time // // T.Set(Timer::Off); //Turn it off // ... T.Elapsed(Timer::Secs) ... //Get time in Secs // // Note: Some compilers are smart enough to realize that the second loop // has no effect, so no code is generated. With any complex code // fragment, the amount of loop overhead time will be minimal anyway. // // // To measure the amount of Wall clock time spent by the user answering a // question, use the following scheme (similar to measuring CPU time) // // Timer T(Timer::Wall); // Construct a Timer // // // Other declarations/code // // T.Set(Timer::On); //Turn it On // // // Put code fragment to prompt user here // // T.Set(Timer::Off); //Turn it Off // ... T.Elapsed(Timer::Secs) ... //Get time in Secs // ////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////