Trace :Trace() if (traceIsActive) cout <"Exit function "<theFunctionName <endl; Once theTrace was designed,coded,and tested,it was deployed and quickly inserted into a large part of io biccts has slowed dow that 2060fa e by a factor of five.We are talking about the case when tracing was off and performance was supposed to be unaffected What Went Wrong ance depending on their respective experience ·/Ois expensive. Our initialace implementation has adhered to all three of these principles.We avoided /O iftracing was off,all methods were inli and all strin arguments were passed by reterence stuck b tive wisdom of the previous ue fel short of t ggests that the dominant issue The of a Trace object is to log function entry and exit: int myPunction(int x) This minimal trace invokes a sequence of computations: .Create the string name local to myFunction.3 inline void Trace::debug(const string &msg) { if (TraceIsActive) { cout << msg << endl; } } inline Trace::~Trace() { if (traceIsActive) { cout << "Exit function " << theFunctionName << endl; } } Once the Trace was designed, coded, and tested, it was deployed and quickly inserted into a large part of the code. Trace objects popped up in most of the functions on the critical execution path. On a subsequent performance test we were shocked to discover that performance plummeted to 20% of its previous level. The insertion of Trace objects has slowed down performance by a factor of five. We are talking about the case when tracing was off and performance was supposed to be unaffected. What Went Wrong Programmers may have different views on C++ performance depending on their respective experiences. But there are a few basic principles that we all agree on: • I/O is expensive. • Function call overhead is a factor so we should inline short, frequently called functions. • Copying objects is expensive. Prefer pass-by-reference over pass-by-value. Our initial Trace implementation has adhered to all three of these principles. We avoided I/O if tracing was off, all methods were inlined, and all string arguments were passed by reference. We stuck by the rules and yet we got blindsided. Obviously, the collective wisdom of the previous rules fell short of the expertise required to develop high-performance C++. Our experience suggests that the dominant issue in C++ performance is not covered by these three principles. It is the creation (and eventual destruction) of unnecessary objects that were created in anticipation of being used but are not. The Trace implementation is an example of the devastating effect of useless objects on performance, evident even in the simplest use of a Trace object. The minimal usage of a Trace object is to log function entry and exit: int myFunction(int x) { string name = "myFunction"; Trace t(name); ... }; This minimal trace invokes a sequence of computations: • Create the string name local to myFunction. • Invoke the Trace constructor. • The Trace constructor invokes the string constructor to create the member string