1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.record;
20 
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataInputStream;
24 import java.io.DataOutputStream;
25 import java.io.IOException;
26 import java.lang.reflect.Array;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.util.Random;
30 
31 /**
32  * Benchmark for various types of serializations
33  */
34 public class RecordBench {
35 
36   private static class Times {
37     long init;
38     long serialize;
39     long deserialize;
40     long write;
41     long readFields;
42   };
43 
44   private static final long SEED = 0xDEADBEEFL;
45   private static final Random rand = new Random();
46 
47   /** Do not allow to create a new instance of RecordBench */
RecordBench()48   private RecordBench() {}
49 
initBuffers(Record[] buffers)50   private static void initBuffers(Record[] buffers) {
51     final int BUFLEN = 32;
52     for (int idx = 0; idx < buffers.length; idx++) {
53       buffers[idx] = new RecBuffer();
54       int buflen = rand.nextInt(BUFLEN);
55       byte[] bytes = new byte[buflen];
56       rand.nextBytes(bytes);
57       ((RecBuffer)buffers[idx]).setData(new Buffer(bytes));
58     }
59   }
60 
initStrings(Record[] strings)61   private static void initStrings(Record[] strings) {
62     final int STRLEN = 32;
63     for (int idx = 0; idx < strings.length; idx++) {
64       strings[idx] = new RecString();
65       int strlen = rand.nextInt(STRLEN);
66       StringBuilder sb = new StringBuilder(strlen);
67       for (int ich = 0; ich < strlen; ich++) {
68         int cpt = 0;
69         while (true) {
70           cpt = rand.nextInt(0x10FFFF+1);
71           if (Utils.isValidCodePoint(cpt)) {
72             break;
73           }
74         }
75         sb.appendCodePoint(cpt);
76       }
77       ((RecString)strings[idx]).setData(sb.toString());
78     }
79   }
80 
initInts(Record[] ints)81   private static void initInts(Record[] ints) {
82     for (int idx = 0; idx < ints.length; idx++) {
83       ints[idx] = new RecInt();
84       ((RecInt)ints[idx]).setData(rand.nextInt());
85     }
86   }
87 
makeArray(String type, int numRecords, Times times)88   private static Record[] makeArray(String type, int numRecords, Times times) {
89     Method init = null;
90     try {
91       init = RecordBench.class.getDeclaredMethod("init"+
92                                                  toCamelCase(type) + "s",
93                                                  new Class[] {Record[].class});
94     } catch (NoSuchMethodException ex) {
95       throw new RuntimeException(ex);
96     }
97 
98     Record[] records = new Record[numRecords];
99     times.init = System.nanoTime();
100     try {
101       init.invoke(null, new Object[]{records});
102     } catch (Exception ex) {
103       throw new RuntimeException(ex);
104     }
105     times.init = System.nanoTime() - times.init;
106     return records;
107   }
108 
runBinaryBench(String type, int numRecords, Times times)109   private static void runBinaryBench(String type, int numRecords, Times times)
110     throws IOException {
111     Record[] records = makeArray(type, numRecords, times);
112     ByteArrayOutputStream bout = new ByteArrayOutputStream();
113     BinaryRecordOutput rout = new BinaryRecordOutput(bout);
114     DataOutputStream dout = new DataOutputStream(bout);
115 
116     for(int idx = 0; idx < numRecords; idx++) {
117       records[idx].serialize(rout);
118     }
119     bout.reset();
120 
121     times.serialize = System.nanoTime();
122     for(int idx = 0; idx < numRecords; idx++) {
123       records[idx].serialize(rout);
124     }
125     times.serialize = System.nanoTime() - times.serialize;
126 
127     byte[] serialized = bout.toByteArray();
128     ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
129     BinaryRecordInput rin = new BinaryRecordInput(bin);
130 
131     times.deserialize = System.nanoTime();
132     for(int idx = 0; idx < numRecords; idx++) {
133       records[idx].deserialize(rin);
134     }
135     times.deserialize = System.nanoTime() - times.deserialize;
136 
137     bout.reset();
138 
139     times.write = System.nanoTime();
140     for(int idx = 0; idx < numRecords; idx++) {
141       records[idx].write(dout);
142     }
143     times.write = System.nanoTime() - times.write;
144 
145     bin.reset();
146     DataInputStream din = new DataInputStream(bin);
147 
148     times.readFields = System.nanoTime();
149     for(int idx = 0; idx < numRecords; idx++) {
150       records[idx].readFields(din);
151     }
152     times.readFields = System.nanoTime() - times.readFields;
153   }
154 
runCsvBench(String type, int numRecords, Times times)155   private static void runCsvBench(String type, int numRecords, Times times)
156     throws IOException {
157     Record[] records = makeArray(type, numRecords, times);
158     ByteArrayOutputStream bout = new ByteArrayOutputStream();
159     CsvRecordOutput rout = new CsvRecordOutput(bout);
160 
161     for(int idx = 0; idx < numRecords; idx++) {
162       records[idx].serialize(rout);
163     }
164     bout.reset();
165 
166     times.serialize = System.nanoTime();
167     for(int idx = 0; idx < numRecords; idx++) {
168       records[idx].serialize(rout);
169     }
170     times.serialize = System.nanoTime() - times.serialize;
171 
172     byte[] serialized = bout.toByteArray();
173     ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
174     CsvRecordInput rin = new CsvRecordInput(bin);
175 
176     times.deserialize = System.nanoTime();
177     for(int idx = 0; idx < numRecords; idx++) {
178       records[idx].deserialize(rin);
179     }
180     times.deserialize = System.nanoTime() - times.deserialize;
181   }
182 
runXmlBench(String type, int numRecords, Times times)183   private static void runXmlBench(String type, int numRecords, Times times)
184     throws IOException {
185     Record[] records = makeArray(type, numRecords, times);
186     ByteArrayOutputStream bout = new ByteArrayOutputStream();
187     XmlRecordOutput rout = new XmlRecordOutput(bout);
188 
189     for(int idx = 0; idx < numRecords; idx++) {
190       records[idx].serialize(rout);
191     }
192     bout.reset();
193 
194     bout.write("<records>\n".getBytes());
195 
196     times.serialize = System.nanoTime();
197     for(int idx = 0; idx < numRecords; idx++) {
198       records[idx].serialize(rout);
199     }
200     times.serialize = System.nanoTime() - times.serialize;
201 
202     bout.write("</records>\n".getBytes());
203 
204     byte[] serialized = bout.toByteArray();
205     ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
206 
207     times.deserialize = System.nanoTime();
208     XmlRecordInput rin = new XmlRecordInput(bin);
209     for(int idx = 0; idx < numRecords; idx++) {
210       records[idx].deserialize(rin);
211     }
212     times.deserialize = System.nanoTime() - times.deserialize;
213   }
214 
printTimes(String type, String format, int numRecords, Times times)215   private static void printTimes(String type,
216                                  String format,
217                                  int numRecords,
218                                  Times times) {
219     System.out.println("Type: " + type + " Format: " + format +
220                        " #Records: "+numRecords);
221     if (times.init != 0) {
222       System.out.println("Initialization Time (Per record) : "+
223                          times.init/numRecords + " Nanoseconds");
224     }
225 
226     if (times.serialize != 0) {
227       System.out.println("Serialization Time (Per Record) : "+
228                          times.serialize/numRecords + " Nanoseconds");
229     }
230 
231     if (times.deserialize != 0) {
232       System.out.println("Deserialization Time (Per Record) : "+
233                          times.deserialize/numRecords + " Nanoseconds");
234     }
235 
236     if (times.write != 0) {
237       System.out.println("Write Time (Per Record) : "+
238                          times.write/numRecords + " Nanoseconds");
239     }
240 
241     if (times.readFields != 0) {
242       System.out.println("ReadFields Time (Per Record) : "+
243                          times.readFields/numRecords + " Nanoseconds");
244     }
245 
246     System.out.println();
247   }
248 
toCamelCase(String inp)249   private static String toCamelCase(String inp) {
250     char firstChar = inp.charAt(0);
251     if (Character.isLowerCase(firstChar)) {
252       return ""+Character.toUpperCase(firstChar) + inp.substring(1);
253     }
254     return inp;
255   }
256 
exitOnError()257   private static void exitOnError() {
258     String usage = "RecordBench {buffer|string|int}"+
259       " {binary|csv|xml} <numRecords>";
260     System.out.println(usage);
261     System.exit(1);
262   }
263 
264   /**
265    * @param args the command line arguments
266    */
main(String[] args)267   public static void main(String[] args) throws IOException {
268     String version = "RecordBench v0.1";
269     System.out.println(version+"\n");
270 
271     if (args.length != 3) {
272       exitOnError();
273     }
274 
275     String typeName = args[0];
276     String format = args[1];
277     int numRecords = Integer.decode(args[2]).intValue();
278 
279     Method bench = null;
280     try {
281       bench = RecordBench.class.getDeclaredMethod("run"+
282                                                   toCamelCase(format) + "Bench",
283                                                   new Class[] {String.class, Integer.TYPE, Times.class});
284     } catch (NoSuchMethodException ex) {
285       ex.printStackTrace();
286       exitOnError();
287     }
288 
289     if (numRecords < 0) {
290       exitOnError();
291     }
292 
293     // dry run
294     rand.setSeed(SEED);
295     Times times = new Times();
296     try {
297       bench.invoke(null, new Object[] {typeName, numRecords, times});
298     } catch (Exception ex) {
299       ex.printStackTrace();
300       System.exit(1);
301     }
302 
303     // timed run
304     rand.setSeed(SEED);
305     try {
306       bench.invoke(null, new Object[] {typeName, numRecords, times});
307     } catch (Exception ex) {
308       ex.printStackTrace();
309       System.exit(1);
310     }
311     printTimes(typeName, format, numRecords, times);
312   }
313 }
314