You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by "Remko Popma (JIRA)" <ji...@apache.org> on 2016/08/22 16:44:20 UTC

[jira] [Updated] (LOG4J2-1349) Garbage-free ThreadContext map

     [ https://issues.apache.org/jira/browse/LOG4J2-1349?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Remko Popma updated LOG4J2-1349:
--------------------------------
    Attachment: DataStructOpsPerfMediumDataSet.png
                DataStructOpsPerfSmallDataSet.png
                InjectWithConfigProperties.png
                InjectWithoutConfigProperties.png
                ThreadContextPut.png

This is now available for review in branch {{LOG4J2-1349-gcfree-threadcontext}}.

* Moved ContextData and related interfaces to org.apache.logging.log4j.spi package in log4j-api
* There are two implementations: ArrayContextData and OpenHashMapContextData (also in the spi package)

*Benchmarks*

For benchmarking purposes, I created a copy-on-write ContextData-based and a garbage-free ContextData-based ThreadContextMap implementation for each of these data structures. These are compared against the current java.util.HashMap-based ThreadContextMap.

!ThreadContextPut.png!

This benchmark compares how fast data can be put into the thread context for each of the data structures. The 5, 50, 500 is the number of key-value pairs we put in the context. "Default" is the current copy-on-write HashMap<String, String> implementation that is in master. Note that the default implementation becomes very slow if we put hundreds of key-value pairs in the context. 

!InjectWithoutConfigProperties.png!

Injecting is the operation of copying data from the thread context to a log event. "Inject without config properties" is the happy case where there are no {{List<Property>}} key-value pairs from the configuration. For the copy-on-write thread context we can use the current thread context snapshot without any further modifications. This is very fast. (The "Default" current copy-on-write HashMap<String, String> implementation is not fast any more now that the LogEvent context data is a ContextData object and not a Map any more, so we need to copy the key-value pairs.)

The ArrayContextData data structure is optimized for fast copy and this works quite well; the Garbage-free ArrayContextData-based thread context performs good enough for smallish (less than 100 items) data sets. 

!InjectWithConfigProperties.png!

The other inject case is where we need to copy data from _both_ the thread context _and_ the configuration's {{List<Property>}} key-value pairs. Here we cannot just use the reference; we need to do some additional copying even if the thread context is copy-on-write. The ArrayContextData data structure (being optimized for fast copy) performs best here.

!DataStructOpsPerfSmallDataSet.png!

This benchmark and the next compare the performance of ArrayContextData, OpenHashMapContextData and java.util.HashMap for a number of basic operations:
* creating a copy ("clone" invokes the copy constructor)
* putAll on an empty instance
* put a single key-value pair
* get a single value for a specified key
* iterate over all key-value pairs. For the ContextData this is done with the BiConsumer and TriConsumer interface. For java.util.HashMap this is done with the standard Iterator.

The above benchmark is for a small data set of 5 items.

!DataStructOpsPerfMediumDataSet.png!

This graph shows the results of the same test for a medium size data set of 500 items. This shows that the ArrayContextData is optimized for fast iteration and fast initialization/copy. Getting and putting single values is not as fast as with a hash map. For our use case (less than 100 items) the difference is acceptable.

*Conclusion*

When the configuration does not declare any {{<Property>}} elements, the copy-on-write thread context is faster, otherwise the garbage-free thread context is faster.

Either way, an ArrayContextData-based thread context performs much better than the current java.util.HashMap-based implementation (now that LogEvents use ContextData instead of Map<String, String>).

*ThreadContext API*

No changes are required to the public ThreadContext API. I added a package-protected method {{ThreadContext::getThreadContextMap}} which will not be visible to user code.

> Garbage-free ThreadContext map
> ------------------------------
>
>                 Key: LOG4J2-1349
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-1349
>             Project: Log4j 2
>          Issue Type: Improvement
>          Components: API
>    Affects Versions: 2.5
>            Reporter: Remko Popma
>            Assignee: Remko Popma
>             Fix For: 2.7
>
>         Attachments: DataStructOpsPerfMediumDataSet.png, DataStructOpsPerfSmallDataSet.png, InjectWithConfigProperties.png, InjectWithoutConfigProperties.png, ThreadContextPut.png
>
>
> The current ThreadContext map and stack implementations allocate temporary objects. This ticket is to investigate and track the work for alternative implementations that are garbage-free.
> Both DefaultThreadContextMap and DefaultThreadContextStack are copy-on-write data structures: each modification replaces the ThreadLocal object with a modified copy. The advantage of this approach is that there is no need to make a copy for each LogEvent.
> Also, DefaultThreadContextMap uses a JDK map, the JDK collections tend to allocate a lot of temporary objects.



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

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org