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 0000000..4957d1c
--- /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("<ShortStack vector:[");
+ for (int i = 0; i < vector.length; i++) {
+ if (i != 0) {
+ sb.append(" ");
+ }
+
+ if (i == top) {
+ 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 74bfc13..7cc4f61 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 @@
* Used to keep track of the last field for the current and previous structs,
* so we can do the delta stuff.
*/
- private Stack<Short> lastField_ = new Stack<Short>();
+ 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 0000000..07831e5
--- /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<Short> stdStack = new Stack<Short>();
+ 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 f8e316a..365cef7 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 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 7d0a37c..fb134e6 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 @@
protected boolean canBeUsedNaked() {
return true;
}
+
+ public static void main(String args[]) throws Exception {
+ new TestTCompactProtocol().benchmark();
+ }
}
\ No newline at end of file