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 "Ralph Goers (JIRA)" <ji...@apache.org> on 2013/02/24 01:00:13 UTC
[jira] [Updated] (LOG4J2-154) ThreadContext performance
improvement: shallow copies for reads, deep copies for writes
[ https://issues.apache.org/jira/browse/LOG4J2-154?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Ralph Goers updated LOG4J2-154:
-------------------------------
Attachment: LOG4J2-154.patch
Based on what I saw from your code I have created my own version of a patch for this issue. Normally I follow the Commit-Then-Review model and have others review my commits after they are done. But in this case I would really like some feedback before I apply the patch to see if anyone can spot problems with it.
> ThreadContext performance improvement: shallow copies for reads, deep copies for writes
> ---------------------------------------------------------------------------------------
>
> Key: LOG4J2-154
> URL: https://issues.apache.org/jira/browse/LOG4J2-154
> Project: Log4j 2
> Issue Type: Improvement
> Components: Core
> Affects Versions: 2.0-beta3
> Reporter: Remko Popma
> Attachments: LOG4J2-154.patch, LOG4J2-154-patch-DefaultThreadContextMap.txt, LOG4J2-154-patch-ThreadContextMap.txt, LOG4J2-154-patch-ThreadContext.txt
>
>
> Currently, every time a Log4jLogEvent object is created, a deep copy is made of both the context map and the context stack. However, expected usage is that only a few objects are pushed onto the stack or put in the context map, while the number of LogEvents is in the thousands or millions.
> Essentially, there are far more reads than writes, so using a copy-on-write mechanism should give us better performance.
> Example context map put: deep copy (expensive but rare)
> public static void put(String key, String value) {
> if (!useMap) {
> return;
> }
> Map<String, String> map = localMap.get();
> Map<String, String> copy = null;
> if (map == null) {
> copy = new HashMap<String, String>();
> } else {
> copy = new HashMap<String, String>(map);
> }
> copy.put(key, value);
> localMap.set(copy);
> }
> Example context stack push: deep copy (expensive but rare)
> public static void push(String message) {
> if (!useStack) {
> return;
> }
> ContextStack stack = localStack.get();
> ContextStack copy = null;
> if (stack == null) {
> copy = new ThreadContextStack();
> } else {
> copy = stack.copy();
> }
> copy.push(message);
> localStack.set(copy);
> }
> Now, when the Log4jLogEvents are created, they just call ThreadContext.getImmutableContext and getImmutableStack. These methods return an unmodifiable wrapper around the most recent copy.
> Example for context map:
> public static Map<String, String> getImmutableContext() {
> Map<String, String> map = localMap.get();
> return map == null ? EMPTY_MAP : Collections.unmodifiableMap(map);
> }
> Example for context stack:
> public static ContextStack getImmutableStack() {
> ContextStack stack = localStack.get();
> return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList());
> }
> Note that ThreadContext.ThreadContextStack needs to be changed to contain an ArrayList rather than extend it, to facilitate making both deep mutable copies and shallow immutable copies.
> private static class ThreadContextStack implements ContextStack {
> private static final long serialVersionUID = 5050501L;
> private List<String> list;
> public ThreadContextStack() {
> list = new ArrayList<String>();
> }
> /**
> * This constructor uses the specified list as its internal data
> * structure unchanged. It does not make a defensive copy.
> */
> public ThreadContextStack(List<String> aList) {
> list = aList; // don't copy!
> }
> /**
> * This constructor copies the elements of the specified collection into
> * a new list. Changes to the specified collection will not affect this
> * {@code ThreadContextStack}.
> */
> public ThreadContextStack(Collection<String> collection) {
> list = new ArrayList<String>(collection);
> }
> public void clear() {
> list.clear();
> }
> public String pop() {
> int index = list.size() - 1;
> if (index >= 0) {
> String result = list.get(index);
> list.remove(index);
> return result;
> }
> throw new NoSuchElementException("The ThreadContext stack is empty");
> }
> public String peek() {
> int index = list.size() - 1;
> if (index >= 0) {
> return list.get(index);
> }
> return null;
> }
> public void push(String message) {
> list.add(message);
> }
> public int getDepth() {
> return list.size();
> }
> public List<String> asList() {
> return list;
> }
> public void trim(int depth) {
> if (depth < 0) {
> throw new IllegalArgumentException(
> "Maximum stack depth cannot be negative");
> }
> while (list.size() > depth) {
> list.remove(list.size() - 1);
> }
> }
> /**
> * Returns a deep copy of this stack.
> */
> public ContextStack copy() {
> return new ThreadContextStack(new ArrayList<>(list));
> }
> }
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org