Author: markt Date: Sun Sep 23 19:12:44 2012 New Revision: 1389122 URL: http://svn.apache.org/viewvc?rev=1389122&view=rev Log: Expand functionality of ConcurrentStack and then switch to ConcurrentStack removing ~25% of the remaining object allocation during load test. No noticeable performance hit.
Modified: tomcat/trunk/java/org/apache/catalina/valves/Constants.java tomcat/trunk/java/org/apache/tomcat/util/collections/ConcurrentStack.java tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java tomcat/trunk/test/org/apache/tomcat/util/collections/TestConcurrentStack.java Modified: tomcat/trunk/java/org/apache/catalina/valves/Constants.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/Constants.java?rev=1389122&r1=1389121&r2=1389122&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/valves/Constants.java (original) +++ tomcat/trunk/java/org/apache/catalina/valves/Constants.java Sun Sep 23 19:12:44 2012 @@ -76,4 +76,12 @@ public final class Constants { public static final char[] SC_SERVICE_UNAVAILABLE_CHAR = new char[] {'5', '0', '3'}; public static final char[] SC_GATEWAY_TIMEOUT_CHAR = new char[] {'5', '0', '4'}; public static final char[] SC_HTTP_VERSION_NOT_SUPPORTED_CHAR = new char[] {'5', '0', '5'}; + + public static final char[][] OCTETS = new char[256][]; + + static { + for (int i = 0; i < 256; i++) { + OCTETS[i] = Integer.toString(i).toCharArray(); + } + } } Modified: tomcat/trunk/java/org/apache/tomcat/util/collections/ConcurrentStack.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/collections/ConcurrentStack.java?rev=1389122&r1=1389121&r2=1389122&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/collections/ConcurrentStack.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/collections/ConcurrentStack.java Sun Sep 23 19:12:44 2012 @@ -25,18 +25,40 @@ package org.apache.tomcat.util.collectio */ public class ConcurrentStack<T> { - private int size = 128; + public static final int DEFAULT_SIZE = 128; + private static final int DEFAULT_LIMIT = -1; + + private int size; + private int limit; + /* * Points to the next available object in the stack */ private int index = -1; - private Object[] stack = new Object[size]; + private Object[] stack; + + + public ConcurrentStack() { + this(DEFAULT_SIZE, DEFAULT_LIMIT); + } + + public ConcurrentStack(int size, int limit) { + this.size = size; + this.limit = limit; + stack = new Object[size]; + } + public synchronized void push(T obj) { index++; if (index == size) { - expand(); + if (limit == -1 || size < limit) { + expand(); + } else { + index--; + return; + } } stack[index] = obj; } @@ -46,11 +68,25 @@ public class ConcurrentStack<T> { if (index == -1) { return null; } - return (T) stack[index--]; + T result = (T) stack[index]; + stack[index--] = null; + return result; + } + + public synchronized void clear() { + if (index > -1) { + for (int i = 0; i < index + 1; i++) { + stack[i] = null; + } + } + index = -1; } private void expand() { int newSize = size * 2; + if (limit != -1 && newSize > limit) { + newSize = limit; + } Object[] newStack = new Object[newSize]; System.arraycopy(stack, 0, newStack, 0, size); // This is the only point where garbage is created by throwing away the Modified: tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java?rev=1389122&r1=1389121&r2=1389122&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/NioEndpoint.java Sun Sep 23 19:12:44 2012 @@ -51,6 +51,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.IntrospectionUtils; +import org.apache.tomcat.util.collections.ConcurrentStack; import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler; import org.apache.tomcat.util.net.jsse.NioX509KeyManager; @@ -126,157 +127,30 @@ public class NioEndpoint extends Abstrac /** * Cache for SocketProcessor objects */ - protected ConcurrentLinkedQueue<SocketProcessor> processorCache = new ConcurrentLinkedQueue<SocketProcessor>() { - private static final long serialVersionUID = 1L; - protected AtomicInteger size = new AtomicInteger(0); - @Override - public boolean offer(SocketProcessor sc) { - sc.reset(null,null); - boolean offer = socketProperties.getProcessorCache()==-1?true:size.get()<socketProperties.getProcessorCache(); - //avoid over growing our cache or add after we have stopped - if ( running && (!paused) && (offer) ) { - boolean result = super.offer(sc); - if ( result ) { - size.incrementAndGet(); - } - return result; - } - else return false; - } - - @Override - public SocketProcessor poll() { - SocketProcessor result = super.poll(); - if ( result != null ) { - size.decrementAndGet(); - } - return result; - } - - @Override - public void clear() { - super.clear(); - size.set(0); - } - }; - + protected final ConcurrentStack<SocketProcessor> processorCache = + new ConcurrentStack<>(ConcurrentStack.DEFAULT_SIZE, + socketProperties.getProcessorCache()); /** * Cache for key attachment objects */ - protected ConcurrentLinkedQueue<KeyAttachment> keyCache = new ConcurrentLinkedQueue<KeyAttachment>() { - private static final long serialVersionUID = 1L; - protected AtomicInteger size = new AtomicInteger(0); - @Override - public boolean offer(KeyAttachment ka) { - ka.reset(); - boolean offer = socketProperties.getKeyCache()==-1?true:size.get()<socketProperties.getKeyCache(); - //avoid over growing our cache or add after we have stopped - if ( running && (!paused) && (offer) ) { - boolean result = super.offer(ka); - if ( result ) { - size.incrementAndGet(); - } - return result; - } - else return false; - } - - @Override - public KeyAttachment poll() { - KeyAttachment result = super.poll(); - if ( result != null ) { - size.decrementAndGet(); - } - return result; - } - - @Override - public void clear() { - super.clear(); - size.set(0); - } - }; - + protected final ConcurrentStack<KeyAttachment> keyCache = + new ConcurrentStack<>(ConcurrentStack.DEFAULT_SIZE, + socketProperties.getKeyCache()); /** * Cache for poller events */ - protected ConcurrentLinkedQueue<PollerEvent> eventCache = new ConcurrentLinkedQueue<PollerEvent>() { - private static final long serialVersionUID = 1L; - protected AtomicInteger size = new AtomicInteger(0); - @Override - public boolean offer(PollerEvent pe) { - pe.reset(); - boolean offer = socketProperties.getEventCache()==-1?true:size.get()<socketProperties.getEventCache(); - //avoid over growing our cache or add after we have stopped - if ( running && (!paused) && (offer) ) { - boolean result = super.offer(pe); - if ( result ) { - size.incrementAndGet(); - } - return result; - } - else return false; - } - - @Override - public PollerEvent poll() { - PollerEvent result = super.poll(); - if ( result != null ) { - size.decrementAndGet(); - } - return result; - } - - @Override - public void clear() { - super.clear(); - size.set(0); - } - }; - + protected final ConcurrentStack<PollerEvent> eventCache = + new ConcurrentStack<>(ConcurrentStack.DEFAULT_SIZE, + socketProperties.getEventCache()); /** * Bytebuffer cache, each channel holds a set of buffers (two, except for SSL holds four) */ - protected ConcurrentLinkedQueue<NioChannel> nioChannels = new ConcurrentLinkedQueue<NioChannel>() { - private static final long serialVersionUID = 1L; - protected AtomicInteger size = new AtomicInteger(0); - protected AtomicInteger bytes = new AtomicInteger(0); - @Override - public boolean offer(NioChannel socket) { - boolean offer = socketProperties.getBufferPool()==-1?true:size.get()<socketProperties.getBufferPool(); - offer = offer && (socketProperties.getBufferPoolSize()==-1?true:(bytes.get()+socket.getBufferSize())<socketProperties.getBufferPoolSize()); - //avoid over growing our cache or add after we have stopped - if ( running && (!paused) && (offer) ) { - boolean result = super.offer(socket); - if ( result ) { - size.incrementAndGet(); - bytes.addAndGet(socket.getBufferSize()); - } - return result; - } - else return false; - } - - @Override - public NioChannel poll() { - NioChannel result = super.poll(); - if ( result != null ) { - size.decrementAndGet(); - bytes.addAndGet(-result.getBufferSize()); - } - return result; - } - - @Override - public void clear() { - super.clear(); - size.set(0); - bytes.set(0); - } - }; + protected final ConcurrentStack<NioChannel> nioChannels = + new ConcurrentStack<>(ConcurrentStack.DEFAULT_SIZE, + socketProperties.getBufferPoolSize()); // ------------------------------------------------------------- Properties @@ -663,7 +537,7 @@ public class NioEndpoint extends Abstrac Socket sock = socket.socket(); socketProperties.setProperties(sock); - NioChannel channel = nioChannels.poll(); + NioChannel channel = nioChannels.pop(); if ( channel == null ) { // SSL setup if (sslContext != null) { @@ -747,7 +621,7 @@ public class NioEndpoint extends Abstrac return false; } attachment.setCometNotify(false); //will get reset upon next reg - SocketProcessor sc = processorCache.poll(); + SocketProcessor sc = processorCache.pop(); if ( sc == null ) sc = new SocketProcessor(socket,status); else sc.reset(socket,status); if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc); @@ -1020,7 +894,7 @@ public class NioEndpoint extends Abstrac } public void add(final NioChannel socket, final int interestOps) { - PollerEvent r = eventCache.poll(); + PollerEvent r = eventCache.pop(); if ( r==null) r = new PollerEvent(socket,null,interestOps); else r.reset(socket,null,interestOps); if ( (interestOps&OP_CALLBACK) == OP_CALLBACK ) { @@ -1048,7 +922,9 @@ public class NioEndpoint extends Abstrac r.run(); if ( r instanceof PollerEvent ) { ((PollerEvent)r).reset(); - eventCache.offer((PollerEvent)r); + if (running && !paused) { + eventCache.push((PollerEvent)r); + } } } catch ( Throwable x ) { log.error("",x); @@ -1061,11 +937,11 @@ public class NioEndpoint extends Abstrac public void register(final NioChannel socket) { socket.setPoller(this); - KeyAttachment key = keyCache.poll(); + KeyAttachment key = keyCache.pop(); final KeyAttachment ka = key!=null?key:new KeyAttachment(socket); ka.reset(this,socket,getSocketProperties().getSoTimeout()); ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests()); - PollerEvent r = eventCache.poll(); + PollerEvent r = eventCache.pop(); ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER); else r.reset(socket,ka,OP_REGISTER); @@ -1699,9 +1575,13 @@ public class NioEndpoint extends Abstrac try { if (ka!=null) ka.setComet(false); socket.getPoller().cancelledKey(key, SocketStatus.ERROR); - nioChannels.offer(socket); + if (running && !paused) { + nioChannels.push(socket); + } socket = null; - if ( ka!=null ) keyCache.offer(ka); + if (running && !paused && ka != null) { + keyCache.push(ka); + } ka = null; }catch ( Exception x ) { log.error("",x); @@ -1716,9 +1596,13 @@ public class NioEndpoint extends Abstrac ka = (KeyAttachment) key.attachment(); socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT); } - nioChannels.offer(socket); + if (running && !paused) { + nioChannels.push(socket); + } socket = null; - if ( ka!=null ) keyCache.offer(ka); + if (running && !paused && ka != null) { + keyCache.push(ka); + } ka = null; } else { final SelectionKey fk = key; @@ -1759,7 +1643,9 @@ public class NioEndpoint extends Abstrac socket = null; status = null; //return to cache - processorCache.offer(this); + if (running && !paused) { + processorCache.push(this); + } } } } Modified: tomcat/trunk/test/org/apache/tomcat/util/collections/TestConcurrentStack.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/collections/TestConcurrentStack.java?rev=1389122&r1=1389121&r2=1389122&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/collections/TestConcurrentStack.java (original) +++ tomcat/trunk/test/org/apache/tomcat/util/collections/TestConcurrentStack.java Sun Sep 23 19:12:44 2012 @@ -75,4 +75,45 @@ public class TestConcurrentStack { Assert.assertNull(stack.pop()); } + @Test + public void testLimit() { + ConcurrentStack<Object> stack = new ConcurrentStack<>(2,2); + + Object o1 = new Object(); + Object o2 = new Object(); + Object o3 = new Object(); + Object o4 = new Object(); + + stack.push(o1); + stack.push(o2); + stack.push(o3); + stack.push(o4); + + Assert.assertSame(stack.pop(), o2); + Assert.assertSame(stack.pop(), o1); + + Assert.assertNull(stack.pop()); + } + + + @Test + public void testLimitExpand() { + ConcurrentStack<Object> stack = new ConcurrentStack<>(1,3); + + Object o1 = new Object(); + Object o2 = new Object(); + Object o3 = new Object(); + Object o4 = new Object(); + + stack.push(o1); + stack.push(o2); + stack.push(o3); + stack.push(o4); + + Assert.assertSame(stack.pop(), o3); + Assert.assertSame(stack.pop(), o2); + Assert.assertSame(stack.pop(), o1); + + Assert.assertNull(stack.pop()); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org