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

Remko Popma commented on LOG4J2-250:
------------------------------------

I did some research to see if it is worth making the proposed change. It turns 
out that the performance difference between constructing a Log4jLogEvent with 
or without an exception is quite dramatic.

Here are the latency numbers generated by JMH:
{code}
Benchmark                           Mode Thr     Count  Sec         Mean   Mean 
error    Units
o.s.MyBenchmark.testBaseline      sample   1   6703951    1       22.780        
0.741    ns/op
o.s.MyBenchmark.testException     sample   1    188867    1    26455.008      
116.268    ns/op
o.s.MyBenchmark.testNoException   sample   1   5457363    1      130.162       
10.459    ns/op
{code}

Constructing a Log4jLogEvent with a null exception costs 130 nanoseconds. With 
non-null exception this becomes 26,455 nanoseconds. A 200x difference. 
Throughput numbers:
{code}
Benchmark                           Mode Thr     Count  Sec         Mean   Mean 
error    Units
o.s.MyBenchmark.testBaseline       thrpt   1         5    1  2385314.189   
185910.970   ops/ms
o.s.MyBenchmark.testException      thrpt   1         5    1       35.390        
3.086   ops/ms
o.s.MyBenchmark.testNoException    thrpt   1         5    1    10441.802      
621.190   ops/ms
{code}

Here is the microbenchmark code:
{code}
package org.sample;

import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
import org.openjdk.jmh.logic.BlackHole;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.SimpleMessage;

public class MyBenchmark {
    private static final Message MESSAGE = new SimpleMessage("Test message");
    private static final Throwable ERROR = new Exception("test");

    @GenerateMicroBenchmark
    public void testBaseline(BlackHole bh) {
    }

    @GenerateMicroBenchmark
    public void testNoException(BlackHole bh) {
        Throwable t = null;
        Log4jLogEvent event = new Log4jLogEvent("a.b.c", null, "a.b.c", 
Level.INFO, MESSAGE, t);
        bh.consume(event);
    }

    @GenerateMicroBenchmark
    public void testException(BlackHole bh) {
        Throwable t = ERROR;
        Log4jLogEvent event = new Log4jLogEvent("a.b.c", null, "a.b.c", 
Level.INFO, MESSAGE, t);
        bh.consume(event);
    }
}
{code}
The command to run the latency test:
{code}
java -jar microbenchmarks.jar ".*MyBenchmark.*" -i 5 -f 1 -wi 5 -bm sample -tu 
ns
{code}

The command to run the throughput test:
{code}
java -jar microbenchmarks.jar ".*MyBenchmark.*" -i 5 -f 1 -wi 5 -bm Throughput 
-tu ms
{code}

To set up for the above test, first follow the *Basic Usage* instructions from 
the JMH page: http://openjdk.java.net/projects/code-tools/jmh/

Then replace the generated test/pom.xml with the pom.xml below:

{code}
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"; 
xmlns="http://maven.apache.org/POM/4.0.0";
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <groupId>org.sample</groupId>
  <artifactId>test</artifactId>
  <version>1.0</version>
  <name>Auto-generated JMH benchmark</name>
  <properties>
    <slf4j.version>1.7.5</slf4j.version>
    <logback.version>1.0.13</logback.version>
    <log4j2.version>2.0-rc1</log4j2.version>
    <maven.compile.source>1.6</maven.compile.source>
    <maven.compile.target>1.6</maven.compile.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.openjdk.jmh</groupId>
      <artifactId>jmh-core</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
       <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-ext</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>${logback.version}</version>
      </dependency>
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
      </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>${log4j2.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>${log4j2.version}</version>
      </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.0</version>
        <configuration>
          <compilerVersion>1.6</compilerVersion>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <finalName>microbenchmarks</finalName>
              <transformers>
                <transformer 
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>org.openjdk.jmh.Main</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
{code}

> (TBD) Refactor Log4jLogEvent to lazily create ThrowableProxy
> ------------------------------------------------------------
>
>                 Key: LOG4J2-250
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-250
>             Project: Log4j 2
>          Issue Type: Improvement
>          Components: Core
>    Affects Versions: 2.0-beta5
>            Reporter: Remko Popma
>
> Background: during investigation of LOG4J2-245, several people raised 
> performance concerns regarding ThrowableProxy. This issue is to separate that 
> concern so that LOG4J2-245 can focus on resolving the EmptyStackException.
> ThrowableProxy does quite a bit of expensive-looking work to find the JAR 
> file or directory where the class that threw the exception is located. This 
> work is done for every event that contains an exception.
> However, this information is only used when the configuration contains either 
> a RootThrowablePattern {"rEx", "rThrowable", "rException" } or an 
> ExtendedThrowablePattern {"xEx", "xThrowable", "xException" } or when the 
> LogEvent is serialized.
> I propose to change the Log4jLogEvent implementation: instead of 
> unconditionally creating a ThrowableProxy object every time a log event 
> contains an exception, only wrap this exception in a ThrowableProxy when 
> either:
> * the {{getThrownProxy()}} method is called
> * the log event is serialized
> For reference, similar work has already been done on 
> {{o.a.l.l.core.async.RingBufferLogEvent}}.



--
This message was sent by Atlassian JIRA
(v6.2#6252)

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

Reply via email to