From: Bryan Duxbury Date: Sat, 3 Apr 2010 22:52:03 +0000 (+0000) Subject: THRIFT-752. java: Use a faster Stack implementation in TCompactProtocol X-Git-Tag: 0.3.0~43 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=88569a25d1e8380574de16be541f9e6e18bbca12;p=common%2Fthrift.git THRIFT-752. java: Use a faster Stack implementation in TCompactProtocol This patch adds ShortStack, an internal implementation of Stack that works directly on primitive short objects, and makes TCompactProtocol use this. A brief performance test shows that this makes serialization about 8% faster and deserialization about 15% faster, though the actual gain you see will be dependent on the nature of your structs - the more levels, the more gain. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@930593 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/java/src/org/apache/thrift/ShortStack.java b/lib/java/src/org/apache/thrift/ShortStack.java new file mode 100644 index 00000000..4957d1c7 --- /dev/null +++ b/lib/java/src/org/apache/thrift/ShortStack.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.thrift; + +/** + * ShortStack is a short-specific Stack implementation written for the express + * purpose of very fast operations on TCompactProtocol's field id stack. This + * implementation performs at least 10x faster than java.util.Stack. + */ +public class ShortStack { + + private short[] vector; + private int top = -1; + + public ShortStack(int initialCapacity) { + vector = new short[initialCapacity]; + } + + public short pop() { + return vector[top--]; + } + + public void push(short pushed) { + if (vector.length == top + 1) { + grow(); + } + vector[++top] = pushed; + } + + private void grow() { + short[] newVector = new short[vector.length * 2]; + System.arraycopy(vector, 0, newVector, 0, vector.length); + vector = newVector; + } + + public short peek() { + return vector[top]; + } + + public void clear() { + top = -1; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(">"); + } + + sb.append(vector[i]); + + if (i == top) { + sb.append("<<"); + } + } + sb.append("]>"); + return sb.toString(); + } +} diff --git a/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java b/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java index 74bfc13c..7cc4f61c 100755 --- a/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java @@ -21,8 +21,8 @@ package org.apache.thrift.protocol; import java.io.UnsupportedEncodingException; -import java.util.Stack; +import org.apache.thrift.ShortStack; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransport; @@ -96,7 +96,7 @@ public final class TCompactProtocol extends TProtocol { * Used to keep track of the last field for the current and previous structs, * so we can do the delta stuff. */ - private Stack lastField_ = new Stack(); + private ShortStack lastField_ = new ShortStack(15); private short lastFieldId_ = 0; diff --git a/lib/java/test/org/apache/thrift/TestShortStack.java b/lib/java/test/org/apache/thrift/TestShortStack.java new file mode 100644 index 00000000..07831e52 --- /dev/null +++ b/lib/java/test/org/apache/thrift/TestShortStack.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.thrift; + +import java.util.Stack; + +import junit.framework.TestCase; + +public class TestShortStack extends TestCase { + private static final int NUM_TRIALS = 5; + private static final int NUM_REPS = 10000000; + + public void testOps() throws Exception { + ShortStack s = new ShortStack(10); + s.push((short)10); + s.push((short)11); + s.push((short)12); + assertEquals((short)12, s.peek()); + assertEquals((short)12, s.peek()); + assertEquals((short)12, s.pop()); + assertEquals((short)11, s.pop()); + s.push((short)40); + assertEquals((short)40, s.peek()); + assertEquals((short)40, s.pop()); + assertEquals((short)10, s.peek()); + assertEquals((short)10, s.pop()); + try { + s.peek(); + fail("should have thrown an exception!"); + } catch (Exception e) { + // yay + } + + try { + s.pop(); + fail("should have thrown an exception!"); + } catch (Exception e) { + // yay + } + } + + public void testGrow() throws Exception { + ShortStack s = new ShortStack(1); + s.push((short)1); + s.push((short)1); + s.push((short)1); + s.push((short)1); + s.push((short)1); + } + + public static void main(String[] args) throws Exception { + for (int trial = 0; trial < NUM_TRIALS; trial++) { + long start = System.currentTimeMillis(); + ShortStack s = new ShortStack(10); + for (int rep = 0; rep < NUM_REPS; rep++) { + s.push((short)1); + s.push((short)11); + s.push((short)111); + s.pop(); + s.pop(); + s.push((short)12); + s.push((short)121); + s.push((short)1211); + s.push((short)12111); + s.pop(); + s.pop(); + s.pop(); + s.pop(); + s.push((short)5); + s.pop(); + s.pop(); + } + long end = System.currentTimeMillis(); + System.out.println("ShortStack: " + (end-start)); + + start = System.currentTimeMillis(); + Stack stdStack = new Stack(); + for (int rep = 0; rep < NUM_REPS; rep++) { + stdStack.push((short)1); + stdStack.push((short)11); + stdStack.push((short)111); + stdStack.pop(); + stdStack.pop(); + stdStack.push((short)12); + stdStack.push((short)121); + stdStack.push((short)1211); + stdStack.push((short)12111); + stdStack.pop(); + stdStack.pop(); + stdStack.pop(); + stdStack.pop(); + stdStack.push((short)5); + stdStack.pop(); + stdStack.pop(); + } + end = System.currentTimeMillis(); + System.out.println("Built-in stack: " + (end-start)); + } + } +} diff --git a/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java b/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java index f8e316ac..365cef7f 100644 --- a/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java +++ b/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java @@ -368,4 +368,33 @@ public abstract class ProtocolTestBase extends TestCase { public abstract void writeMethod(TProtocol proto) throws TException; public abstract void readMethod(TProtocol proto) throws TException; } + + private static final int NUM_TRIALS = 5; + private static final int NUM_REPS = 10000; + + protected void benchmark() throws Exception { + for (int trial = 0; trial < NUM_TRIALS; trial++) { + TSerializer ser = new TSerializer(getFactory()); + byte[] serialized = null; + long serStart = System.currentTimeMillis(); + for (int rep = 0; rep < NUM_REPS; rep++) { + serialized = ser.serialize(Fixtures.holyMoley); + } + long serEnd = System.currentTimeMillis(); + long serElapsed = serEnd - serStart; + System.out.println("Ser:\t" + serElapsed + "ms\t" + + ((double)serElapsed / NUM_REPS) + "ms per serialization"); + + HolyMoley cpts = new HolyMoley(); + TDeserializer deser = new TDeserializer(getFactory()); + long deserStart = System.currentTimeMillis(); + for (int rep = 0; rep < NUM_REPS; rep++) { + deser.deserialize(cpts, serialized); + } + long deserEnd = System.currentTimeMillis(); + long deserElapsed = deserEnd - deserStart; + System.out.println("Des:\t" + deserElapsed + "ms\t" + + ((double)deserElapsed / NUM_REPS) + "ms per deserialization"); + } + } } diff --git a/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java b/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java index 7d0a37c3..fb134e69 100755 --- a/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java +++ b/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java @@ -33,4 +33,8 @@ public class TestTCompactProtocol extends ProtocolTestBase { protected boolean canBeUsedNaked() { return true; } + + public static void main(String args[]) throws Exception { + new TestTCompactProtocol().benchmark(); + } } \ No newline at end of file