Many logging frameworks (e.g., log4j) allow you to pass lambda expressions instead of String
s to the logging API. The argument is that if the string is particularly expressive to construct, the string construction can be lazily executed via the lambda expression. That way, the string is only constructed if the system's log level matches that of the call.
But, given that modern compilers do much method inlining automatically, is there really a point to using lambda expressions in this way? I'll supply a simplified example below to demonstrate this concern.
Suppose our traditional logging method looks like this:
void log(int level, String message) {
if (level >= System.logLevel)
System.out.println(message);
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, "Very expensive string to construct ..." + etc);
Let's suppose that FINE
is less than CRITICAL
, so, although an expensive string is constructed, it's all for not since the message is not outputted.
Lambda logging APIs help this situation so that the string is only evaluated (constructed) when necessary:
void log(int level, Supplier<String> message) {
if (level >= System.logLevel)
System.out.println(message.apply());
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, () -> "Very expensive string to construct ..." + etc);
But, it's feasible that the compiler can just inline the logging method so that the net effect is as follows:
System.logLevel = Level.CRITICAL;
if (Level.FINE >= System.logLevel)
System.out.println("Very expensive string to construct..." + etc);
In this case, we don't have to evaluate the string prior to the logging API call (because there is none), and presumably, we would gain performance from just the inlining.
In summary, my question is, how do lambda expressions help us in this situation given that the compiler can possibly inline logging API calls? The only thing I can think of is that, somehow, in the lambda case, the string is not evaluate if the logging level is not a match.
Your optimization hasn't just introduced inlining - it's changed ordering. That's not generally valid.
In particular, it wouldn't be valid to change whether methods are called, unless the JIT can prove that those methods have no other effect. I'd be very surprised if a JIT compiler would inline and reorder to that extent - the cost of checking that all the operations involved in constructing the argument to the method have no side effects is probably not worth the benefit in most cases. (The JIT compiler has no way of treating logging methods differently to other methods.)
So while it's possible for a really, really smart JIT compiler to do this, I'd be very surprised to see any that actually did this. If you find yourself working with one, and write tests to prove that this approach is no more expensive than using lambda expressions, and continue to prove that over time, that's great - but it sounds like you're keener on assuming that's the case, which I definitely wouldn't.
Raffi lets look at an example on how the compiler inlining you are talking about will change the program logic and compiler needs to be very smart enough to be able to figure that out:
public String process(){
//do some important bussiness logic
return "Done processing";
}
1) Without inlining the process()
will be callled regardless of logging level:
log( Level.FINE, "Very expensive string to construct ..." + process() );
2) With inlining the process()
will be called only under certain logging level and our important bussiness logic wont be able to run:
if (Level.FINE >= System.logLevel)
System.out.println("Very expensive string to construct..." + process() );
The compiler in this case has to figure out how the message string is created and not inline the method if it calls any other method during its creation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With