├── .gitignore
├── LICENSE
├── fasttuple-bench
├── pom.xml
└── src
│ └── main
│ └── java
│ ├── com
│ └── boundary
│ │ └── tuple
│ │ ├── AccessMethodBenchmark.java
│ │ ├── Container.java
│ │ └── FastTupleBenchmarks.java
│ └── other
│ └── FastObjectPool.java
├── fasttuple-core
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── boundary
│ │ └── tuple
│ │ ├── Destroyer.java
│ │ ├── DirectTupleSchema.java
│ │ ├── FastTuple.java
│ │ ├── HeapTupleSchema.java
│ │ ├── Loader.java
│ │ ├── SizeOf.java
│ │ ├── TuplePool.java
│ │ ├── TupleSchema.java
│ │ ├── codegen
│ │ ├── CodegenUtil.java
│ │ ├── DirectTupleCodeGenerator.java
│ │ ├── HeapTupleCodeGenerator.java
│ │ ├── TupleAllocatorGenerator.java
│ │ ├── TupleCodeGenerator.java
│ │ └── TupleExpressionGenerator.java
│ │ └── unsafe
│ │ └── Coterie.java
│ └── test
│ └── java
│ └── com
│ └── boundary
│ └── tuple
│ ├── DirectTupleSchemaTest.java
│ ├── TuplePoolTest.java
│ └── codegen
│ ├── DirectTupleCodeGeneratorTest.java
│ ├── HeapTupleCodeGeneratorTest.java
│ └── TupleExpressionGeneratorTest.java
├── pom.xml
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | target
3 | *.iml
4 | hs_err*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2014 Boundary
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/fasttuple-bench/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
26 | 4.0.0
27 |
28 | fasttuple
29 | com.boundary
30 | 0.1-SNAPSHOT
31 |
32 |
33 | com.boundary
34 | fasttuple-bench
35 | 0.1-SNAPSHOT
36 | jar
37 |
38 | Auto-generated JMH benchmark
39 |
40 |
41 |
42 | org.openjdk.jmh
43 | jmh-core
44 | 0.6
45 |
46 |
47 | org.openjdk.jmh
48 | jmh-generator-annprocess
49 | 0.6
50 | provided
51 |
52 |
53 | com.boundary
54 | fasttuple-core
55 | ${project.version}
56 |
57 |
58 | nf.fr.eraasoft
59 | objectpool
60 | 1.1.2
61 |
62 |
63 |
64 |
65 | UTF-8
66 |
67 |
68 |
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-compiler-plugin
73 | 3.0
74 |
75 | 1.7
76 | 1.7
77 | 1.7
78 |
79 |
80 |
81 | org.apache.maven.plugins
82 | maven-shade-plugin
83 | 2.0
84 |
85 |
86 | package
87 |
88 | shade
89 |
90 |
91 | microbenchmarks
92 |
93 |
94 | org.openjdk.jmh.Main
95 |
96 |
97 |
98 |
99 | *:*
100 |
101 | META-INF/*.SF
102 | META-INF/*.DSA
103 | META-INF/*.RSA
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/fasttuple-bench/src/main/java/com/boundary/tuple/AccessMethodBenchmark.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.boundary.tuple.unsafe.Coterie;
4 | import nf.fr.eraasoft.pool.ObjectPool;
5 | import nf.fr.eraasoft.pool.PoolException;
6 | import nf.fr.eraasoft.pool.PoolSettings;
7 | import nf.fr.eraasoft.pool.PoolableObjectBase;
8 | import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
9 | import org.openjdk.jmh.annotations.Scope;
10 | import org.openjdk.jmh.annotations.State;
11 | import other.FastObjectPool;
12 | import sun.misc.Unsafe;
13 |
14 | import java.lang.invoke.ConstantCallSite;
15 | import java.lang.invoke.MethodHandles;
16 | import java.lang.reflect.Field;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.concurrent.ArrayBlockingQueue;
20 | import java.util.concurrent.BlockingQueue;
21 |
22 | @State(Scope.Benchmark)
23 | public class AccessMethodBenchmark {
24 | private DirectTupleSchema schema;
25 | private static final Unsafe unsafe = Coterie.unsafe();
26 | private BlockingQueue containers;
27 | private PoolSettings poolSettings = new PoolSettings(
28 | new PoolableObjectBase() {
29 | @Override
30 | public Container make() throws PoolException {
31 | return new Container(0,0,(short)0);
32 | }
33 |
34 | @Override
35 | public void activate(Container container) throws PoolException {
36 | container.a = 0;
37 | container.b = 0;
38 | container.c = 0;
39 | }
40 | }
41 | );
42 | private FastObjectPool pool2;
43 | TuplePool pool3;
44 | ObjectPool pool;
45 | long record2;
46 | Field fieldA;
47 | Field fieldB;
48 | Field fieldC;
49 | ConstantCallSite mhsa;
50 | ConstantCallSite mhsb;
51 | ConstantCallSite mhsc;
52 | ConstantCallSite mhga;
53 | ConstantCallSite mhgb;
54 | ConstantCallSite mhgc;
55 |
56 | public AccessMethodBenchmark() {
57 | try {
58 | containers = new ArrayBlockingQueue(100);
59 | containers.offer(new Container(0,0,(short)0));
60 | schema = TupleSchema.builder().
61 | addField("a", Long.TYPE).
62 | addField("b", Integer.TYPE).
63 | addField("c", Short.TYPE).
64 | implementInterface(StaticBinding.class).
65 | directMemory().
66 | build();
67 | record2 = schema.createRecord();
68 | poolSettings.min(1).max(10);
69 | pool = poolSettings.pool();
70 | pool2 = new FastObjectPool(new FastObjectPool.PoolFactory() {
71 | @Override
72 | public Container create() {
73 | return new Container(0,0,(short)0);
74 | }
75 | }, 10);
76 | pool3 = new TuplePool(10, false,
77 | new Loader() {
78 | @Override
79 | public Container[] createArray(int size) throws Exception {
80 | Container[] ary = new Container[size];
81 | for (int i=0; i() {
88 | @Override
89 | public void destroyArray(Container[] ary) {
90 |
91 | }
92 | }
93 | );
94 |
95 | fieldA = Container.class.getDeclaredField("a");
96 | fieldB = Container.class.getDeclaredField("b");
97 | fieldC = Container.class.getDeclaredField("c");
98 | MethodHandles.Lookup lookup = MethodHandles.lookup();
99 | mhsa = new ConstantCallSite(lookup.findSetter(Container.class, "a", Long.TYPE));
100 | mhsb = new ConstantCallSite(lookup.findSetter(Container.class, "b", Integer.TYPE));
101 | mhsc = new ConstantCallSite(lookup.findSetter(Container.class, "c", Short.TYPE));
102 | mhga = new ConstantCallSite(lookup.findGetter(Container.class, "a", Long.TYPE));
103 | mhgb = new ConstantCallSite(lookup.findGetter(Container.class, "b", Integer.TYPE));
104 | mhgc = new ConstantCallSite(lookup.findGetter(Container.class, "c", Short.TYPE));
105 | } catch (Exception ex) {
106 |
107 | }
108 | }
109 |
110 | @GenerateMicroBenchmark
111 | public long testAllocateSetAndDeallocate() {
112 | long record = schema.createRecord();
113 | schema.setLong(record, 0, 100);
114 | schema.setInt(record, 1, 200);
115 | schema.setShort(record, 2, (short)300);
116 |
117 | long r = schema.getLong(record, 0) + schema.getInt(record, 1) + schema.getShort(record, 2);
118 | schema.destroy(record);
119 | return r;
120 | }
121 |
122 | @GenerateMicroBenchmark
123 | public long testOffheapSchemaSet() {
124 | schema.setLong(record2, 0, 100);
125 | schema.setInt(record2, 1, 200);
126 | schema.setShort(record2, 2, (short)300);
127 | return schema.getLong(record2, 0) + schema.getInt(record2, 1) + schema.getShort(record2, 2);
128 | }
129 |
130 | @GenerateMicroBenchmark
131 | public long testOffheapAllocateAndSet() {
132 | long record = unsafe.allocateMemory(8 + 4 + 2);
133 | unsafe.putLong(record, 100);
134 | unsafe.putInt(record+8, 200);
135 | unsafe.putShort(record+12, (short)300);
136 | long r = unsafe.getLong(record) + unsafe.getInt(record+8) + unsafe.getShort(record+12);
137 | unsafe.freeMemory(record);
138 | return r;
139 | }
140 |
141 | @GenerateMicroBenchmark
142 | public long testOffheapDirectSet() {
143 | unsafe.putLong(record2 + 0L, 100);
144 | unsafe.putInt(record2 + 8L, 200);
145 | unsafe.putShort(record2 + 12L, (short)300);
146 | return unsafe.getLong(record2 + 0L) + unsafe.getInt(record2 + 8L) + unsafe.getShort(record2 + 12L);
147 | }
148 |
149 | @GenerateMicroBenchmark
150 | public long testInvokeDynamic() throws Throwable {
151 | Container container = new Container(0,0,(short)0);
152 | mhsa.dynamicInvoker().invoke(container, 100L);
153 | mhsb.dynamicInvoker().invoke(container, 200);
154 | mhsc.dynamicInvoker().invoke(container, (short)300L);
155 | return (Long)mhga.dynamicInvoker().invoke(container) + (Integer)mhgb.dynamicInvoker().invoke(container) + (Short)mhgc.dynamicInvoker().invoke(container);
156 | }
157 |
158 | @GenerateMicroBenchmark
159 | public long testStormTuple() {
160 | List list = new ArrayList();
161 | list.add(100L);
162 | list.add(200L);
163 | list.add(300L);
164 | return list.get(0) + list.get(1) + list.get(2);
165 | }
166 |
167 | @GenerateMicroBenchmark
168 | public long testLongArray() {
169 | long[] longs = new long[3];
170 | longs[0] = 100L;
171 | longs[1] = 200;
172 | longs[2] = (short)300;
173 | return longs[0] + longs[1] + longs[2];
174 | }
175 |
176 | @GenerateMicroBenchmark
177 | public long testClass() {
178 | Container container = new Container(0, 0, (short)0);
179 | container.a = 100;
180 | container.b = 200;
181 | container.c = 300;
182 | return container.a + container.b + container.c;
183 | }
184 |
185 | @GenerateMicroBenchmark
186 | public long testReflectField() throws Exception {
187 | Container container = new Container(0, 0, (short)0);
188 | fieldA.setLong(container, 100);
189 | fieldB.setInt(container, 200);
190 | fieldC.setShort(container, (short)300);
191 | return fieldA.getLong(container) + fieldB.getInt(container) + fieldC.getShort(container);
192 | }
193 |
194 | @GenerateMicroBenchmark
195 | public long testQueuedObject() throws InterruptedException {
196 | Container container = containers.take();
197 | container.a = 100;
198 | container.b = 200;
199 | container.c = 300;
200 | long r = container.a + container.b + container.c;
201 | containers.offer(container);
202 | return r;
203 | }
204 |
205 | @GenerateMicroBenchmark
206 | public long testPooledObject() throws Exception {
207 | Container container = pool.getObj();
208 | container.a = 100;
209 | container.b = 200;
210 | container.c = 300;
211 | long l = container.a + container.b + container.c;
212 | pool.returnObj(container);
213 | return l;
214 | }
215 |
216 | @GenerateMicroBenchmark
217 | public long testFastPool() throws Exception {
218 | FastObjectPool.Holder holder = pool2.take();
219 | Container container = holder.getValue();
220 | container.a = 100;
221 | container.b = 200;
222 | container.c = 300;
223 | long r = container.a + container.b + container.c;
224 | pool2.release(holder);
225 | return r;
226 | }
227 |
228 | @GenerateMicroBenchmark
229 | public long testTuplePool() throws Exception {
230 | Container container = pool3.checkout();
231 | container.a = 100;
232 | container.b = 200;
233 | container.c = 300;
234 | long r = container.a + container.b + container.c;
235 | pool3.release(container);
236 | return r;
237 | }
238 |
239 | @GenerateMicroBenchmark
240 | public long testFastTuplePreAllocIndexedBoxing() throws Exception {
241 | FastTuple tuple = schema.createTuple(record2);
242 | tuple.set(1, 100L);
243 | tuple.set(2, 200);
244 | tuple.set(3, (short) 300);
245 | return (Long)tuple.get(1) + (Integer)tuple.get(2) + (Short)tuple.get(3);
246 | }
247 |
248 | @GenerateMicroBenchmark
249 | public long testFastTuplePreAllocIndexed() throws Exception {
250 | FastTuple tuple = schema.createTuple(record2);
251 | tuple.setLong(1, 100L);
252 | tuple.setInt(2, 200);
253 | tuple.setShort(3, (short) 300);
254 | return tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
255 | }
256 |
257 | @GenerateMicroBenchmark
258 | public long testFastTupleStaticBinding() throws Exception {
259 | StaticBinding tuple = (StaticBinding)schema.createTuple(record2);
260 | tuple.a(100L);
261 | tuple.b(200);
262 | tuple.c((short)300);
263 | return tuple.a() + tuple.b() + tuple.c();
264 | }
265 |
266 |
267 |
268 | public static interface StaticBinding {
269 | public void a(long a);
270 | public void b(int b);
271 | public void c(short c);
272 | public long a();
273 | public int b();
274 | public short c();
275 | }
276 |
277 | }
278 |
279 |
280 |
--------------------------------------------------------------------------------
/fasttuple-bench/src/main/java/com/boundary/tuple/Container.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | /**
4 | * Created by cliff on 5/12/14.
5 | */
6 | class Container {
7 | public long a;
8 | public int b;
9 | public short c;
10 |
11 | public Container(long a, int b, short c) {
12 | this.a = a;
13 | this.b = b;
14 | this.c = c;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/fasttuple-bench/src/main/java/com/boundary/tuple/FastTupleBenchmarks.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.boundary.tuple.codegen.TupleExpressionGenerator;
4 | import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
5 | import org.openjdk.jmh.annotations.Scope;
6 | import org.openjdk.jmh.annotations.Setup;
7 | import org.openjdk.jmh.annotations.State;
8 |
9 | import java.util.ArrayDeque;
10 |
11 | /**
12 | * Created by cliff on 5/12/14.
13 | */
14 | public class FastTupleBenchmarks {
15 |
16 | @State(Scope.Benchmark)
17 | public static class DirectSchema {
18 | public DirectTupleSchema schema;
19 | public TupleExpressionGenerator.TupleExpression eval1;
20 | public TupleExpressionGenerator.LongTupleExpression eval2;
21 | public ArrayDeque deque;
22 | public FastTuple tuple;
23 |
24 | @Setup
25 | public void setup() throws Exception {
26 | schema = TupleSchema.builder().
27 | addField("a", Long.TYPE).
28 | addField("b", Integer.TYPE).
29 | addField("c", Short.TYPE).
30 | implementInterface(StaticBinding.class).
31 | poolOfSize(10).
32 | directMemory().
33 | build();
34 | eval1 = TupleExpressionGenerator.builder().
35 | expression("tuple.a(100L), tuple.b(200), tuple.c((short)300)").
36 | schema(schema).
37 | returnVoid();
38 | eval2 = TupleExpressionGenerator.builder().
39 | expression("tuple.a() + tuple.b() + tuple.c()").
40 | schema(schema).
41 | returnLong();
42 |
43 | deque = new ArrayDeque<>();
44 | for (FastTuple tuple : schema.createTupleArray(10)) {
45 | deque.push(tuple);
46 | }
47 |
48 | tuple = schema.createTuple();
49 | }
50 | }
51 |
52 | @State(Scope.Benchmark)
53 | public static class HeapSchema {
54 | public HeapTupleSchema schema;
55 | public TupleExpressionGenerator.TupleExpression eval1;
56 | public TupleExpressionGenerator.LongTupleExpression eval2;
57 | public TupleExpressionGenerator.TupleExpression eval3;
58 | public TupleExpressionGenerator.LongTupleExpression eval4;
59 | public ArrayDeque deque;
60 | public FastTuple tuple;
61 |
62 | @Setup
63 | public void setup() throws Exception {
64 | schema = TupleSchema.builder().
65 | addField("a", Long.TYPE).
66 | addField("b", Integer.TYPE).
67 | addField("c", Short.TYPE).
68 | poolOfSize(10).
69 | implementInterface(StaticBinding.class).
70 | heapMemory().
71 | build();
72 | eval1 = TupleExpressionGenerator.builder().
73 | expression("tuple.a(100L), tuple.b(200), tuple.c((short)300)").
74 | schema(schema).
75 | returnVoid();
76 | eval2 = TupleExpressionGenerator.builder().
77 | expression("tuple.a() + tuple.b() + tuple.c()").
78 | schema(schema).
79 | returnLong();
80 |
81 | eval3 = TupleExpressionGenerator.builder().
82 | expression("tuple.a = 100L, tuple.b = 200, tuple.c = (short)300").
83 | schema(schema).
84 | returnVoid();
85 | eval4 = TupleExpressionGenerator.builder().
86 | expression("tuple.a + tuple.b + tuple.c").
87 | schema(schema).
88 | returnLong();
89 |
90 | deque = new ArrayDeque<>();
91 | for (FastTuple tuple : schema.createTupleArray(10)) {
92 | deque.push(tuple);
93 | }
94 |
95 | tuple = schema.createTuple();
96 | }
97 | }
98 |
99 | public static interface StaticBinding {
100 | public void a(long a);
101 | public void b(int b);
102 | public void c(short c);
103 | public long a();
104 | public int b();
105 | public short c();
106 | }
107 |
108 | public static class DirectBenchmarks {
109 | @GenerateMicroBenchmark
110 | public long measureDirectSchemaAllocate(DirectSchema ds) throws Exception {
111 | FastTuple tuple = ds.schema.createTuple();
112 | long r = tuple.getLong(1);
113 | ds.schema.destroy(tuple);
114 | return r;
115 | }
116 |
117 | @GenerateMicroBenchmark
118 | public long measureDirectSchemaDeque(DirectSchema ds) throws Exception {
119 | FastTuple tuple = ds.deque.pop();
120 |
121 | tuple.setLong(1, 100L);
122 | tuple.setInt(2, 200);
123 | tuple.setShort(3, (short) 300);
124 |
125 | long r = tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
126 | ds.deque.push(tuple);
127 | return r;
128 | }
129 |
130 | @GenerateMicroBenchmark
131 | public long measureDirectSchemaPoolIndexedBoxed(DirectSchema ds) throws Exception {
132 | FastTuple tuple = ds.schema.pool().checkout();
133 |
134 | tuple.set(1, 100L);
135 | tuple.set(2, 200);
136 | tuple.set(3, (short)300);
137 |
138 | long r = (Long)tuple.get(1) + (Integer)tuple.get(2) + (Short)tuple.get(3);
139 | ds.schema.pool().release(tuple);
140 | return r;
141 | }
142 |
143 | @GenerateMicroBenchmark
144 | public long measureDirectSchemaPreallocIndexedBoxed(DirectSchema ds) throws Exception {
145 | FastTuple tuple = ds.tuple;
146 |
147 | tuple.set(1, 100L);
148 | tuple.set(2, 200);
149 | tuple.set(3, (short)300);
150 |
151 | return (Long)tuple.get(1) + (Integer)tuple.get(2) + (Short)tuple.get(3);
152 | }
153 |
154 | @GenerateMicroBenchmark
155 | public long measureDirectSchemaPoolIndexed(DirectSchema ds) throws Exception {
156 | FastTuple tuple = ds.schema.pool().checkout();
157 |
158 | tuple.setLong(1, 100L);
159 | tuple.setInt(2, 200);
160 | tuple.setShort(3, (short)300);
161 |
162 | long r = tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
163 | ds.schema.pool().release(tuple);
164 | return r;
165 | }
166 |
167 | @GenerateMicroBenchmark
168 | public long measureDirectSchemaPreallocIndexed(DirectSchema ds) throws Exception {
169 | FastTuple tuple = ds.tuple;
170 |
171 | tuple.setLong(1, 100L);
172 | tuple.setInt(2, 200);
173 | tuple.setShort(3, (short)300);
174 |
175 | return tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
176 | }
177 |
178 | @GenerateMicroBenchmark
179 | public long measureDirectSchemaPoolEval(DirectSchema ds) throws Exception {
180 | FastTuple tuple = ds.schema.pool().checkout();
181 |
182 | ds.eval1.evaluate(tuple);
183 | long r = ds.eval2.evaluate(tuple);
184 | ds.schema.pool().release(tuple);
185 | return r;
186 | }
187 |
188 | @GenerateMicroBenchmark
189 | public long measureDirectSchemaPreallocEval(DirectSchema ds) throws Exception {
190 | FastTuple tuple = ds.tuple;
191 |
192 | ds.eval1.evaluate(tuple);
193 | return ds.eval2.evaluate(tuple);
194 | }
195 |
196 | @GenerateMicroBenchmark
197 | public long measureDirectSchemaPoolIface(DirectSchema ds) throws Exception {
198 | StaticBinding tuple = (StaticBinding)ds.schema.pool().checkout();
199 |
200 | tuple.a(100L);
201 | tuple.b(200);
202 | tuple.c((short)300);
203 |
204 | long r = tuple.a() + tuple.b() + tuple.c();
205 | ds.schema.pool().release((FastTuple)tuple);
206 | return r;
207 | }
208 |
209 | @GenerateMicroBenchmark
210 | public long measureDirectSchemaPreallocIface(DirectSchema ds) throws Exception {
211 | StaticBinding tuple = (StaticBinding)ds.tuple;
212 |
213 | tuple.a(100L);
214 | tuple.b(200);
215 | tuple.c((short)300);
216 |
217 | return tuple.a() + tuple.b() + tuple.c();
218 | }
219 | }
220 |
221 | public static class HeapBenchmarks {
222 | @GenerateMicroBenchmark
223 | public long measureHeapSchemaAllocate(HeapSchema hs) throws Exception {
224 | FastTuple tuple = hs.schema.createTuple();
225 |
226 | return tuple.getLong(1);
227 | }
228 |
229 | @GenerateMicroBenchmark
230 | public long measureHeapSchemaDeque(HeapSchema hs) throws Exception {
231 | FastTuple tuple = hs.deque.pop();
232 |
233 | tuple.setLong(1, 100L);
234 | tuple.setInt(2, 200);
235 | tuple.setShort(3, (short) 300);
236 |
237 | long r = tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
238 | hs.deque.push(tuple);
239 | return r;
240 | }
241 |
242 | @GenerateMicroBenchmark
243 | public long measureHeapSchemaPoolIndexedBoxed(HeapSchema hs) throws Exception {
244 | FastTuple tuple = hs.schema.pool().checkout();
245 |
246 | tuple.set(1, 100L);
247 | tuple.set(2, 200);
248 | tuple.set(3, (short)300);
249 |
250 | long r = (Long)tuple.get(1) + (Integer)tuple.get(2) + (Short)tuple.get(3);
251 | hs.schema.pool().release(tuple);
252 | return r;
253 | }
254 |
255 | @GenerateMicroBenchmark
256 | public long measureHeapSchemaPreallocIndexedBoxed(HeapSchema hs) throws Exception {
257 | FastTuple tuple = hs.tuple;
258 |
259 | tuple.set(1, 100L);
260 | tuple.set(2, 200);
261 | tuple.set(3, (short)300);
262 |
263 | return (Long)tuple.get(1) + (Integer)tuple.get(2) + (Short)tuple.get(3);
264 | }
265 |
266 | @GenerateMicroBenchmark
267 | public long measureHeapSchemaPoolIndexed(HeapSchema hs) throws Exception {
268 | FastTuple tuple = hs.schema.pool().checkout();
269 |
270 | tuple.setLong(1, 100L);
271 | tuple.setInt(2, 200);
272 | tuple.setShort(3, (short)300);
273 |
274 | long r = tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
275 | hs.schema.pool().release(tuple);
276 | return r;
277 | }
278 |
279 | @GenerateMicroBenchmark
280 | public long measureHeapSchemaPreallocIndexed(HeapSchema hs) throws Exception {
281 | FastTuple tuple = hs.tuple;
282 |
283 | tuple.setLong(1, 100L);
284 | tuple.setInt(2, 200);
285 | tuple.setShort(3, (short)300);
286 |
287 | return tuple.getLong(1) + tuple.getInt(2) + tuple.getShort(3);
288 | }
289 |
290 | @GenerateMicroBenchmark
291 | public long measureHeapSchemaPoolEval(HeapSchema hs) throws Exception {
292 | FastTuple tuple = hs.schema.pool().checkout();
293 |
294 | hs.eval1.evaluate(tuple);
295 | long r = hs.eval2.evaluate(tuple);
296 | hs.schema.pool().release(tuple);
297 | return r;
298 | }
299 |
300 | @GenerateMicroBenchmark
301 | public long measureHeapSchemaPreallocEval(HeapSchema hs) throws Exception {
302 | FastTuple tuple = hs.tuple;
303 |
304 | hs.eval1.evaluate(tuple);
305 | return hs.eval2.evaluate(tuple);
306 | }
307 |
308 | @GenerateMicroBenchmark
309 | public long measureHeapSchemaPreallocEvalField(HeapSchema hs) throws Exception {
310 | FastTuple tuple = hs.tuple;
311 |
312 | hs.eval3.evaluate(tuple);
313 | return hs.eval4.evaluate(tuple);
314 | }
315 |
316 | @GenerateMicroBenchmark
317 | public long measureHeapSchemaPoolIface(HeapSchema hs) throws Exception {
318 | StaticBinding tuple = (StaticBinding)hs.schema.pool().checkout();
319 |
320 | tuple.a(100L);
321 | tuple.b(200);
322 | tuple.c((short)300);
323 |
324 | long r = tuple.a() + tuple.b() + tuple.c();
325 | hs.schema.pool().release((FastTuple)tuple);
326 | return r;
327 | }
328 |
329 | @GenerateMicroBenchmark
330 | public long measureHeapSchemaPreallocIface(HeapSchema hs) throws Exception {
331 | StaticBinding tuple = (StaticBinding)hs.tuple;
332 |
333 | tuple.a(100L);
334 | tuple.b(200);
335 | tuple.c((short)300);
336 |
337 | return tuple.a() + tuple.b() + tuple.c();
338 | }
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/fasttuple-bench/src/main/java/other/FastObjectPool.java:
--------------------------------------------------------------------------------
1 | package other;
2 |
3 | import sun.misc.Unsafe;
4 |
5 | import java.lang.reflect.Field;
6 | import java.security.AccessController;
7 | import java.security.PrivilegedExceptionAction;
8 | import java.util.concurrent.atomic.AtomicInteger;
9 | import java.util.concurrent.locks.ReentrantLock;
10 |
11 | /**
12 | * Taken from: http://www.javacodegeeks.com/2013/07/lock-less-java-object-pool.html
13 | *
14 | */
15 | public class FastObjectPool {
16 |
17 | private Holder[] objects;
18 |
19 | private volatile int takePointer;
20 | private int releasePointer;
21 |
22 | private final int mask;
23 | private final long BASE;
24 | private final long INDEXSCALE;
25 | private final long ASHIFT;
26 |
27 | public ReentrantLock lock = new ReentrantLock();
28 | private ThreadLocal> localValue = new ThreadLocal>();
29 |
30 | @SuppressWarnings("unchecked")
31 | public FastObjectPool(PoolFactory factory , int size)
32 | {
33 |
34 | int newSize=1;
35 | while(newSize(factory.create());
44 | }
45 | mask = size-1;
46 | releasePointer = size;
47 | BASE = THE_UNSAFE.arrayBaseOffset(Holder[].class);
48 | INDEXSCALE = THE_UNSAFE.arrayIndexScale(Holder[].class);
49 | ASHIFT = 31 - Integer.numberOfLeadingZeros((int) INDEXSCALE);
50 | }
51 |
52 | public Holder take()
53 | {
54 | int localTakePointer;
55 |
56 | Holder localObject = localValue.get();
57 | if(localObject!=null)
58 | {
59 | if(localObject.state.compareAndSet(Holder.FREE, Holder.USED))
60 | {
61 | return localObject;
62 | }
63 | }
64 |
65 | while(releasePointer != (localTakePointer=takePointer) )
66 | {
67 | int index = localTakePointer & mask;
68 | Holder holder = objects[index];
69 | //if(holder!=null && THE_UNSAFE.compareAndSwapObject(objects, (index*INDEXSCALE)+BASE, holder, null))
70 | if(holder!=null && THE_UNSAFE.compareAndSwapObject(objects, (index< object) throws InterruptedException
84 | {
85 | lock.lockInterruptibly();
86 | try{
87 | int localValue=releasePointer;
88 | //long index = ((localValue & mask) * INDEXSCALE ) + BASE;
89 | long index = ((localValue & mask)<
107 | {
108 | private T value;
109 | public static final int FREE=0;
110 | public static final int USED=1;
111 |
112 | private AtomicInteger state = new AtomicInteger(FREE);
113 | public Holder(T value)
114 | {
115 | this.value = value;
116 | }
117 |
118 | public T getValue() {
119 | return value;
120 | }
121 | }
122 |
123 | public static interface PoolFactory
124 | {
125 | public T create();
126 | }
127 |
128 | public static final Unsafe THE_UNSAFE;
129 | static
130 | {
131 | try
132 | {
133 | final PrivilegedExceptionAction action = new PrivilegedExceptionAction()
134 | {
135 | public Unsafe run() throws Exception
136 | {
137 | Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
138 | theUnsafe.setAccessible(true);
139 | return (Unsafe) theUnsafe.get(null);
140 | }
141 | };
142 |
143 | THE_UNSAFE = AccessController.doPrivileged(action);
144 | }
145 | catch (Exception e)
146 | {
147 | throw new RuntimeException("Unable to load unsafe", e);
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/fasttuple-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.boundary
8 | fasttuple-core
9 | 0.1-SNAPSHOT
10 |
11 |
12 |
13 | org.codehaus.janino
14 | janino
15 | 2.7.4
16 |
17 |
18 |
19 | org.codehaus.janino
20 | commons-compiler
21 | 2.7.4
22 |
23 |
24 |
25 | com.google.guava
26 | guava
27 | 17.0
28 |
29 |
30 |
31 | junit
32 | junit
33 | 4.11
34 | test
35 |
36 |
37 |
38 |
39 | UTF-8
40 |
41 |
42 |
43 |
44 |
45 | org.apache.maven.plugins
46 | maven-compiler-plugin
47 | 3.0
48 |
49 | 1.7
50 | 1.7
51 | 1.7
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/Destroyer.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | /**
4 | * Created by cliff on 5/15/14.
5 | */
6 | public interface Destroyer {
7 | public void destroyArray(T[] ary);
8 | }
9 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/DirectTupleSchema.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.boundary.tuple.codegen.DirectTupleCodeGenerator;
4 | import com.boundary.tuple.codegen.TupleAllocatorGenerator;
5 | import com.boundary.tuple.unsafe.Coterie;
6 | import sun.misc.Unsafe;
7 |
8 | import java.util.Arrays;
9 | import java.util.Comparator;
10 |
11 | import static com.boundary.tuple.SizeOf.sizeOf;
12 |
13 | /**
14 | * Created by cliff on 5/9/14.
15 | */
16 | public class DirectTupleSchema extends TupleSchema {
17 | // layout is the mapping from the given logical index to an offset in the tuple
18 | protected final int[] layout;
19 | protected final int[] widths;
20 | protected int byteSize;
21 | protected long addressOffset;
22 | protected final int wordSize;
23 | protected TupleAllocatorGenerator.TupleAllocator allocator;
24 |
25 | private static final Unsafe unsafe = Coterie.unsafe();
26 |
27 | public static class Builder extends TupleSchema.Builder {
28 | protected int wordSize = 8;
29 |
30 | public Builder(TupleSchema.Builder builder) {
31 | super(builder);
32 | }
33 |
34 | /**
35 | * Pads out the size of each individual record such that it fits within a multiple of the wordSize.
36 | * This is useful for eliminating false sharing when adjacent records are being utilized by different
37 | * threads.
38 | *
39 | * @param wordSize
40 | * @return
41 | */
42 | public Builder padToWordSize(int wordSize) {
43 | this.wordSize = wordSize;
44 | return this;
45 | }
46 |
47 | public DirectTupleSchema build() throws Exception {
48 | return new DirectTupleSchema(this);
49 | }
50 | }
51 |
52 | public DirectTupleSchema(Builder builder) throws Exception {
53 | super(builder);
54 | int size = fieldNames.length;
55 | this.layout = new int[size];
56 | this.widths = new int[size];
57 | this.wordSize = builder.wordSize;
58 | generateLayout();
59 | generateClass();
60 | }
61 |
62 | public long getLong(long address, int index) {
63 | return unsafe.getLong(address + layout[index]);
64 | }
65 |
66 | public int getInt(long address, int index) {
67 | return unsafe.getInt(address + layout[index]);
68 | }
69 |
70 | public short getShort(long address, int index) {
71 | return unsafe.getShort(address + layout[index]);
72 | }
73 |
74 | public char getChar(long address, int index) {
75 | return unsafe.getChar(address + layout[index]);
76 | }
77 |
78 | public byte getByte(long address, int index) {
79 | return unsafe.getByte(address + layout[index]);
80 | }
81 |
82 | public double getDouble(long address, int index) {
83 | return unsafe.getDouble(address + layout[index]);
84 | }
85 |
86 | public float getFloat(long address, int index) {
87 | return unsafe.getFloat(address + layout[index]);
88 | }
89 |
90 | public void setLong(long address, int index, long value) {
91 | unsafe.putLong(address + layout[index], value);
92 | }
93 |
94 | public void setInt(long address, int index, int value) {
95 | unsafe.putInt(address + layout[index], value);
96 | }
97 |
98 | public void setShort(long address, int index, short value) {
99 | unsafe.putShort(address + layout[index], value);
100 | }
101 |
102 | public void setChar(long address, int index, char value) {
103 | unsafe.putChar(address + layout[index], value);
104 | }
105 |
106 | public void setByte(long address, int index, byte value) {
107 | unsafe.putByte(address + layout[index], value);
108 | }
109 |
110 | public void setFloat(long address, int index, float value) {
111 | unsafe.putFloat(address + layout[index], value);
112 | }
113 |
114 | public void setDouble(long address, int index, double value) {
115 | unsafe.putDouble(address + layout[index], value);
116 | }
117 |
118 | public int[] getLayout() {
119 | return layout.clone();
120 | }
121 |
122 | public int getByteSize() {
123 | return byteSize;
124 | }
125 |
126 | public FastTuple createTuple(long address) throws Exception {
127 | FastTuple tuple = allocator.allocate();
128 | unsafe.putLong(tuple, addressOffset, address);
129 | return tuple;
130 | }
131 |
132 | public long createRecord() {
133 | return unsafe.allocateMemory(byteSize);
134 | }
135 |
136 | public long createRecordArray(long size) {
137 | return unsafe.allocateMemory(size * byteSize);
138 | }
139 |
140 | public FastTuple createTuple() throws Exception {
141 | long address = createRecord();
142 | return createTuple(address);
143 | }
144 |
145 | @Override
146 | public FastTuple[] createTupleArray(int size) throws Exception {
147 | long address = createRecordArray(size);
148 | FastTuple[] tuples = new FastTuple[size];
149 | for (int i=0; i() {
193 | @Override
194 | public int compare(Member o1, Member o2) {
195 | return o2.size - o1.size;
196 | }
197 | });
198 | int offset = 0;
199 | for (Member m : members) {
200 | layout[m.index] = offset;
201 | widths[m.index] = m.size;
202 | offset += m.size;
203 | }
204 | int padding = wordSize - (offset % wordSize);
205 | byteSize = offset + padding;
206 | }
207 |
208 | private static class Member {
209 | public final int index;
210 | public final int size;
211 |
212 | public Member(int index, int size) {
213 | this.index = index;
214 | this.size = size;
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/FastTuple.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | /**
4 | * Created by cliff on 5/2/14.
5 | */
6 | public abstract class FastTuple {
7 |
8 | /**
9 | * Does a boxed get on a tuple field.
10 | *
11 | * @param i Index of the field to get. Counting starts at 1.
12 | * @return The value.
13 | */
14 | public abstract Object get(int i);
15 |
16 | /**
17 | * Does an unboxed get on a tuple field.
18 | *
19 | * @param i Index of the field to get. Counting starts at 1.
20 | * @return The value.
21 | * @throws java.lang.IllegalArgumentException if the specified field is not a long.
22 | */
23 | public abstract long getLong(int i);
24 |
25 | /**
26 | * Does an unboxed get on a tuple field.
27 | *
28 | * @param i Index of the field to get. Counting starts at 1.
29 | * @return The value.
30 | * @throws java.lang.IllegalArgumentException if the specified field is not an int.
31 | */
32 | public abstract int getInt(int i);
33 |
34 | /**
35 | * Does an unboxed get on a tuple field.
36 | *
37 | * @param i Index of the field to get. Counting starts at 1.
38 | * @return The value.
39 | * @throws java.lang.IllegalArgumentException if the specified field is not a short.
40 | */
41 | public abstract short getShort(int i);
42 |
43 | /**
44 | * Does an unboxed get on a tuple field.
45 | *
46 | * @param i Index of the field to get. Counting starts at 1.
47 | * @return The value.
48 | * @throws java.lang.IllegalArgumentException if the specified field is not a char.
49 | */
50 | public abstract char getChar(int i);
51 |
52 | /**
53 | * Does an unboxed get on a tuple field.
54 | *
55 | * @param i Index of the field to get. Counting starts at 1.
56 | * @return The value.
57 | * @throws java.lang.IllegalArgumentException if the specified field is not a byte.
58 | */
59 | public abstract byte getByte(int i);
60 |
61 | /**
62 | * Does an unboxed get on a tuple field.
63 | *
64 | * @param i Index of the field to get. Counting starts at 1.
65 | * @return The value.
66 | * @throws java.lang.IllegalArgumentException if the specified field is not a float.
67 | */
68 | public abstract float getFloat(int i);
69 |
70 | /**
71 | * Does an unboxed get on a tuple field.
72 | *
73 | * @param i Index of the field to get. Counting starts at 1.
74 | * @return The value.
75 | * @throws java.lang.IllegalArgumentException if the specified field is not a double.
76 | */
77 | public abstract double getDouble(int i);
78 |
79 | /**
80 | * Does an unboxed set on a tuple field.
81 | *
82 | * @param i Index of the field to set. Counting starts at 1.
83 | * @param value The value to set.
84 | * @throws java.lang.IllegalArgumentException if the specified field is not a long.
85 | */
86 | public abstract void setLong(int i, long value);
87 |
88 | /**
89 | * Does an unboxed set on a tuple field.
90 | *
91 | * @param i Index of the field to set. Counting starts at 1.
92 | * @param value The value to set.
93 | * @throws java.lang.IllegalArgumentException if the specified field is not an int.
94 | */
95 | public abstract void setInt(int i, int value);
96 |
97 | /**
98 | * Does an unboxed set on a tuple field.
99 | *
100 | * @param i Index of the field to set. Counting starts at 1.
101 | * @param value The value to set.
102 | * @throws java.lang.IllegalArgumentException if the specified field is not a short.
103 | */
104 | public abstract void setShort(int i, short value);
105 |
106 | /**
107 | * Does an unboxed set on a tuple field.
108 | *
109 | * @param i Index of the field to set. Counting starts at 1.
110 | * @param value The value to set.
111 | * @throws java.lang.IllegalArgumentException if the specified field is not a char.
112 | */
113 | public abstract void setChar(int i, char value);
114 |
115 | /**
116 | * Does an unboxed set on a tuple field.
117 | *
118 | * @param i Index of the field to set. Counting starts at 1.
119 | * @param value The value to set.
120 | * @throws java.lang.IllegalArgumentException if the specified field is not a byte.
121 | */
122 | public abstract void setByte(int i, byte value);
123 |
124 | /**
125 | * Does an unboxed set on a tuple field.
126 | *
127 | * @param i Index of the field to set. Counting starts at 1.
128 | * @param value The value to set.
129 | * @throws java.lang.IllegalArgumentException if the specified field is not a float.
130 | */
131 | public abstract void setFloat(int i, float value);
132 |
133 | /**
134 | * Does an unboxed set on a tuple field.
135 | *
136 | * @param i Index of the field to set. Counting starts at 1.
137 | * @param value The value to set.
138 | * @throws java.lang.IllegalArgumentException if the specified field is not a double.
139 | */
140 | public abstract void setDouble(int i, double value);
141 |
142 | /**
143 | * Does a boxed set on a tuple field.
144 | *
145 | * @param i Index of the field to set. Counting starts at 1.
146 | * @param value The value to set.
147 | * @throws java.lang.IllegalArgumentException if the
148 | */
149 | public abstract void set(int i, Object value);
150 | }
151 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/HeapTupleSchema.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.boundary.tuple.codegen.HeapTupleCodeGenerator;
4 | import com.boundary.tuple.codegen.TupleAllocatorGenerator;
5 | import com.boundary.tuple.unsafe.Coterie;
6 | import sun.misc.Unsafe;
7 |
8 | /**
9 | * Created by cliff on 5/9/14.
10 | */
11 | public class HeapTupleSchema extends TupleSchema {
12 | private TupleAllocatorGenerator.TupleAllocator allocator;
13 |
14 | public static class Builder extends TupleSchema.Builder {
15 |
16 | public Builder(TupleSchema.Builder builder) {
17 | super(builder);
18 | }
19 |
20 | public HeapTupleSchema build() throws Exception {
21 | return new HeapTupleSchema(this);
22 | }
23 | }
24 |
25 | public HeapTupleSchema(Builder builder) throws Exception {
26 | super(builder);
27 | generateClass();
28 | }
29 |
30 | @Override
31 | protected void generateClass() throws Exception {
32 | this.clazz = new HeapTupleCodeGenerator(iface, fieldNames, fieldTypes).cookToClass();
33 | this.allocator = new TupleAllocatorGenerator(clazz).createAllocator();
34 | }
35 |
36 | @Override
37 | public FastTuple createTuple() throws Exception {
38 | return allocator.allocate();
39 | }
40 |
41 | @Override
42 | public FastTuple[] createTupleArray(int size) throws Exception {
43 | FastTuple[] tuples = new FastTuple[size];
44 | for (int i=0; i {
7 | public T[] createArray(int size) throws Exception;
8 | }
9 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/SizeOf.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | /**
4 | * Created by cliff on 5/3/14.
5 | */
6 | public final class SizeOf {
7 |
8 | private SizeOf() {}
9 |
10 | public static int sizeOf(Class c) {
11 | if (c.equals(Byte.TYPE)) {
12 | return 1;
13 | } else if (c.equals(Short.TYPE) || c.equals(Character.TYPE)) {
14 | return 2;
15 | } else if (c.equals(Integer.TYPE) || c.equals(Float.TYPE)) {
16 | return 4;
17 | } else {
18 | return 8;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/TuplePool.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.google.common.base.Function;
4 | import com.google.common.base.Optional;
5 |
6 | import java.util.ArrayDeque;
7 | import java.util.concurrent.CopyOnWriteArrayList;
8 |
9 | /**
10 | * Created by cliff on 5/4/14.
11 | */
12 | public class TuplePool {
13 | private final ThreadLocal> pool;
14 | private final Optional> initializer;
15 | private final Loader loader;
16 | private final Destroyer destroyer;
17 | private final CopyOnWriteArrayList references;
18 | private int size;
19 | private final int reloadSize;
20 | private final boolean createWhenExhausted;
21 | private volatile boolean closed = false;
22 |
23 | public TuplePool(int size,
24 | boolean createWhenExhausted,
25 | Loader loader,
26 | Destroyer destroyer) {
27 | this(size, createWhenExhausted, loader, destroyer, null);
28 | }
29 |
30 | public TuplePool(final int size,
31 | boolean createWhenExhausted,
32 | Loader loader,
33 | Destroyer destroyer,
34 | Function initializer) {
35 | this.size = 0;
36 | this.reloadSize = size;
37 | this.createWhenExhausted = createWhenExhausted;
38 | this.references = new CopyOnWriteArrayList<>();
39 | this.loader = loader;
40 | this.destroyer = destroyer;
41 | this.initializer = Optional.fromNullable(initializer);
42 | this.pool = new ThreadLocal>() {
43 | @Override
44 | protected ArrayDeque initialValue() {
45 | ArrayDeque deque = new ArrayDeque<>(size);
46 | reload(deque);
47 | return deque;
48 | }
49 | };
50 | }
51 |
52 | public T checkout() {
53 | ArrayDeque deque = pool.get();
54 | possiblyReload(deque);
55 | T obj = deque.pop();
56 | initialize(obj);
57 | return obj;
58 | }
59 |
60 | public void release(T obj) {
61 | pool.get().push(obj);
62 | }
63 |
64 | public int getSize() {
65 | return size;
66 | }
67 |
68 | private void initialize(T obj) {
69 | if (initializer.isPresent()) {
70 | initializer.get().apply(obj);
71 | }
72 | }
73 |
74 | private void possiblyReload(ArrayDeque deque) {
75 | if (deque.isEmpty()) {
76 | if (createWhenExhausted) {
77 | reload(deque);
78 | } else {
79 | throw new IllegalStateException("Tuple pool is exhausted.");
80 | }
81 | }
82 | }
83 |
84 | private void reload(ArrayDeque deque) {
85 | if (closed) {
86 | throw new IllegalStateException("Pool's closed everyone out!");
87 | }
88 | try {
89 | final T[] tuples = loader.createArray(reloadSize);
90 | size += reloadSize;
91 | for (T tuple : tuples) {
92 | deque.push(tuple);
93 | }
94 | } catch (Exception ex) {
95 | throw new IllegalStateException("Unable to reload Tuple pool");
96 | }
97 | }
98 |
99 | public void close() {
100 | closed = true;
101 | for (T[] ary : references) {
102 | destroyer.destroyArray(ary);
103 | }
104 | references.clear();
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/TupleSchema.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple;
2 |
3 | import com.google.common.base.Preconditions;
4 | import com.google.common.collect.Lists;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by cliff on 5/2/14.
13 | */
14 | public abstract class TupleSchema implements Loader, Destroyer {
15 | protected final String[] fieldNames;
16 | protected final Class[] fieldTypes;
17 | protected final Class iface;
18 | protected Class clazz;
19 | protected final TuplePool pool;
20 |
21 | public static Builder builder() {
22 | return new Builder();
23 | }
24 |
25 | protected TupleSchema(Builder builder) {
26 | this.fieldNames = builder.fn.toArray(new String[builder.fn.size()]);
27 | this.fieldTypes = builder.ft.toArray(new Class[builder.ft.size()]);
28 | Preconditions.checkArgument(fieldNames.length == fieldTypes.length,
29 | "fieldNames and fieldTypes must have equal length");
30 | for (int i = 0; i < fieldNames.length; i++) {
31 | Preconditions.checkArgument(fieldTypes[i].isPrimitive() && !fieldTypes[i].equals(Boolean.TYPE));
32 | }
33 | this.iface = builder.iface;
34 | if (iface != null) {
35 | Preconditions.checkArgument(iface.isInterface(),
36 | iface.getName() + " is not an interface");
37 | }
38 | this.pool = new TuplePool<>(builder.poolSize, builder.createWhenExhausted, this, this);
39 |
40 | }
41 |
42 | public static class Builder {
43 | private List fn;
44 | private List ft;
45 | private Class iface;
46 | private int poolSize;
47 | private int threads;
48 | private boolean createWhenExhausted = false;
49 |
50 | public Builder(Builder builder) {
51 | fn = new ArrayList<>(builder.fn);
52 | ft = new ArrayList<>(builder.ft);
53 | iface = builder.iface;
54 | poolSize = builder.poolSize;
55 | threads = builder.threads;
56 | createWhenExhausted = builder.createWhenExhausted;
57 | }
58 |
59 | public Builder() {
60 | fn = Lists.newArrayList();
61 | ft = Lists.newArrayList();
62 | iface = null;
63 | poolSize = 0;
64 | threads = 0;
65 | }
66 |
67 | /**
68 | * Adds a field name and type to the schema. Field names end up as both method names and field names
69 | * in the generated class, therefore they have the same restrictions on allowable characters. Passing
70 | * in an illegal name will cause a CompileException during the call to build.
71 | *
72 | * @param fieldName
73 | * @param fieldType
74 | * @return
75 | */
76 | public Builder addField(String fieldName, Class fieldType) {
77 | fn.add(fieldName);
78 | ft.add(fieldType);
79 | return this;
80 | }
81 |
82 | /**
83 | * The generated FastTuple subclass will implement the passed in interface. FastTuple's produced
84 | * from this schema can then be cast to the interface type, for type safe invocation of the desired methods.
85 | *
86 | * @param iface
87 | * @return
88 | */
89 | public Builder implementInterface(Class iface) {
90 | this.iface = iface;
91 | return this;
92 | }
93 |
94 | public Builder addFieldNames(String... fieldNames) {
95 | Collections.addAll(fn, fieldNames);
96 | return this;
97 | }
98 |
99 | public Builder addFieldNames(Iterable fieldNames) {
100 | for (String st : fieldNames) {
101 | fn.add(st);
102 | }
103 | return this;
104 | }
105 |
106 | public Builder addFieldTypes(Class... fieldTypes) {
107 | Collections.addAll(ft, fieldTypes);
108 | return this;
109 | }
110 |
111 | public Builder addFieldTypes(Iterable fieldTypes) {
112 | for (Class c : fieldTypes) {
113 | ft.add(c);
114 | }
115 | return this;
116 | }
117 |
118 | /**
119 | * Sets the initial size for each thread local tuple pool. The total number
120 | * of tuples that will be allocated can be found by multiplying this number
121 | * by the number of threads that will be checking tuples out of the pool.
122 | *
123 | * @param poolSize The size to generate specified in number of tuples.
124 | * @return
125 | */
126 | public Builder poolOfSize(int poolSize) {
127 | this.poolSize = poolSize;
128 | return this;
129 | }
130 |
131 | /**
132 | * Specifies that the tuple pool should allocate more tuples when it becomes
133 | * exhausted. Otherwise, an exhausted pool will throw an IllegalStateException.
134 | *
135 | * @return
136 | */
137 | public Builder expandingPool() {
138 | this.createWhenExhausted = true;
139 | return this;
140 | }
141 |
142 | /**
143 | * Causes this schema to allocate its memory off of the main java heap.
144 | *
145 | * @return
146 | */
147 | public DirectTupleSchema.Builder directMemory() {
148 | return new DirectTupleSchema.Builder(this);
149 | }
150 |
151 | /**
152 | * Causes this schema to allocate its memory on heap, and fully reachable by GC.
153 | *
154 | * @return
155 | */
156 | public HeapTupleSchema.Builder heapMemory() {
157 | return new HeapTupleSchema.Builder(this);
158 | }
159 |
160 | }
161 |
162 | @Override
163 | public String toString() {
164 | StringBuilder str = new StringBuilder("(");
165 | for (int i=0; i pool() {
256 | return pool;
257 | }
258 |
259 | public ClassLoader getClassLoader() {
260 | return clazz.getClassLoader();
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/CodegenUtil.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.codehaus.commons.compiler.Location;
5 | import org.codehaus.janino.Java;
6 | import org.codehaus.janino.Mod;
7 |
8 | /**
9 | * Created by cliff on 5/14/14.
10 | */
11 | public final class CodegenUtil {
12 |
13 | public static Java.ConstructorDeclarator nullConstructor(Location loc) {
14 | return new Java.ConstructorDeclarator(
15 | loc,
16 | null,
17 | new Java.Modifiers(Mod.PUBLIC),
18 | new Java.FunctionDeclarator.FormalParameters(
19 | loc,
20 | new Java.FunctionDeclarator.FormalParameter[0],
21 | false
22 | ),
23 | new Java.Type[0],
24 | null,
25 | Lists.newArrayList()
26 | );
27 | }
28 |
29 | public static Java.FunctionDeclarator.FormalParameters emptyParams(Location loc) {
30 | return new Java.FunctionDeclarator.FormalParameters(loc, new Java.FunctionDeclarator.FormalParameter[0], false);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/DirectTupleCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.codehaus.commons.compiler.CompileException;
5 | import org.codehaus.janino.Java;
6 | import org.codehaus.janino.Mod;
7 | import sun.misc.Unsafe;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Created by cliff on 5/5/14.
13 | */
14 | public class DirectTupleCodeGenerator extends TupleCodeGenerator {
15 | protected int[] layout;
16 |
17 | public DirectTupleCodeGenerator(Class iface, String[] fieldNames, Class[] fieldTypes, int[] layout) {
18 | super(iface, fieldNames, fieldTypes);
19 | this.layout = layout.clone();
20 | }
21 |
22 | protected Java.FieldDeclaration[] generateFields() {
23 | return new Java.FieldDeclaration[] {
24 | new Java.FieldDeclaration(
25 | loc,
26 | null,
27 | new Java.Modifiers(Mod.PUBLIC),
28 | new Java.BasicType(loc, Java.BasicType.LONG),
29 | new Java.VariableDeclarator[] {new Java.VariableDeclarator(loc, "address", 0, null)}),
30 | new Java.FieldDeclaration(loc,
31 | null,
32 | new Java.Modifiers((short)(Mod.STATIC + Mod.PRIVATE)),
33 | classToType(loc, Unsafe.class),
34 | new Java.VariableDeclarator[] {
35 | new Java.VariableDeclarator(loc,
36 | "unsafe",
37 | 0,
38 | new Java.MethodInvocation(loc,
39 | new Java.AmbiguousName(loc, new String[] {"Coterie"}),
40 | "unsafe",
41 | new Java.Rvalue[0]))
42 | })
43 | };
44 | }
45 |
46 | @Override
47 | protected List generateIndexedGetterImpl() throws CompileException {
48 | List list = Lists.newArrayList();
49 | for (int i=0; i < fieldNames.length; i++) {
50 | list.add(
51 | new Java.SwitchStatement.SwitchBlockStatementGroup(loc,
52 | Lists.newArrayList(new Java.IntegerLiteral(loc, Integer.toString(i+1))),
53 | false,
54 | Lists.newArrayList(new Java.ReturnStatement(loc,
55 | generateGetInvocation(fieldTypes[i], i)))
56 | )
57 | );
58 | }
59 | list.add(generateDefaultCase());
60 | return list;
61 | }
62 |
63 | @Override
64 | protected List generateIndexedGetterImpl(Class type) throws CompileException {
65 | List list = Lists.newArrayList();
66 | for (int n=0; n < fieldNames.length; n++) {
67 | if (!type.equals(fieldTypes[n])) {
68 | continue;
69 | }
70 | list.add(new Java.SwitchStatement.SwitchBlockStatementGroup(loc,
71 | Lists.newArrayList(new Java.IntegerLiteral(loc, String.valueOf(n+1))),
72 | false,
73 | Lists.newArrayList(
74 | new Java.ReturnStatement(loc, generateGetInvocation(type, n))
75 | )
76 | ));
77 | }
78 | list.add(generateDefaultCase());
79 | return list;
80 | }
81 |
82 | @Override
83 | protected List generateIndexedSetterImpl(String value) throws CompileException {
84 | List list = Lists.newArrayList();
85 | for (int i=0; i < fieldNames.length; i++) {
86 | list.add(
87 | new Java.SwitchStatement.SwitchBlockStatementGroup(loc,
88 | Lists.newArrayList(new Java.IntegerLiteral(loc, Integer.toString(i+1))),
89 | false,
90 | Lists.newArrayList(
91 | new Java.ExpressionStatement(generateSetInvocation(fieldTypes[i], i, value)),
92 | new Java.BreakStatement(loc, null)
93 | )
94 | )
95 | );
96 | }
97 | list.add(generateDefaultCase());
98 | return list;
99 | }
100 |
101 | @Override
102 | protected List generateIndexedSetterImpl(String value, Class type) throws CompileException {
103 | List list = Lists.newArrayList();
104 | for (int n=0; n < fieldNames.length; n++) {
105 | if (!type.equals(fieldTypes[n])) {
106 | continue;
107 | }
108 | list.add(new Java.SwitchStatement.SwitchBlockStatementGroup(loc,
109 | Lists.newArrayList(new Java.IntegerLiteral(loc, String.valueOf(n+1))),
110 | false,
111 | Lists.newArrayList(
112 | new Java.ExpressionStatement(generateSetInvocation(type, n, value)),
113 | new Java.BreakStatement(loc, null)
114 | )
115 | ));
116 | }
117 | list.add(generateDefaultCase());
118 | return list;
119 | }
120 |
121 | protected Java.Rvalue generateGetInvocation(Class type, int index) throws CompileException {
122 | return new Java.MethodInvocation(loc,
123 | new Java.AmbiguousName(loc, new String[] {"unsafe"}),
124 | "get" + accessorForType(type),
125 | new Java.Rvalue[] {
126 | new Java.BinaryOperation(loc,
127 | new Java.AmbiguousName(loc, new String[] {"address"}),
128 | "+",
129 | new Java.IntegerLiteral(loc, Integer.toString(layout[index])))
130 | }
131 | );
132 | }
133 |
134 | protected Java.Rvalue generateSetInvocation(Class type, int index, String value) throws CompileException {
135 | return new Java.MethodInvocation(loc,
136 | new Java.AmbiguousName(loc, new String[] {"unsafe"}),
137 | "put" + accessorForType(type),
138 | new Java.Rvalue[]{
139 | new Java.BinaryOperation(loc,
140 | new Java.AmbiguousName(loc, new String[]{"address"}),
141 | "+",
142 | new Java.IntegerLiteral(loc, Integer.toString(layout[index]))),
143 | new Java.Cast(loc, classToRefType(type), new Java.AmbiguousName(loc, new String[] {value}))
144 | }
145 | );
146 | }
147 |
148 | protected String accessorForType(Class type) {
149 | if (type.equals(Byte.TYPE)) {
150 | return "Byte";
151 | } else if (type.equals(Character.TYPE)) {
152 | return "Char";
153 | } else if (type.equals(Short.TYPE)) {
154 | return "Short";
155 | } else if (type.equals(Integer.TYPE)) {
156 | return "Int";
157 | } else if (type.equals(Float.TYPE)) {
158 | return "Float";
159 | } else if (type.equals(Double.TYPE)) {
160 | return "Double";
161 | } else if (type.equals(Long.TYPE)) {
162 | return "Long";
163 | }
164 | return null;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/HeapTupleCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.google.common.collect.Lists;
4 | import org.codehaus.commons.compiler.CompileException;
5 | import org.codehaus.janino.Java;
6 | import org.codehaus.janino.Mod;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Created by cliff on 5/9/14.
12 | */
13 | public class HeapTupleCodeGenerator extends TupleCodeGenerator {
14 |
15 | public HeapTupleCodeGenerator(Class iface, String[] fieldName, Class[] fieldType) {
16 | super(iface, fieldName, fieldType);
17 | }
18 |
19 | @Override
20 | protected Java.FieldDeclaration[] generateFields() {
21 | Java.FieldDeclaration[] declarations = new Java.FieldDeclaration[fieldTypes.length];
22 | for (int i=0; i generateIndexedGetterImpl() throws CompileException {
39 | List list = Lists.newArrayList();
40 | for (int i=0; inewArrayList(new Java.IntegerLiteral(loc, String.valueOf(i+1))),
45 | false,
46 | Lists.newArrayList(new Java.ReturnStatement(loc, generateGetInvocation(fieldTypes[i], i)))
47 | )
48 | );
49 | }
50 | list.add(generateDefaultCase());
51 | return list;
52 | }
53 |
54 | @Override
55 | protected List generateIndexedGetterImpl(Class type) throws CompileException {
56 | List list = Lists.newArrayList();
57 | for (int i=0; inewArrayList(new Java.IntegerLiteral(loc, String.valueOf(i+1))),
65 | false,
66 | Lists.newArrayList(new Java.ReturnStatement(loc, generateGetInvocation(fieldTypes[i], i)))
67 | )
68 | );
69 | }
70 | list.add(generateDefaultCase());
71 | return list;
72 | }
73 |
74 | @Override
75 | protected List generateIndexedSetterImpl(String value) throws CompileException {
76 | List list = Lists.newArrayList();
77 | for (int i=0; inewArrayList(new Java.IntegerLiteral(loc, String.valueOf(i+1))),
82 | false,
83 | Lists.newArrayList(
84 | new Java.ExpressionStatement(generateSetInvocation(fieldTypes[i], i, value)),
85 | new Java.BreakStatement(loc, null)
86 | )
87 | )
88 | );
89 | }
90 | list.add(generateDefaultCase());
91 | return list;
92 | }
93 |
94 | @Override
95 | protected List generateIndexedSetterImpl(String value, Class type) throws CompileException {
96 | List list = Lists.newArrayList();
97 | for (int i=0; inewArrayList(new Java.IntegerLiteral(loc, String.valueOf(i+1))),
105 | false,
106 | Lists.newArrayList(
107 | new Java.ExpressionStatement(generateSetInvocation(fieldTypes[i], i, value)),
108 | new Java.BreakStatement(loc, null)
109 | )
110 | )
111 | );
112 | }
113 | list.add(generateDefaultCase());
114 | return list;
115 | }
116 |
117 | @Override
118 | protected Java.Rvalue generateGetInvocation(Class type, int index) throws CompileException {
119 | return new Java.FieldAccessExpression(loc, new Java.ThisReference(loc), fieldNames[index]);
120 | }
121 |
122 | @Override
123 | protected Java.Rvalue generateSetInvocation(Class type, int index, String value) throws CompileException {
124 | return new Java.Assignment(loc, new Java.FieldAccessExpression(loc, new Java.ThisReference(loc), fieldNames[index]), "=",
125 | new Java.Cast(loc, classToRefType(fieldTypes[index]), new Java.AmbiguousName(loc, new String[] {value})));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/TupleAllocatorGenerator.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.FastTuple;
4 | import com.google.common.collect.Lists;
5 | import org.codehaus.commons.compiler.Location;
6 | import org.codehaus.janino.ClassBodyEvaluator;
7 | import org.codehaus.janino.Java;
8 | import org.codehaus.janino.Mod;
9 |
10 | import static com.boundary.tuple.codegen.CodegenUtil.*;
11 |
12 | /**
13 | * Created by cliff on 5/14/14.
14 | */
15 | public class TupleAllocatorGenerator extends ClassBodyEvaluator {
16 | private static final String packageName = "com.boundary.tuple";
17 | public static interface TupleAllocator {
18 | public FastTuple allocate();
19 | }
20 |
21 | private final Class allocatorClass;
22 |
23 | public TupleAllocatorGenerator(Class tupleClass) throws Exception {
24 | setParentClassLoader(tupleClass.getClassLoader());
25 | String className = tupleClass.getName() + "Allocator";
26 | setClassName(packageName + "." + className);
27 | Java.CompilationUnit cu = new Java.CompilationUnit(null);
28 | Location loc = new Location(null, (short)0, (short)0);
29 | cu.setPackageDeclaration(new Java.PackageDeclaration(loc, packageName));
30 | cu.addPackageMemberTypeDeclaration(makeClassDefinition(loc, tupleClass, className));
31 | allocatorClass = compileToClass(cu);
32 | }
33 |
34 | public TupleAllocator createAllocator() throws Exception {
35 | return (TupleAllocator)allocatorClass.getConstructor().newInstance();
36 | }
37 |
38 | private Java.PackageMemberClassDeclaration makeClassDefinition(Location loc, Class tupleClass, String className) throws Exception {
39 | Java.PackageMemberClassDeclaration cd = new Java.PackageMemberClassDeclaration(
40 | loc,
41 | null,
42 | new Java.Modifiers(Mod.PUBLIC),
43 | className,
44 | null,
45 | null,
46 | new Java.Type[] {
47 | classToType(loc, TupleAllocator.class)
48 | });
49 |
50 | cd.addConstructor(nullConstructor(loc));
51 | cd.addDeclaredMethod(new Java.MethodDeclarator(
52 | loc,
53 | null,
54 | new Java.Modifiers(Mod.PUBLIC),
55 | classToType(loc, FastTuple.class),
56 | "allocate",
57 | emptyParams(loc),
58 | new Java.Type[0],
59 | Lists.newArrayList(
60 | new Java.ReturnStatement(loc,
61 | new Java.NewClassInstance(
62 | loc,
63 | null,
64 | new Java.ReferenceType(loc, tupleClass.getCanonicalName().split("\\."), new Java.TypeArgument[0]),
65 | new Java.Rvalue[0]))
66 | )
67 | ));
68 |
69 | return cd;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/TupleCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.FastTuple;
4 | import com.google.common.collect.Lists;
5 | import org.codehaus.commons.compiler.CompileException;
6 | import org.codehaus.commons.compiler.Location;
7 | import org.codehaus.janino.ClassBodyEvaluator;
8 | import org.codehaus.janino.Java;
9 | import org.codehaus.janino.Mod;
10 |
11 | import java.util.List;
12 | import java.util.concurrent.atomic.AtomicLong;
13 | import static java.lang.Character.toUpperCase;
14 | import static com.boundary.tuple.codegen.CodegenUtil.*;
15 |
16 | /**
17 | * Created by cliff on 5/3/14.
18 | */
19 | public abstract class TupleCodeGenerator extends ClassBodyEvaluator {
20 | private static AtomicLong counter = new AtomicLong(0l);
21 | protected static Class[] types = new Class[] {
22 | Long.TYPE,
23 | Integer.TYPE,
24 | Short.TYPE,
25 | Character.TYPE,
26 | Byte.TYPE,
27 | Float.TYPE,
28 | Double.TYPE
29 | };
30 | protected Class iface;
31 | protected String[] fieldNames;
32 | protected Class[] fieldTypes;
33 | protected Location loc;
34 | protected String className;
35 |
36 | public TupleCodeGenerator(Class iface, String[] fieldNames, Class[] fieldTypes) {
37 | this.loc = new Location("", (short)0, (short)0);
38 | this.iface = iface;
39 | this.fieldNames = fieldNames.clone();
40 | this.fieldTypes = fieldTypes.clone();
41 | this.className = "FastTuple" + counter.getAndIncrement();
42 | this.setClassName("com.boundary.tuple." + className);
43 | this.setParentClassLoader(this.getClass().getClassLoader());
44 | }
45 |
46 | protected abstract Java.FieldDeclaration[] generateFields();
47 |
48 | public Class cookToClass() throws CompileException {
49 | return compileToClass(makeCompilationUnit());
50 | }
51 |
52 | protected Java.CompilationUnit makeCompilationUnit() throws CompileException {
53 | Java.CompilationUnit cu = new Java.CompilationUnit(null);
54 | Location loc = new Location("", ((short) 0), ((short) 0));
55 | cu.setPackageDeclaration(new Java.PackageDeclaration(loc, "com.boundary.tuple"));
56 | cu.addImportDeclaration(new Java.CompilationUnit.SingleTypeImportDeclaration(loc, "com.boundary.tuple.unsafe.Coterie".split("\\.")));
57 | Class[] ifaces;
58 | if (iface != null) {
59 | ifaces = new Class[] {iface};
60 | } else {
61 | ifaces = new Class[] {};
62 | }
63 | Java.PackageMemberClassDeclaration cd = new Java.PackageMemberClassDeclaration(
64 | loc,
65 | null, //doc
66 | new Java.Modifiers(Mod.PUBLIC),
67 | className,
68 | null, //type parameters
69 | classToType(loc, FastTuple.class), //class to extend
70 | classesToTypes(loc, ifaces)
71 | );
72 | cu.addPackageMemberTypeDeclaration(cd);
73 | for (Java.FieldDeclaration dec : generateFields()) {
74 | cd.addFieldDeclaration(dec);
75 | }
76 |
77 | cd.addConstructor(nullConstructor(loc));
78 |
79 | for (int i = 0; i < fieldNames.length; i++) {
80 | String name = fieldNames[i];
81 | Class type = fieldTypes[i];
82 |
83 |
84 | cd.addDeclaredMethod(generateGetter(name, type, i));
85 | cd.addDeclaredMethod(generateSetter(name, type, i));
86 | }
87 | cd.addDeclaredMethod(generateIndexedGetter());
88 | cd.addDeclaredMethod(generateIndexedSetter());
89 | for (Java.MethodDeclarator method : generateIndexedTypedGetters()) {
90 | cd.addDeclaredMethod(method);
91 | }
92 | for (Java.MethodDeclarator method : generateIndexedTypedSetters()) {
93 | cd.addDeclaredMethod(method);
94 | }
95 | return cu;
96 | }
97 |
98 | protected Java.MethodDeclarator generateIndexedGetter() throws CompileException {
99 | return new Java.MethodDeclarator(
100 | loc,
101 | null,
102 | new Java.Modifiers(Mod.PUBLIC),
103 | classToType(loc, Object.class),
104 | "get",
105 | new Java.FunctionDeclarator.FormalParameters(loc, new Java.FunctionDeclarator.FormalParameter[] {
106 | new Java.FunctionDeclarator.FormalParameter(loc,true, classToType(loc, Integer.TYPE), "index")}, false),
107 | new Java.Type[] {},
108 | Lists.newArrayList(
109 | new Java.SwitchStatement(loc, new Java.AmbiguousName(loc, new String[] {"index"}), generateIndexedGetterImpl())
110 | )
111 | );
112 | }
113 |
114 | protected List generateIndexedTypedGetters() throws CompileException {
115 | List methods = Lists.newArrayList();
116 | for (int i=0; inewArrayList(
127 | new Java.SwitchStatement(loc, new Java.AmbiguousName(loc, new String[] {"index"}), generateIndexedGetterImpl(types[i]))
128 | )
129 | ));
130 | }
131 | return methods;
132 | }
133 |
134 | protected List generateIndexedTypedSetters() throws CompileException {
135 | List methods = Lists.newArrayList();
136 | for (int i=0; inewArrayList(
149 | new Java.SwitchStatement(loc, new Java.AmbiguousName(loc, new String[] {"index"}), generateIndexedSetterImpl("value", types[i]))
150 | )
151 | ));
152 | }
153 | return methods;
154 | }
155 |
156 | protected Java.MethodDeclarator generateIndexedSetter() throws CompileException {
157 | return new Java.MethodDeclarator(
158 | loc,
159 | null,
160 | new Java.Modifiers(Mod.PUBLIC),
161 | classToType(loc, Void.TYPE),
162 | "set",
163 | new Java.FunctionDeclarator.FormalParameters(loc, new Java.FunctionDeclarator.FormalParameter[] {
164 | new Java.FunctionDeclarator.FormalParameter(loc, true, classToType(loc, Integer.TYPE), "index"),
165 | new Java.FunctionDeclarator.FormalParameter(loc, true, classToType(loc, Object.class), "value")
166 | },false),
167 | new Java.Type[] {},
168 | Lists.newArrayList(
169 | new Java.SwitchStatement(loc, new Java.AmbiguousName(loc, new String[] {"index"}), generateIndexedSetterImpl("value"))
170 | )
171 | );
172 | }
173 |
174 | protected abstract List generateIndexedGetterImpl() throws CompileException;
175 | protected abstract List generateIndexedGetterImpl(Class type) throws CompileException;
176 | protected abstract List generateIndexedSetterImpl(String value) throws CompileException;
177 | protected abstract List generateIndexedSetterImpl(String value, Class type) throws CompileException;
178 |
179 | protected Java.MethodDeclarator generateGetter(String name, Class type, int index) throws CompileException {
180 | // unsafe().get* (long)
181 | Java.BlockStatement st = new Java.ReturnStatement(loc, generateGetInvocation(type, index));
182 | return new Java.MethodDeclarator(
183 | loc,
184 | null,
185 | new Java.Modifiers(Mod.PUBLIC),
186 | classToType(loc, type),
187 | name,
188 | new Java.FunctionDeclarator.FormalParameters(loc, new Java.FunctionDeclarator.FormalParameter[] {}, false),
189 | new Java.Type[] {},
190 | Lists.newArrayList(st)
191 | );
192 | }
193 |
194 | protected Java.MethodDeclarator generateSetter(String name, Class type, int index) throws CompileException {
195 | Java.BlockStatement st = new Java.ExpressionStatement(generateSetInvocation(type, index, "value"));
196 | return new Java.MethodDeclarator(
197 | loc,
198 | null,
199 | new Java.Modifiers(Mod.PUBLIC),
200 | classToType(loc, Void.TYPE),
201 | name,
202 | new Java.FunctionDeclarator.FormalParameters(loc, new Java.FunctionDeclarator.FormalParameter[] {
203 | new Java.FunctionDeclarator.FormalParameter(loc, true, classToType(loc, type), "value")
204 | }, false),
205 | new Java.Type[] {},
206 | Lists.newArrayList(st)
207 | );
208 | }
209 |
210 | protected abstract Java.Rvalue generateGetInvocation(Class type, int index) throws CompileException;
211 | protected abstract Java.Rvalue generateSetInvocation(Class type, int index, String value) throws CompileException;
212 |
213 | protected String capitalize(String st) {
214 | return String.valueOf(toUpperCase(st.charAt(0))) + st.substring(1);
215 | }
216 |
217 |
218 | protected int primIndex(Class type) {
219 | if (type.equals(Long.TYPE)) return Java.BasicType.LONG;
220 | if (type.equals(Integer.TYPE)) return Java.BasicType.INT;
221 | if (type.equals(Short.TYPE)) return Java.BasicType.SHORT;
222 | if (type.equals(Character.TYPE)) return Java.BasicType.CHAR;
223 | if (type.equals(Byte.TYPE)) return Java.BasicType.BYTE;
224 | if (type.equals(Float.TYPE)) return Java.BasicType.FLOAT;
225 | if (type.equals(Double.TYPE)) return Java.BasicType.DOUBLE;
226 | return 0;
227 | }
228 |
229 | protected String primToBox(Class type) {
230 | if (type.equals(Long.TYPE)) return "Long";
231 | if (type.equals(Integer.TYPE)) return "Integer";
232 | if (type.equals(Short.TYPE)) return "Short";
233 | if (type.equals(Character.TYPE)) return "Character";
234 | if (type.equals(Byte.TYPE)) return "Byte";
235 | if (type.equals(Float.TYPE)) return "Float";
236 | if (type.equals(Double.TYPE)) return "Double";
237 | return null;
238 | }
239 |
240 | protected Java.SwitchStatement.SwitchBlockStatementGroup generateDefaultCase() {
241 | return new Java.SwitchStatement.SwitchBlockStatementGroup(
242 | loc,
243 | Lists.newArrayList(),
244 | true,
245 | Lists.newArrayList(
246 | new Java.ThrowStatement(
247 | loc,
248 | new Java.NewClassInstance(
249 | loc,
250 | null,
251 | new Java.ReferenceType(loc, new String[] {"IllegalArgumentException"}, null),
252 | new Java.Rvalue[0]))
253 | ));
254 | }
255 |
256 | protected Java.Type classToRefType(Class type) {
257 | if (type.isPrimitive()) {
258 | return new Java.ReferenceType(loc, primToBox(type).split("\\."), null);
259 | } else {
260 | return new Java.ReferenceType(loc, type.getCanonicalName().split("\\."), null);
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/fasttuple-core/src/main/java/com/boundary/tuple/codegen/TupleExpressionGenerator.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.FastTuple;
4 | import com.boundary.tuple.TupleSchema;
5 | import com.google.common.collect.Lists;
6 | import org.codehaus.commons.compiler.Location;
7 | import org.codehaus.janino.*;
8 |
9 | import java.io.StringReader;
10 | import java.util.List;
11 | import java.util.concurrent.atomic.AtomicLong;
12 |
13 | import static com.boundary.tuple.codegen.CodegenUtil.*;
14 |
15 | /**
16 | * Created by cliff on 5/12/14.
17 | */
18 | public class TupleExpressionGenerator extends ClassBodyEvaluator {
19 | public static interface TupleExpression {
20 | public void evaluate(FastTuple tuple);
21 | }
22 |
23 | public static interface ObjectTupleExpression {
24 | public Object evaluate(FastTuple tuple);
25 | }
26 |
27 | public static interface LongTupleExpression {
28 | public long evaluate(FastTuple tuple);
29 | }
30 |
31 | public static interface IntTupleExpression {
32 | public long evaluate(FastTuple tuple);
33 | }
34 |
35 | public static interface ShortTupleExpression {
36 | public short evaluate(FastTuple tuple);
37 | }
38 |
39 | public static interface CharTupleExpression {
40 | public char evaluate(FastTuple tuple);
41 | }
42 |
43 | public static interface ByteTupleExpression {
44 | public byte evaluate(FastTuple tuple);
45 | }
46 |
47 | public static interface FloatTupleExpression {
48 | public byte evaluate(FastTuple tuple);
49 | }
50 |
51 | public static interface DoubleTupleExpression {
52 | public byte evaluate(FastTuple tuple);
53 | }
54 |
55 | private static String packageName = "com.boundary.tuple";
56 | private static AtomicLong counter = new AtomicLong(0);
57 | private String expression;
58 | private TupleSchema schema;
59 | private Class evaluatorClass;
60 | private Object evaluator;
61 | private Class iface;
62 | private Class returnType;
63 |
64 | public static Builder builder() {
65 | return new Builder();
66 | }
67 |
68 | public static class Builder {
69 | private String expression = null;
70 | private TupleSchema schema = null;
71 |
72 | public Builder() {}
73 |
74 | public Builder expression(String expression) {
75 | this.expression = expression;
76 | return this;
77 | }
78 |
79 | public Builder schema(TupleSchema schema) {
80 | this.schema = schema;
81 | return this;
82 | }
83 |
84 | public TupleExpression returnVoid() throws Exception {
85 | return (TupleExpression) new TupleExpressionGenerator(schema, expression, TupleExpression.class, Void.TYPE).evaluator();
86 | }
87 |
88 | public ObjectTupleExpression returnObject() throws Exception {
89 | return (ObjectTupleExpression) new TupleExpressionGenerator(schema, expression, ObjectTupleExpression.class, Object.class).evaluator();
90 | }
91 |
92 | public LongTupleExpression returnLong() throws Exception {
93 | return (LongTupleExpression) new TupleExpressionGenerator(schema, expression, LongTupleExpression.class, Long.TYPE).evaluator();
94 | }
95 |
96 | public IntTupleExpression returnInt() throws Exception {
97 | return (IntTupleExpression) new TupleExpressionGenerator(schema, expression, IntTupleExpression.class, Integer.TYPE).evaluator();
98 | }
99 |
100 | public ShortTupleExpression returnShort() throws Exception {
101 | return (ShortTupleExpression) new TupleExpressionGenerator(schema, expression, ShortTupleExpression.class, Short.TYPE).evaluator();
102 | }
103 |
104 | public CharTupleExpression returnChar() throws Exception {
105 | return (CharTupleExpression) new TupleExpressionGenerator(schema, expression, CharTupleExpression.class, Character.TYPE).evaluator();
106 | }
107 |
108 | public ByteTupleExpression returnByte() throws Exception {
109 | return (ByteTupleExpression) new TupleExpressionGenerator(schema, expression, ByteTupleExpression.class, Byte.TYPE).evaluator();
110 | }
111 |
112 | public FloatTupleExpression returnFloat() throws Exception {
113 | return (FloatTupleExpression) new TupleExpressionGenerator(schema, expression, FloatTupleExpression.class, Float.TYPE).evaluator();
114 | }
115 |
116 | public DoubleTupleExpression returnDouble() throws Exception {
117 | return (DoubleTupleExpression) new TupleExpressionGenerator(schema, expression, DoubleTupleExpression.class, Double.TYPE).evaluator();
118 | }
119 |
120 | }
121 |
122 | private TupleExpressionGenerator(TupleSchema schema, String expression, Class iface, Class returnType) throws Exception {
123 | this.schema = schema;
124 | this.expression = expression;
125 | this.iface = iface;
126 | this.returnType = returnType;
127 | setParentClassLoader(schema.getClassLoader());
128 | generateEvaluatorClass();
129 | }
130 |
131 | private void generateEvaluatorClass() throws Exception {
132 | Scanner scanner = new Scanner(null, new StringReader(expression));
133 | Parser parser = new Parser(scanner);
134 | Location loc = parser.location();
135 | String className = "TupleExpression" + counter.incrementAndGet();
136 | setClassName(packageName + "." + className);
137 | Java.CompilationUnit cu = makeCompilationUnit(parser);
138 | cu.setPackageDeclaration(new Java.PackageDeclaration(loc, packageName));
139 | Java.PackageMemberClassDeclaration cd = new Java.PackageMemberClassDeclaration(loc,
140 | null,
141 | new Java.Modifiers(Mod.PUBLIC),
142 | className,
143 | null,
144 | null,
145 | new Java.Type[] {
146 | classToType(loc, iface)
147 | }
148 | );
149 | cu.addPackageMemberTypeDeclaration(cd);
150 | cd.addConstructor(nullConstructor(loc));
151 | cd.addDeclaredMethod(generateFrontendMethod(loc));
152 | cd.addDeclaredMethod(generateBackendMethod(parser));
153 | this.evaluatorClass = compileToClass(cu);
154 | this.evaluator = evaluatorClass.getConstructor().newInstance();
155 | }
156 |
157 | private Java.MethodDeclarator generateFrontendMethod(Location loc) throws Exception {
158 | return new Java.MethodDeclarator(loc,
159 | null,
160 | new Java.Modifiers(Mod.PUBLIC),
161 | classToType(loc, returnType),
162 | "evaluate",
163 | generateArgs(loc, FastTuple.class),
164 | new Java.Type[0],
165 | Lists.newArrayList(
166 | maybeGenerateReturn(loc,
167 | new Java.MethodInvocation(
168 | loc,
169 | null,
170 | "doEval",
171 | new Java.Rvalue[] {
172 | new Java.Cast(
173 | loc,
174 | new Java.ReferenceType(loc, schema.tupleClass().getCanonicalName().split("\\."), null),
175 | new Java.AmbiguousName(loc, new String[] {"tuple"})
176 | )
177 | }
178 | )
179 | )
180 | )
181 | );
182 | }
183 |
184 | private Java.MethodDeclarator generateBackendMethod(Parser parser) throws Exception {
185 | Location loc = parser.location();
186 | List statements = Lists.newArrayList();
187 | Java.Rvalue[] exprs = parser.parseExpressionList();
188 | for (int i=0; i pool = new TuplePool<>(10, false,
22 | new Loader() {
23 | @Override
24 | public Long[] createArray(int size) throws Exception {
25 | Long[] ary = new Long[size];
26 | for (int i=0; i() {
33 | @Override
34 | public void destroyArray(Long[] ary) {
35 |
36 | }
37 | }
38 | );
39 |
40 | for (int i=0; i<10; i++) {
41 | Long n = pool.checkout();
42 | assertEquals(new Long(0L), n);
43 | }
44 | exception.expect(IllegalStateException.class);
45 | pool.checkout();
46 | }
47 |
48 | @Test
49 | public void expandPoolTest() throws Exception {
50 | TuplePool pool = new TuplePool<>(10, true,
51 | new Loader() {
52 | @Override
53 | public Long[] createArray(int size) throws Exception {
54 | Long[] ary = new Long[size];
55 | for (int i=0; i() {
62 | @Override
63 | public void destroyArray(Long[] ary) {
64 |
65 | }
66 | }
67 | );
68 | for (int i=0; i<11; i++) {
69 | Long n = pool.checkout();
70 | assertEquals(new Long(0L), n);
71 | }
72 | assertEquals(20, pool.getSize());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/fasttuple-core/src/test/java/com/boundary/tuple/codegen/DirectTupleCodeGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.DirectTupleSchema;
4 | import com.boundary.tuple.FastTuple;
5 | import com.boundary.tuple.TupleSchema;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.*;
9 |
10 | /**
11 | * Created by cliff on 5/5/14.
12 | */
13 | public class DirectTupleCodeGeneratorTest {
14 |
15 | @Test
16 | public void testAccessorsGetGenerated() throws Exception {
17 | DirectTupleSchema schema = TupleSchema.builder().
18 | addField("a", Long.TYPE).
19 | addField("b", Integer.TYPE).
20 | addField("c", Short.TYPE).
21 | addField("d", Character.TYPE).
22 | addField("e", Byte.TYPE).
23 | addField("f", Float.TYPE).
24 | addField("g", Double.TYPE).
25 | directMemory().
26 | build();
27 |
28 | DirectTupleCodeGenerator codegen = new DirectTupleCodeGenerator(null, schema.getFieldNames(), schema.getFieldTypes(), schema.getLayout());
29 | Class clazz = codegen.cookToClass();
30 | assertGetterAndSetterGenerated(clazz, "a", long.class);
31 | assertGetterAndSetterGenerated(clazz, "b", int.class);
32 | assertGetterAndSetterGenerated(clazz, "c", short.class);
33 | assertGetterAndSetterGenerated(clazz, "d", char.class);
34 | assertGetterAndSetterGenerated(clazz, "e", byte.class);
35 | assertGetterAndSetterGenerated(clazz, "f", float.class);
36 | assertGetterAndSetterGenerated(clazz, "g", double.class);
37 | }
38 |
39 | @Test
40 | public void testAccessorsWork() throws Exception {
41 | TupleSchema schema = TupleSchema.builder().
42 | addField("a", Long.TYPE).
43 | addField("b", Integer.TYPE).
44 | addField("c", Short.TYPE).
45 | addField("d", Character.TYPE).
46 | addField("e", Byte.TYPE).
47 | addField("f", Float.TYPE).
48 | addField("g", Double.TYPE).
49 | directMemory().
50 | build();
51 | FastTuple tuple = schema.createTuple();
52 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "a", Long.TYPE, 100L);
53 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "b", Integer.TYPE, 40);
54 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "c", Short.TYPE, (short)10);
55 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "d", Character.TYPE, 'j');
56 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "e", Byte.TYPE, (byte)255);
57 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "f", Float.TYPE, 0.125f);
58 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "g", Double.TYPE, 0.125);
59 | }
60 |
61 | @Test
62 | public void testIndexedSetAndGet() throws Exception {
63 | TupleSchema schema = TupleSchema.builder().
64 | addField("a", Long.TYPE).
65 | addField("b", Integer.TYPE).
66 | addField("c", Short.TYPE).
67 | addField("d", Character.TYPE).
68 | addField("e", Byte.TYPE).
69 | addField("f", Float.TYPE).
70 | addField("g", Double.TYPE).
71 | directMemory().
72 | build();
73 | FastTuple tuple = schema.createTuple();
74 | assertIndexedGetterAndSetterRoundTrip(tuple, 1, 100L);
75 | assertIndexedGetterAndSetterRoundTrip(tuple, 2, 40);
76 | assertIndexedGetterAndSetterRoundTrip(tuple, 3, (short)10);
77 | assertIndexedGetterAndSetterRoundTrip(tuple, 4, 'j');
78 | assertIndexedGetterAndSetterRoundTrip(tuple, 5, (byte)255);
79 | assertIndexedGetterAndSetterRoundTrip(tuple, 6, 0.125f);
80 | assertIndexedGetterAndSetterRoundTrip(tuple, 7, 0.125);
81 | }
82 |
83 | @Test
84 | public void testIndexedTypedSetAndGet() throws Exception {
85 | TupleSchema schema = TupleSchema.builder().
86 | addField("a", Long.TYPE).
87 | addField("b", Integer.TYPE).
88 | addField("c", Short.TYPE).
89 | addField("d", Character.TYPE).
90 | addField("e", Byte.TYPE).
91 | addField("f", Float.TYPE).
92 | addField("g", Double.TYPE).
93 | directMemory().
94 | build();
95 | FastTuple tuple = schema.createTuple();
96 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 1, 100L);
97 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 2, 40);
98 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 3, (short) 10);
99 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 4, 'j');
100 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 5, (byte) 255);
101 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 6, 0.125f);
102 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 7, 0.125);
103 | }
104 |
105 | @Test
106 | public void testInterfaceIsImplemented() throws Exception {
107 | TupleSchema schema = TupleSchema.builder().
108 | addField("a", Long.TYPE).
109 | implementInterface(StaticBinding.class).
110 | directMemory().
111 | build();
112 | FastTuple tuple = schema.createTuple();
113 | assertTrue(tuple instanceof StaticBinding);
114 | }
115 |
116 | public void assertGetterAndSetterGenerated(Class clazz, String name, Class type) throws Exception {
117 | assertEquals(type, clazz.getDeclaredMethod(name).getReturnType());
118 | assertNotNull(clazz.getDeclaredMethod(name, type));
119 | }
120 |
121 | public void assertGetterAndSetterRoundTrip(Object tuple, Class clazz, String name, Class type, Object value) throws Exception {
122 | clazz.getDeclaredMethod(name, type).invoke(tuple, value);
123 | assertEquals(value, clazz.getDeclaredMethod(name).invoke(tuple));
124 | }
125 |
126 | public void assertIndexedGetterAndSetterRoundTrip(FastTuple tuple, int index, Object value) {
127 | tuple.set(index, value);
128 | assertEquals(value, tuple.get(index));
129 | }
130 |
131 | public void assertIndexedTypedGetterAndSetterRoundTrip(FastTuple tuple, int index, Object value) {
132 | if (value.getClass().equals(Long.class)) {
133 | tuple.setLong(index, (Long) value);
134 | assertEquals(value, tuple.getLong(index));
135 | } else if (value.getClass().equals(Short.class)) {
136 | tuple.setShort(index, (Short) value);
137 | assertEquals(value, tuple.getShort(index));
138 | } else if (value.getClass().equals(Character.class)) {
139 | tuple.setChar(index, (Character) value);
140 | assertEquals(value, tuple.getChar(index));
141 | } else if (value.getClass().equals(Integer.class)) {
142 | tuple.setInt(index, (Integer) value);
143 | assertEquals(value, tuple.getInt(index));
144 | } else if (value.getClass().equals(Byte.class)) {
145 | tuple.setByte(index, (Byte) value);
146 | assertEquals(value, tuple.getByte(index));
147 | } else if (value.getClass().equals(Float.class)) {
148 | tuple.setFloat(index, (Float) value);
149 | assertEquals(value, tuple.getFloat(index));
150 | } else if (value.getClass().equals(Double.class)) {
151 | tuple.setDouble(index, (Double) value);
152 | assertEquals(value, tuple.getDouble(index));
153 | }
154 | }
155 |
156 | public static interface StaticBinding {
157 | public void a(long a);
158 | public long a();
159 | }
160 | }
161 |
162 |
163 |
--------------------------------------------------------------------------------
/fasttuple-core/src/test/java/com/boundary/tuple/codegen/HeapTupleCodeGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.FastTuple;
4 | import com.boundary.tuple.HeapTupleSchema;
5 | import com.boundary.tuple.TupleSchema;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.*;
9 |
10 | /**
11 | * Created by cliff on 5/9/14.
12 | */
13 | public class HeapTupleCodeGeneratorTest {
14 | @Test
15 | public void testAccessorsGetGenerated() throws Exception {
16 | HeapTupleSchema schema = TupleSchema.builder().
17 | addField("a", Long.TYPE).
18 | addField("b", Integer.TYPE).
19 | addField("c", Short.TYPE).
20 | addField("d", Character.TYPE).
21 | addField("e", Byte.TYPE).
22 | addField("f", Float.TYPE).
23 | addField("g", Double.TYPE).
24 | heapMemory().
25 | build();
26 |
27 | HeapTupleCodeGenerator codegen = new HeapTupleCodeGenerator(null, schema.getFieldNames(), schema.getFieldTypes());
28 | Class clazz = codegen.cookToClass();
29 | assertGetterAndSetterGenerated(clazz, "a", long.class);
30 | assertGetterAndSetterGenerated(clazz, "b", int.class);
31 | assertGetterAndSetterGenerated(clazz, "c", short.class);
32 | assertGetterAndSetterGenerated(clazz, "d", char.class);
33 | assertGetterAndSetterGenerated(clazz, "e", byte.class);
34 | assertGetterAndSetterGenerated(clazz, "f", float.class);
35 | assertGetterAndSetterGenerated(clazz, "g", double.class);
36 | }
37 |
38 | @Test
39 | public void testAccessorsWork() throws Exception {
40 | TupleSchema schema = TupleSchema.builder().
41 | addField("a", Long.TYPE).
42 | addField("b", Integer.TYPE).
43 | addField("c", Short.TYPE).
44 | addField("d", Character.TYPE).
45 | addField("e", Byte.TYPE).
46 | addField("f", Float.TYPE).
47 | addField("g", Double.TYPE).
48 | heapMemory().
49 | build();
50 | FastTuple tuple = schema.createTuple();
51 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "a", Long.TYPE, 100L);
52 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "b", Integer.TYPE, 40);
53 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "c", Short.TYPE, (short)10);
54 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "d", Character.TYPE, 'j');
55 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "e", Byte.TYPE, (byte)255);
56 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "f", Float.TYPE, 0.125f);
57 | assertGetterAndSetterRoundTrip(tuple, schema.tupleClass(), "g", Double.TYPE, 0.125);
58 | }
59 |
60 | @Test
61 | public void testIndexedSetAndGet() throws Exception {
62 | TupleSchema schema = TupleSchema.builder().
63 | addField("a", Long.TYPE).
64 | addField("b", Integer.TYPE).
65 | addField("c", Short.TYPE).
66 | addField("d", Character.TYPE).
67 | addField("e", Byte.TYPE).
68 | addField("f", Float.TYPE).
69 | addField("g", Double.TYPE).
70 | heapMemory().
71 | build();
72 | FastTuple tuple = schema.createTuple();
73 | assertIndexedGetterAndSetterRoundTrip(tuple, 1, 100L);
74 | assertIndexedGetterAndSetterRoundTrip(tuple, 2, 40);
75 | assertIndexedGetterAndSetterRoundTrip(tuple, 3, (short)10);
76 | assertIndexedGetterAndSetterRoundTrip(tuple, 4, 'j');
77 | assertIndexedGetterAndSetterRoundTrip(tuple, 5, (byte)255);
78 | assertIndexedGetterAndSetterRoundTrip(tuple, 6, 0.125f);
79 | assertIndexedGetterAndSetterRoundTrip(tuple, 7, 0.125);
80 | }
81 |
82 | @Test
83 | public void testIndexedTypedSetAndGet() throws Exception {
84 | TupleSchema schema = TupleSchema.builder().
85 | addField("a", Long.TYPE).
86 | addField("b", Integer.TYPE).
87 | addField("c", Short.TYPE).
88 | addField("d", Character.TYPE).
89 | addField("e", Byte.TYPE).
90 | addField("f", Float.TYPE).
91 | addField("g", Double.TYPE).
92 | heapMemory().
93 | build();
94 | FastTuple tuple = schema.createTuple();
95 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 1, 100L);
96 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 2, 40);
97 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 3, (short)10);
98 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 4, 'j');
99 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 5, (byte)255);
100 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 6, 0.125f);
101 | assertIndexedTypedGetterAndSetterRoundTrip(tuple, 7, 0.125);
102 | }
103 |
104 | @Test
105 | public void testInterfaceIsImplemented() throws Exception {
106 | TupleSchema schema = TupleSchema.builder().
107 | addField("a", Long.TYPE).
108 | implementInterface(StaticBinding.class).
109 | heapMemory().
110 | build();
111 | FastTuple tuple = schema.createTuple();
112 | assertTrue(tuple instanceof StaticBinding);
113 | }
114 |
115 | public void assertGetterAndSetterGenerated(Class clazz, String name, Class type) throws Exception {
116 | assertEquals(type, clazz.getDeclaredMethod(name).getReturnType());
117 | assertNotNull(clazz.getDeclaredMethod(name, type));
118 | }
119 |
120 | public void assertGetterAndSetterRoundTrip(Object tuple, Class clazz, String name, Class type, Object value) throws Exception {
121 | clazz.getDeclaredMethod(name, type).invoke(tuple, value);
122 | assertEquals(value, clazz.getDeclaredMethod(name).invoke(tuple));
123 | }
124 |
125 | public void assertIndexedGetterAndSetterRoundTrip(FastTuple tuple, int index, Object value) {
126 | tuple.set(index, value);
127 | assertEquals(value, tuple.get(index));
128 | }
129 |
130 | public void assertIndexedTypedGetterAndSetterRoundTrip(FastTuple tuple, int index, Object value) {
131 | if (value.getClass().equals(Long.class)) {
132 | tuple.setLong(index, (Long) value);
133 | assertEquals(value, tuple.getLong(index));
134 | } else if (value.getClass().equals(Short.class)) {
135 | tuple.setShort(index, (Short) value);
136 | assertEquals(value, tuple.getShort(index));
137 | } else if (value.getClass().equals(Character.class)) {
138 | tuple.setChar(index, (Character) value);
139 | assertEquals(value, tuple.getChar(index));
140 | } else if (value.getClass().equals(Integer.class)) {
141 | tuple.setInt(index, (Integer) value);
142 | assertEquals(value, tuple.getInt(index));
143 | } else if (value.getClass().equals(Byte.class)) {
144 | tuple.setByte(index, (Byte) value);
145 | assertEquals(value, tuple.getByte(index));
146 | } else if (value.getClass().equals(Float.class)) {
147 | tuple.setFloat(index, (Float) value);
148 | assertEquals(value, tuple.getFloat(index));
149 | } else if (value.getClass().equals(Double.class)) {
150 | tuple.setDouble(index, (Double) value);
151 | assertEquals(value, tuple.getDouble(index));
152 | }
153 | }
154 |
155 | public static interface StaticBinding {
156 | public void a(long a);
157 | public long a();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/fasttuple-core/src/test/java/com/boundary/tuple/codegen/TupleExpressionGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.boundary.tuple.codegen;
2 |
3 | import com.boundary.tuple.FastTuple;
4 | import com.boundary.tuple.TupleSchema;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.*;
8 |
9 | /**
10 | * Created by cliff on 5/12/14.
11 | */
12 | public class TupleExpressionGeneratorTest {
13 |
14 | @Test
15 | public void testGetLongOut() throws Exception {
16 | TupleSchema schema = TupleSchema.builder().
17 | addField("a", Long.TYPE).
18 | addField("b", Long.TYPE).
19 | addField("c", Long.TYPE).
20 | heapMemory().
21 | build();
22 |
23 | FastTuple tuple = schema.createTuple();
24 | tuple.setLong(1, 100L);
25 | tuple.setLong(2, 600L);
26 | tuple.setLong(3, 1000L);
27 |
28 | TupleExpressionGenerator.LongTupleExpression eval = TupleExpressionGenerator.builder().expression("tuple.a + tuple.b + tuple.c").schema(schema).returnLong();
29 | assertEquals(1700L, eval.evaluate(tuple));
30 | }
31 |
32 | @Test
33 | public void testMultiExpr() throws Exception {
34 | TupleSchema schema = TupleSchema.builder().
35 | addField("a", Long.TYPE).
36 | addField("b", Long.TYPE).
37 | addField("c", Long.TYPE).
38 | heapMemory().
39 | build();
40 |
41 | FastTuple tuple = schema.createTuple();
42 | TupleExpressionGenerator.TupleExpression eval = TupleExpressionGenerator.builder().expression("tuple.a(100), tuple.b(200), tuple.c(300)").schema(schema).returnVoid();
43 | eval.evaluate(tuple);
44 | assertEquals(100L, tuple.getLong(1));
45 | assertEquals(200, tuple.getLong(2));
46 | assertEquals(300, tuple.getLong(3));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.boundary
5 | fasttuple
6 | pom
7 | 0.1-SNAPSHOT
8 |
9 | FastTuple
10 | FastTuple is a library for generating heterogeneous tuples of primitive types from a runtime defined schema without boxing.
11 | https://github.com/boundary/fasttuple
12 |
13 |
14 | fasttuple-core
15 | fasttuple-bench
16 |
17 |
18 |
19 |
20 |
21 | org.apache.maven.plugins
22 | maven-source-plugin
23 | 2.2.1
24 |
25 |
26 | attach-sources
27 | package
28 |
29 | jar
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.apache.maven.plugins
37 | maven-compiler-plugin
38 | 3.1
39 |
40 | 1.6
41 | 1.6
42 |
43 |
44 |
45 |
46 | org.apache.maven.plugins
47 | maven-javadoc-plugin
48 | 2.9.1
49 |
50 |
51 | attach-javadoc
52 | package
53 |
54 | jar
55 |
56 |
57 |
58 |
59 |
60 | org.apache.maven.plugins
61 | maven-gpg-plugin
62 |
63 |
64 | sign-artifacts
65 | verify
66 |
67 | sign
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | scm:git:git@github.com:boundary/fasttuple.git
77 | scm:git:git@github.com:boundary/fasttuple.git
78 | git@github.com:boundary/fasttuple.git
79 |
80 |
81 |
82 |
83 | Cliff Moon
84 | cliff@boundary.com
85 |
86 |
87 | Philip Warren
88 | philip@boundary.com
89 |
90 |
91 |
92 |
93 |
94 | The Apache Software License, Version 2.0
95 | http://www.apache.org/licenses/LICENSE-2.0.txt
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # FastTuple
2 |
3 | ## Introduction
4 |
5 | There are lots of good things about working on the JVM, like a world class JIT, operating system threads, and a world class garbage collector. However, one limiting factor can often be the interaction between primitive types and referenced types in Java. Primitive types are the built in types that represent integral numbers, floating point numbers, and boolean yes/no values. Primitives are also quite fast and memory efficient: they get allocated either on the stack if they're being used in a method, or inlined in an object when they're declared as field members. And they wind up being fast because the JIT can often optimize their access down to a single CPU instruction. This works really well when you know what types a class will hold as its state beforehand. If, on the other hand, you don't know what an object or array will hold at compile time the JVM forces you to box primitives. Boxing means that the primitives get wrapped in a heap allocated object, and their container will hold a reference to them. That type of overhead winds up being inefficient both in access time and memory space. Access time suffers because this type of layout breaks locality of reference, which is the principle that memory frequently accessed together should be stored adjacently. All modern memory hierarchies optimize for locality of reference in their caching implementations. The extra allocations and garbage generated also put pressure on the JVM's garbage collector, which can often be a cause of long pause times.
6 |
7 | We wrote FastTuple to try and help solve this problem. FastTuple generates heterogeneous collections of primitive values and ensures as best it can that they will be laid out adjacently in memory. The individual values in the tuple can either be accessed from a statically bound interface, via an indexed accessor, or via reflective or other dynamic invocation techniques. FastTuple is designed to deal with a large number of tuples therefore it will also attempt to pool tuples such that they do not add significantly to the GC load of a system. FastTuple is also capable of allocating the tuple value storage entirely off-heap, using Java's direct memory capabilities.
8 |
9 | FastTuple pulls off its trick via runtime bytecode generation. The user supplies it with a schema of field names and types. That schema is then built into a Java class definition which will contain accessor methods and either field definitions or the memory address for an off heap allocation, depending on which storage method was requested. The resulting Java class gets compiled into bytecode and then loaded as a reflective Class object. This Class object can then be used to create instances of the new class.
10 |
11 | ## Building
12 |
13 | This package depends on Janino 2.7.4, which is not currently getting pushed to maven central. As soon as that happens (we have a ticket in with the maintainer) we will begin pushing FastTuple to maven central as well. In the meantime you can build FastTuple like so:
14 |
15 | ```bash
16 | wget http://janino.net/download/janino-2.7.4.zip
17 | unzip janino-2.7.4.zip
18 | mvn install:install-file -Dfile=janino-2.7.4/janino.jar -Dsources=janino-2.7.4/janino-src.zip -DgroupId=org.codehaus.janino -DartifactId=janino -Dversion=2.7.4 -Dpackaging=jar
19 | mvn install:install-file -Dfile=janino-2.7.4/commons-compiler.jar -Dsources=janino-2.7.4/commons-compiler-src.zip -DgroupId=org.codehaus.janino -DartifactId=commons-compiler -Dversion=2.7.4 -Dpackaging=jar
20 | mvn install
21 | ```
22 |
23 | ## Note on GPG
24 |
25 | You'll also need to ensure that you have GPG available on the command line. Mac OS X and Windows users may need to install via (https://gpgtools.org/) or cygwin respectively.
26 |
27 | ## Usage
28 |
29 | Interaction with FastTuple primarily takes place via the TupleSchema class. Each instance of TupleSchema describes a separate type of FastTuple both from the perspective of the FastTuple library and the JVM. At this time, allowable field types are the primitive classes for: long, int, short, char, byte, float, double. Support for String is planned for a later release. Some examples:
30 |
31 | ### Heap Allocated Tuples
32 |
33 | ```java
34 | TupleSchema schema = TupleSchema.builder().
35 | addField("fieldA", Long.TYPE).
36 | addField("fieldB", Int.TYPE).
37 | addField("fieldC", Short.TYPE).
38 | heapMemory().
39 | build();
40 |
41 | //creates a new tuple allocated on the JVM heap
42 | FastTuple tuple = schema.createTuple();
43 | ```
44 |
45 | ### Direct Allocated Tuples
46 |
47 | ```java
48 | TupleSchema schema = TupleSchema.builder().
49 | addField("fieldA", Long.TYPE).
50 | addField("fieldB", Int.TYPE).
51 | addField("fieldC", Short.TYPE).
52 | directMemory().
53 | build();
54 |
55 | //creates a new tuple, allocating memory off heap
56 | FastTuple tuple = schema.createTuple();
57 | //do some stuff
58 | tuple.setLong(1, 10000L);
59 | tuple.setInt(2, 50);
60 | tuple.setShort(3, (short)256);
61 | //if you don't destroy the tuple you are leaking memory
62 | schema.destroy(tuple);
63 | ```
64 |
65 | ### Aligning Direct Allocated Tuples
66 |
67 | Direct allocated tuples can be aligned such that they do not share cache lines. This is useful for situations where
68 | adjacent tuples are manipulated concurrently by different threads: an adequately padded FastTuple can eliminate false sharing in the CPU cache architecture. Veriifying that property, as expected, will require extensive benchmarking inside of the target system.
69 |
70 | ```java
71 | TupleSchema schema = TupleSchema.builder().
72 | addField("fieldA", Long.TYPE).
73 | addField("fieldB", Int.TYPE).
74 | addField("fieldC", Short.TYPE).
75 | directMemory().
76 | padToWordSize(64).
77 | build();
78 |
79 | //creates a new tuple, allocating memory off heap
80 | FastTuple tuple = schema.createTuple();
81 | //do some stuff
82 | tuple.setLong(1, 10000L);
83 | tuple.setInt(2, 50);
84 | tuple.setShort(3, (short)256);
85 | //if you don't destroy the tuple you are leaking memory
86 | schema.destroy(tuple);
87 | ```
88 |
89 | ### Utilizing Tuple Pools
90 |
91 | Each schema will allocate a tuple pool per accessing thread if a poolSize is set.
92 |
93 | ```java
94 | TupleSchema schema = TupleSchema.builder().
95 | addField("fieldA", Long.TYPE).
96 | addField("fieldB", Int.TYPE).
97 | addField("fieldC", Short.TYPE).
98 | poolOfSize(1024).
99 | //allocates an extra poolOfSize records when the pool is empty
100 | .expandingPool().
101 | build();
102 |
103 | //checks a tuple from the pool
104 | FastTuple tuple = schema.pool().checkout();
105 | //do some stuff
106 | tuple.setLong(1, 10000L);
107 | tuple.setInt(2, 50);
108 | tuple.setShort(3, (short)256);
109 | //if you don't check the tuple back in you either leak memory or objects. bad dog.
110 | schema.pool().release(tuple);
111 | ```
112 |
113 | ## Performance
114 |
115 | One of the main goals of this library is performance. Toward that end it has a full suite of microbenchmarks to test the various supported means of accessing and manipulating tuples for both tuning the library and showing the tradeoffs in overhead for things like pooling and allocating tuples on demand. Here's what a full run looks like on a late 2013 macbook pro 2.6ghz with Java 8 1.8.0_05-b13.
116 |
117 | ```
118 | Benchmark Mode Samples Mean Mean error Units
119 | c.b.t.AccessMethodBenchmark.testAllocateSetAndDeallocate thrpt 20 7069.954 181.689 ops/ms
120 | c.b.t.AccessMethodBenchmark.testClass thrpt 20 1628847.332 21756.564 ops/ms
121 | c.b.t.AccessMethodBenchmark.testFastPool thrpt 20 30715.791 570.086 ops/ms
122 | c.b.t.AccessMethodBenchmark.testFastTuplePreAllocIndexed thrpt 20 160173.234 2498.380 ops/ms
123 | c.b.t.AccessMethodBenchmark.testFastTuplePreAllocIndexedBoxing thrpt 20 59117.984 630.813 ops/ms
124 | c.b.t.AccessMethodBenchmark.testFastTupleStaticBinding thrpt 20 157928.877 2411.094 ops/ms
125 | c.b.t.AccessMethodBenchmark.testInvokeDynamic thrpt 20 26085.594 1158.292 ops/ms
126 | c.b.t.AccessMethodBenchmark.testLongArray thrpt 20 1721846.666 23422.990 ops/ms
127 | c.b.t.AccessMethodBenchmark.testOffheapAllocateAndSet thrpt 20 7512.737 167.141 ops/ms
128 | c.b.t.AccessMethodBenchmark.testOffheapDirectSet thrpt 20 898743.426 37659.166 ops/ms
129 | c.b.t.AccessMethodBenchmark.testOffheapSchemaSet thrpt 20 367841.110 4282.620 ops/ms
130 | c.b.t.AccessMethodBenchmark.testPooledObject thrpt 20 12572.464 155.458 ops/ms
131 | c.b.t.AccessMethodBenchmark.testQueuedObject thrpt 20 25092.751 242.771 ops/ms
132 | c.b.t.AccessMethodBenchmark.testReflectField thrpt 20 28850.478 275.462 ops/ms
133 | c.b.t.AccessMethodBenchmark.testStormTuple thrpt 20 79693.517 888.956 ops/ms
134 | c.b.t.AccessMethodBenchmark.testTuplePool thrpt 20 70790.084 950.775 ops/ms
135 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaAllocate thrpt 20 7214.300 98.387 ops/ms
136 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaDeque thrpt 20 153937.210 2534.986 ops/ms
137 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPoolEval thrpt 20 53563.512 836.586 ops/ms
138 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPoolIface thrpt 20 57669.480 716.962 ops/ms
139 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPoolIndexed thrpt 20 57633.447 1025.338 ops/ms
140 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPoolIndexedBoxed thrpt 20 38984.939 823.182 ops/ms
141 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPreallocEval thrpt 20 332142.591 6426.566 ops/ms
142 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPreallocIface thrpt 20 370593.271 5369.162 ops/ms
143 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPreallocIndexed thrpt 20 377811.881 7077.471 ops/ms
144 | c.b.t.FastTupleBenchmarks.DirectBenchmarks.measureDirectSchemaPreallocIndexedBoxed thrpt 20 115699.056 2402.657 ops/ms
145 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaAllocate thrpt 20 990788.476 12295.764 ops/ms
146 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaDeque thrpt 20 173527.417 3619.733 ops/ms
147 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPoolEval thrpt 20 59377.121 1071.014 ops/ms
148 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPoolIface thrpt 20 62391.209 765.821 ops/ms
149 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPoolIndexed thrpt 20 62120.412 1105.981 ops/ms
150 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPoolIndexedBoxed thrpt 20 42187.554 737.124 ops/ms
151 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPreallocEval thrpt 20 397337.746 5274.807 ops/ms
152 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPreallocEvalField thrpt 20 579136.768 8818.827 ops/ms
153 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPreallocIface thrpt 20 554449.236 6740.073 ops/ms
154 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPreallocIndexed thrpt 20 536258.735 10417.522 ops/ms
155 | c.b.t.FastTupleBenchmarks.HeapBenchmarks.measureHeapSchemaPreallocIndexedBoxed thrpt 20 202220.576 3806.694 ops/ms
156 | ```
157 |
158 | To run the benchmarks:
159 |
160 | ```
161 | mvn package
162 | java -jar fasttuple-bench/target/microbenchmarks.jar
163 | ```
164 |
--------------------------------------------------------------------------------