Which of array creation vs string concatenation fo

2019-02-25 17:22发布

While passing multiple pieces of string for logging, is it best to use varargs or string concatenation?

Logging will be disabled in production environment.

Considering below code,

public void log(int logLevel, String ... msg) {
    if (logLevel >= currentLogLevel) {
        for (String m : msg) {
            // print the log to file
        }
    }
}
// calling
log(2, "text1", var1, "text2");

Vs.

public void log(int logLevel, String msg) {
    if (logLevel >= currentLogLevel) {
        // print the log to file
    }
}
// calling
log(2, "text1" + var1 + "text2");

Which one performs well in both contexts (enabled/disabled)?

3条回答
霸刀☆藐视天下
2楼-- · 2019-02-25 17:50

Since Java-8 there's a faster alternative using Supplier<String>:

public void log(int logLevel, Supplier<String> msg) {
    if (logLevel >= currentLogLevel) {
        // print the log to file like out.println(msg.get());
    }
}

// calling
log(2, () -> "text1" + var1 + "text2");

This is significantly faster than concatenation provided that you execute this line many times (first time it's slower as anonymous class should be created). Source: Aleksey Shipilëv talk on Joker'14 (PDF, check slides 26-28).

查看更多
看我几分像从前
3楼-- · 2019-02-25 17:58

If you are using something like SLF4J, you can do parameterized log messages. Like this

log.info("Item {} is done, it took {} ms", item.getName(), duration);

Which also has benefits of not also having to wrap some of them in log level check if(log.isDebugEnabled()). See this faq

查看更多
我命由我不由天
4楼-- · 2019-02-25 18:05

Neither one of these performs particularly well. The varargs will perform better, because it doesn't do String concatenation, which is a significantly more expensive operation than array creation, but because you are using String varargs and not Object varargs, the toString() method will have to be called on all objects even in production, which will be relatively expensive.

However, not to fear! This is a solved problem. SLF4J (Simple Logging Facade for Java) has already figured out the very best way to do this for you. If you use an SLF4J implementation (the two most common are Log4J and Logback), you get to use parameterized logging arguments:

Better alternative

There exists a convenient alternative based on message formats. Assuming entry is an object, you can write:

Object entry = new SomeObject(); 
logger.debug("The entry is {}.", entry);

Only after evaluating whether to log or not, and only if the decision is positive, will the logger implementation format the message and replace the '{}' pair with the string value of entry. In other words, this form does not incur the cost of parameter construction when the log statement is disabled.

The following two lines will yield the exact same output. However, in case of a disabled logging statement, the second variant will outperform the first variant by a factor of at least 30.

logger.debug("The new entry is "+entry+".");
logger.debug("The new entry is {}.", entry);

A two argument variant is also available. For example, you can write:

logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);

If three or more arguments need to be passed, an Object[] variant is also available. For example, you can write:

Object[] paramArray = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", paramArray);

Notice that in the first and second case, it doesn't create the temporary array, method signatures exist to not create the array if it's not necessary in SLF4J. Here is the full documentation where you can see all the different method signatures

If you should decide to use Logback, you should know that the cost of a method invocation when logging is turned off is around 20 nanoseconds. Full discussion of Logback's performance:

You can turn off logging entirely by setting the level of the root logger to Level.OFF, the highest possible level. When logging is turned off entirely, the cost of a log request consists of a method invocation plus an integer comparison. On a 3.2Ghz Pentium D machine this cost is typically around 20 nanoseconds.

查看更多
登录 后发表回答