Java Performance Services
Training, Seminars, Benchmarking, Tuning

Java Performance Tuning Course


Chania Crete
May 22rd-25th 2012

Sydney Australia
Feb 6-9, 2012

Montreal Canada
Feb 27-March 2, 2012

San Francisco CA
March 20-23 2012

Paris France
March 27-March 2, 2012

Request an in-house seminar

Anti-if

I have joined Anti-IF Campaign

Calendar

««May 2012»»
SMTWTFS
   12345
6789101112
13141516171819
20212223242526
2728293031

Performance Anti-Patterns

My Top Tags

                                       

Mailing List

My RSS Feeds








Simple logging tip

posted Wednesday, 2 September 2009

Each time I go into a round of preparing for course delivery I try to drop in information about any recurrent problem that I've run into. This time round the recurrent problem is object creation.

Included in the list of latest places where I've seen object creation become a hinderance to performance was the Scala compiler and a few applications that have been aggressively logging activity. In the former case the problem was due to frequent resizing of the main symbol table. Hopefully the next version of the Scala compiler will fix that problem. The later case had to do with premature construction of debug messages.

Most logging looks something like...

log.debug( "Some tag " + variable.toString() + ", " + someMoreInformation);

The problem is that log.debug(String msg) causes all of the stuff in the brackets to be executed. Not only can this coding style create a significant number of object, it is very hard to detect and very time consuming to fix in an application of significant enough size.

Why is object creation difficult to detect? It just looks as if your application is slow but for no measurable reason. Objects created by the String concatenation will most likely *never* escape from young generational space. Since the young generational spaces use evacuating collectors, the cost of collecting the object is close to zero. However, the cost of creating it isn't. More over object creation involves updating shared structures which implies it involves some sort of concurrency control. This implies a degree of serialization which implies an upper bound on the rate at which objects can be created.

In the case of log messages there is an easy solution, wrap each call in an if statement that checks the current log level. If the log message will never get logged, there isn't any point in creating all of those extra objects. The overall improvements may vary but I've never found them to insignificant.

Log4J

if ( log4j.isDebugEnabled())
    log.debug(message + “ “ + otherMessage);

 

JDK Logging

 if ( logjdk.isLoggable( Level.WARNING))
    logjdk.warning(message + “ “ + otherMessage);

 

It's a simple trick and certainly not a new one. But I think it's worth mentioning again.

Come join me in Chania Greece at my performance tuning course if you are interested in more details on GC or any other performance related topic.

tags:                  




1. darose left...
Wednesday, 2 September 2009 10:35 pm

FYI - slf4j helps a bit here. It uses parameter substitution to format log messages for you, and skips the formatting if the message won't be getting output. e.g.:

logger.trace("Adding update to batch: {}", sql);

Of course it doesn't solve the problem completely, and you still need to use the if block from time to time. For example, here I definitely don't want to go through the trouble of calling record.toString() in a tight loop if the message is just going to get thrown away:

if (logger.isTraceEnabled()) { logger.trace("Importing data record: {}", record.toString()); }

DR


2. Michael Nygard left...
Thursday, 3 September 2009 3:33 pm

It's sort of funny to see the "anti-if campaign" badge on this page.


3. Gili left...
Thursday, 3 September 2009 3:45 pm :: http://www.desktopbeautifier.com/

I am familiar with the logging paradigm you advocate, but every time I see it it leaves me thinking "there has to be a better way than this".

I almost wish that the compiler was smart enough to notice at runtime: "log.trace() always discards its parameters, which means that I don't have to execute any of its arguments in the first place"


4. Kirk Pepperdine left...
Thursday, 3 September 2009 7:18 pm

Michael, your comment just cracked me up.

It does suggest that Gill is correct in that there must be a better solution. David's solution? var args maybe? Serious overloading of the log methods? The last two are sort of ugly but then performance sometimes (but not always) dictates ugly solutions.


5. Markus Kohler left...
Thursday, 3 September 2009 8:28 pm :: http://kohlerm.blogspot.com/

Hi Kirk, We had exactly the same rule, until we switched to the printf style mentioned in the comment :) BTW closures would help here again, to remove the if statement. Regards, Markus


6. William Louth left...
Friday, 4 September 2009 8:54 am :: http://williamlouth.wordpress.com

Is it not so obvious that the solution is not to log in the first place and most certainly not to write the actual statement in the code itself.

By the way detecting object creation is not difficult at all. We have counters and meters for this.

We need to replace logging with an Events API with a dynamic but typed event model that can be augmented with complex event processing agents (diagnostics, health checkers, event aggregators) rather than having to do this after the fact and via post processing of strings in a log file. We lose so much execution & environment context with a log string or log record.


7. Alain O'Dea left...
Friday, 4 September 2009 5:55 pm :: http://concise-software.blogspot.com/

At my job I wrote wrappers for Log and LogFactory such that developers can write complex MessageFormat style log statements without performance penalties. It creates an anonymous Object (closure) for each formatted log call with a toString() that creates and runs the MessageFormat. The only space cost if the log level is off is the Object array the developer sends. The big benefit at runtime is avoiding the quadratic order time cost of string concatenation.


8. Casper Bang left...
Friday, 4 September 2009 6:03 pm

If only we had closures we could factor the if-statement away nicely.


9. Dimitris Andreou left...
Saturday, 5 September 2009 3:36 am :: http://code-o-matic.blogspot.com/

>More over object creation involves updating shared structures

Extremely rarely (i.e. if a thread's local heap is exhausted). The above sentence can be misleading if left unquantified. If someone would want to see what would really happen to a multithreaded app if there really was synchronization associated with object creation, then just add a volatile-write in the constructor of java.lang.Object (hint: it wouldn't be pretty).


10. William Louth left...
Saturday, 5 September 2009 3:49 pm :: http://williamlouth.wordpress.com

Dimitris I believe Kirk was referring to the actual object construction (executing code) and not the memory allocation.

Casper can you quantify the actual savings made with a closure (number of keystrokes? bytecode ops executed,....). How does this compare to actually not writing log statements all over the code.


11. StabiloBoss left...
Monday, 7 September 2009 10:26 am

It surely doesn't solve the problem neither, but it allows sparring a function call and makes the code "less unreadable": you can always declare a static variable together with the Logger, like:

  • boolean debug = logger.isDebugEnabled();

and use this variable in your code instead:

  • if (debug) {

  • }

BTW, I like darose's slf4j proposition and will give a try soon!


12. Kirk Pepperdine left...
Monday, 7 September 2009 4:43 pm

Dimitris, I won't say that I fully understand what is going on but there is clearly a benefit to reducing object allocation (aside from GC benefits) that goes beyond fast path allocations. It gives me the feeling of some lock contention which is why used implies instead of a more definitive word. Clearly the effect of thread local allocation is significant. Try running with -XX:-UseTLAB and you'll see the difference.

StablioBoss, I'd hate to have that evaluated once and then have someone use JMX to reset the logging level.... ;-). I'm not sure that I like varargs over the more verbose form of method overloading. But then, that is still a win over lo4j or jdk logging.

Kirk


13. Jens Melzer left...
Wednesday, 30 September 2009 6:42 am

But there are some possibilities to overcome the object creation thing. Look at the logback project of the(or one of them dunno) founder of log4j. There he uses a printf like possibility to parse arguments without parsing the String. Ok it will create an array, because of the varargs and it will execute all you parse to it. But it's one possibility to partly remove the double check if no real execution is taken place.


14. Damokles left...
Sunday, 4 October 2009 6:04 pm

Couldn't we use some AOP here using aspects to guard the calls with if statements?