[ 
https://issues.apache.org/jira/browse/LOG4J2-1415?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15318186#comment-15318186
 ] 

Anthony Maire commented on LOG4J2-1415:
---------------------------------------

I don't think the way the "switch" is implemented as a major impact on 
performance

I made a small JMH benchmark, running on a Xeon E5-2643 v3 with JVM 1.8.0_92 
(same kind of results on my desktop with a core i7 2600) and the cost of 
instanceof is barely noticeable (and less than the class name switch)

{noformat}
Benchmark                              Mode  Cnt     Score    Error  Units
SwitchClassTest.referenceWithCast      avgt   25  1958.885 ±  7.773  ns/op
SwitchClassTest.referenceWithToString  avgt   25  2050.443 ± 10.347  ns/op
SwitchClassTest.useClassName           avgt   25  2015.931 ± 10.190  ns/op
SwitchClassTest.useInstanceOf          avgt   25  1978.622 ± 10.030  ns/op
{noformat}

Here is the benchmark code:
{code}


import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations=5)
@Measurement(iterations=5)
@Fork(value=5)
public class SwitchClassTest {
        
        private static final String STRING_CLASS_NAME = "java.lang.String";
        private static final String CHARACTER_CLASS_NAME = 
"java.lang.Character";
        private static final String LONG_CLASS_NAME = "java.lang.Long";
        private static final String INTEGER_CLASS_NAME = "java.lang.Integer";
        private static final String BOOLEAN_CLASS_NAME = "java.lang.Boolean";
        private static final String DOUBLE_CLASS_NAME = "java.lang.Double";
        private static final String FLOAT_CLASS_NAME = "java.lang.Float";
        
        private Object[] params = new Object[]{"abc", 'a', Long.MAX_VALUE, 
Integer.MAX_VALUE, Double.MAX_VALUE, Float.MAX_VALUE, Boolean.TRUE};
        private StringBuilder sb = new StringBuilder();

        
        @Benchmark
        public int referenceWithToString() {
                for (Object param : params) {
                        sb.append(param);
                }
                int size = sb.length();
                sb.setLength(0);
                return size;
        }
        
        @Benchmark
        public int referenceWithCast() {
                sb.append(params[0]);
                sb.append(((Character) params[1]).charValue());
                sb.append(((Long) params[2]).longValue());
                sb.append(((Integer) params[3]).intValue());
                sb.append(((Double) params[4]).doubleValue());
                sb.append(((Float) params[5]).floatValue());
                sb.append(((Boolean) params[6]).booleanValue());
                int size = sb.length();
                sb.setLength(0);
                return size;
        }
        
        @Benchmark
        public int useInstanceOf() {
                for (Object param : params) {
                        if (param instanceof String) {
                                sb.append(param);
                        } else if (param instanceof Character) {
                                sb.append(((Character) param).charValue());
                        } else if (param instanceof Long) {
                                sb.append(((Long) param).longValue());
                        } else if (param instanceof Integer) {
                                sb.append(((Integer) param).intValue());
                        } else if (param instanceof Double) {
                                sb.append(((Double) param).doubleValue());
                        } else if (param instanceof Float) {
                                sb.append(((Float) param).floatValue());
                        } else if (param instanceof Boolean) {
                                sb.append(((Boolean) param).booleanValue());
                        }
                }
                int size = sb.length();
                sb.setLength(0);
                return size;
        }
        
        @Benchmark
        public int useClassName() {
                for (Object param : params) {
                        String className = param.getClass().getName();
                        switch (className){
                        case STRING_CLASS_NAME:
                                sb.append(param);
                                break;
                        case CHARACTER_CLASS_NAME:
                                sb.append(((Character) param).charValue());
                                break;
                        case LONG_CLASS_NAME:
                                sb.append(((Long) param).longValue());
                                break;
                        case INTEGER_CLASS_NAME:
                                sb.append(((Integer) param).intValue());
                                break;
                        case DOUBLE_CLASS_NAME:
                                sb.append(((Double) param).doubleValue());
                                break;
                        case FLOAT_CLASS_NAME:
                                sb.append(((Float) param).floatValue());
                                break;
                        case BOOLEAN_CLASS_NAME:
                                sb.append(((Boolean) param).booleanValue());
                                break;
                        }
                }
                int size = sb.length();
                sb.setLength(0);
                return size;
        }

}
{code}



> Avoid calling toString() on auto-boxed primitive message parameters
> -------------------------------------------------------------------
>
>                 Key: LOG4J2-1415
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-1415
>             Project: Log4j 2
>          Issue Type: Improvement
>    Affects Versions: 2.6
>            Reporter: Anthony Maire
>            Priority: Minor
>
> When using Log4j 2 through the SLF4J binding, the Unboxer mecanism is not 
> available and a parameterized message with a primitive type parameter will be 
> auto-boxed.
> Then this boxed value will be formatted by 
> ParameterFormatter.recursiveDeepToString() and further allocations will happen
> To lower allocation rate for SLF4J users, 
> ParameterFormatter.appendSpecialTypes() should handle boxed primitive types 
> too.
> {code}
> private static boolean appendSpecialTypes(final Object o, final StringBuilder 
> str) {
>     ...
>     } else if (o instanceof Long) {
>         str.append(((Long) o).longValue());
>         return true;
>     } else if (o instanceof Integer) {
>         str.append(((Integer) o).intValue());
>         return true;
>     } else if (o instanceof Double) {
>         str.append(((Double) o).doubleValue());
>         return true;
>     } // similarly for float, short, boolean and char.
>     ...
> }
> {code}



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to