Chapter 1.The Tracing War Story Eve worked on contained tra orm or another dtbggmaninnne,andntnteectiowhovoinoantralotareYouoldnotepeta ce d n in a adation due to fve run er som ce principles that often on.It is simple and familiar.We don't e home many pertormance ues that you a ncounter in any Many C++programmers define a simpleclass to print diagnostic information toalog file Pr ogrammers can d race ob in ea at the want to the Trace class ammer find problems without using a debugger.If your C++code happe to be caive coenom,ungereyourdebe The most extreme form of trace performance optimization would be to eliminate the performance cost altogether by embedding trace calls insidedef blocks: uction");//Constructor takes a function name argument The weaknessofthe approach is that you must recompile touracingonand ff This is ngyour customers will not b able to d o unless you jump on the free communicating with the runing program.Theace class implementation could check the trace state prior to logging any trace information: 2ea8e:asboastrioa6asal if (traceIsActive) /log message here expect our code to exhibit typical trace statement wi something along the lines c t.debug("x ="itoa(x))://itoa()converts an int to ascii the1 Chapter 1. The Tracing War Story Every software product we have ever worked on contained tracing functionality in one form or another. Any time your source code exceeds a few thousand lines, tracing becomes essential. It is important for debugging, maintaining, and understanding execution flow of nontrivial software. You would not expect a trace discussion in a performance book but the reality is, on more than one occasion, we have run into severe performance degradation due to poor implementations of tracing. Even slight inefficiencies can have a dramatic effect on performance. The goal of this chapter is not necessarily to teach proper trace implementation, but to use the trace vehicle to deliver some important performance principles that often surface in C++ code. The implementation of trace functionality runs into typical C++ performance obstacles, which makes it a good candidate for performance discussion. It is simple and familiar. We don't have to drown you in a sea of irrelevant details in order to highlight the important issues. Yet, simple or not, trace implementations drive home many performance issues that you are likely to encounter in any random fragment of C++ code. Many C++ programmers define a simple Trace class to print diagnostic information to a log file. Programmers can define a Trace object in each function that they want to trace, and the Trace class can write a message on function entry and function exit. The Trace objects will add extra execution overhead, but they will help a programmer find problems without using a debugger. If your C++ code happens to be embedded as native code in a Java program, using a Java debugger to trace your native code would be a challenge. The most extreme form of trace performance optimization would be to eliminate the performance cost altogether by embedding trace calls inside #ifdef blocks: #ifdef TRACE Trace t("myFuction"); // Constructor takes a function name argument t.debug("Some information message"); #endif The weakness of the #ifdef approach is that you must recompile to turn tracing on and off. This is definitely something your customers will not be able to do unless you jump on the free software bandwagon and ship them your source code. Alternatively, you can control tracing dynamically by communicating with the running program. The Trace class implementation could check the trace state prior to logging any trace information: void Trace::debug(string &msg) { if (traceIsActive) { // log message here } } We don't care about performance when tracing is active. It is assumed that tracing will be turned on only during problem determination. During normal operation, tracing would be inactive by default, and we expect our code to exhibit peak performance. For that to happen, the trace overhead must be minimal. A typical trace statement will look something along the lines of t.debug("x = " + itoa(x)); // itoa() converts an int to ascii This typical statement presents a serious performance problem. Even when tracing is off, we still must create the string argument that is passed in to the debug() function. This single statement hides substantial computation: