package com.ctriposs.baiji;
import com.ctriposs.baiji.generic.GenericBenchmarkRecord;
import com.ctriposs.baiji.specific.*;
import com.google.common.collect.Lists;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class JsonSerializerBenchmarkTest {
private BenchmarkSerializer serializer;
private int loop;
private boolean run;
private ConcurrentHashMap<String, List<ExecutionResult>> records = new ConcurrentHashMap<>();
public static void main(String[] args) throws Exception {
JsonSerializerBenchmarkTest test = new JsonSerializerBenchmarkTest();
test.setUp();
test.testBenchmark();
test.tearDown();
}
/*@Before*/
public void setUp() throws Exception {
loop = 50;
run = false;
warmUp();
}
/*@Test*/
public void testBenchmark() throws Exception {
loop = 1000;
run = true;
testJsonSerializerBenchmark();
testBinaryBenchmark();
testJacksonBenchmark();
print(records);
}
public void testJsonSerializerBenchmark() throws Exception {
serializer = new JsonSerializerBenchmark();
intBenchmark();
booleanBenchmark();
longBenchmark();
doubleBenchmark();
stringBenchmark();
bytesBenchmark();
enumBenchmark();
arrayBenchmark();
mapBenchmark();
recordBenchmark();
if (run) {
benchmarkFiveThreads();
benchmarkTenThreads();
benchmarkTwentyThreads();
}
}
public void testJacksonBenchmark() throws Exception {
serializer = new JacksonBenchmark();
intBenchmark();
booleanBenchmark();
longBenchmark();
doubleBenchmark();
stringBenchmark();
bytesBenchmark();
enumBenchmark();
arrayBenchmark();
mapBenchmark();
recordBenchmark();
if (run) {
benchmarkFiveThreads();
benchmarkTenThreads();
benchmarkTwentyThreads();
}
}
public void testBinaryBenchmark() throws Exception {
serializer = new BinaryBenchmark();
intBenchmark();
booleanBenchmark();
longBenchmark();
doubleBenchmark();
stringBenchmark();
bytesBenchmark();
enumBenchmark();
arrayBenchmark();
mapBenchmark();
recordBenchmark();
if (run) {
benchmarkFiveThreads();
benchmarkTenThreads();
benchmarkTwentyThreads();
}
}
@After
public void tearDown() throws Exception {
}
private void warmUp() throws Exception {
// Pre-run until the JVM Stable
testJsonSerializerBenchmark();
testBinaryBenchmark();
testJacksonBenchmark();
System.out.println("warm up done");
}
private void intBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(42, "\"int\"");
appendResults("write int", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse int", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void booleanBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(true, "\"boolean\"");
appendResults("write boolean", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse boolean", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void longBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(1024 * 1024 * 16L, "\"long\"");
appendResults("write long", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse long", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void doubleBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(24.00000001, "\"double\"");
appendResults("write double", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse double", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void stringBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark("testString", "\"string\"");
appendResults("write string", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse string", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void bytesBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark("testBytes".getBytes(), "\"bytes\"");
appendResults("write bytes", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse bytes", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void enumBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(Enum1Values.RED, "{\"type\":\"enum\",\"name\":\"Enum1Values\",\"namespace\":\"com.ctriposs.baiji.specific\",\"doc\":null,\"symbols\":[\"BLUE\",\"RED\",\"GREEN\"]}");
appendResults("write enum", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse enum", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void arrayBenchmark() {
serializer.clearCache();
double[] results = singleFieldBenchmark(Lists.newArrayList(1, 2, 3, 4, 5), "{\"type\":\"array\",\"items\":\"int\"}");
appendResults("write array", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse array", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void mapBenchmark() {
serializer.clearCache();
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("1a", 1);
map.put("2b", 2);
map.put("3c", 3);
double[] results = singleFieldBenchmark(map, "{\"type\":\"map\",\"values\":\"int\"}");
appendResults("write map", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse map", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void recordBenchmark() {
serializer.clearCache();
ModelFilling2 record = new ModelFilling2(1024 * 1024 * 16L, "testRecord", Lists.newArrayList("a", "b", "c"), Enum2Values.BIKE);
double[] results = singleFieldBenchmark(record, record.getSchema().toString());
appendResults("write record", new ExecutionResult(serializer.getName(), results[0], (int)results[2]));
appendResults("parse record", new ExecutionResult(serializer.getName(), results[1], (int)results[2]));
}
private void benchmarkFiveThreads() throws ExecutionException, InterruptedException {
benchmarkMultiThread(5);
}
private void benchmarkTenThreads() throws ExecutionException, InterruptedException {
benchmarkMultiThread(10);
}
private void benchmarkTwentyThreads() throws ExecutionException, InterruptedException {
benchmarkMultiThread(20);
}
private void benchmarkMultiThread(int threadNum) throws ExecutionException, InterruptedException {
serializer.clearCache();
ExecutorService executorService = Executors.newCachedThreadPool();
ArrayList<Future<ArrayList<Double>>> futures = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
futures.add(executorService.submit(new Callable<ArrayList<Double>>() {
@Override
public ArrayList<Double> call() throws Exception {
ModelFilling2 record = new ModelFilling2(1024 * 1024 * 16L, "testRecord", Lists.newArrayList("a", "b", "c"), Enum2Values.BIKE);
double[] results = singleFieldBenchmark(record, record.getSchema().toString());
ArrayList<Double> result = new ArrayList<>();
result.add(results[0]);
result.add(results[1]);
result.add(results[2]);
return result;
}
}));
}
ArrayList<ArrayList<Double>> results = new ArrayList<>();
for (Future<ArrayList<Double>> future : futures) {
results.add(future.get());
}
executorService.shutdown();
double serialize = 0;
double deserialize = 0;
int bytesSize = 0;
for (int i = 0; i < results.size(); i++) {
ArrayList<Double> perThread = results.get(i);
serialize += perThread.get(0);
deserialize += perThread.get(1);
bytesSize = perThread.get(2).intValue();
}
serialize = serialize/results.size();
deserialize = deserialize/results.size();
appendResults(threadNum + " threads serialize records", new ExecutionResult(serializer.getName(), serialize, bytesSize));
appendResults(threadNum + " threads deserialize records", new ExecutionResult(serializer.getName(), deserialize, bytesSize));
}
private double[] singleFieldBenchmark(Object fieldValue, String fieldType) {
GenericBenchmarkRecord.recordType = fieldType;
GenericBenchmarkRecord benchmarkRecord = new GenericBenchmarkRecord();
benchmarkRecord.put(0, fieldValue);
List<Long> serializeTimes = new ArrayList<>();
List<Long> deserializeTimes = new ArrayList<>();
int bytesSize = 0;
for (int i = 0; i < loop; i++) {
try (OutputStream os = new ByteArrayOutputStream()) {
long startTime = System.nanoTime();
serializer.serialize(benchmarkRecord, os);
long endTime = System.nanoTime();
serializeTimes.add((endTime - startTime)/1000);
byte[] bytes = ((ByteArrayOutputStream) os).toByteArray();
bytesSize = bytes.length;
InputStream is = new ByteArrayInputStream(bytes);
long startTimeTwo = System.nanoTime();
serializer.deserialize(GenericBenchmarkRecord.class, is);
long endTimeTwo = System.nanoTime();
deserializeTimes.add((endTimeTwo - startTimeTwo)/1000);
} catch (IOException e) {
e.printStackTrace();
}
}
return new double[]{aggregateResults(serializeTimes), aggregateResults(deserializeTimes), bytesSize};
}
private double aggregateResults(List<Long> results) {
double sum = 0;
for (Long result : results) {
sum += result;
}
return sum/results.size();
}
private void appendResults(String type, ExecutionResult result) {
if (!run)
return;
if (records.containsKey(type)) {
records.get(type).add(result);
} else {
List<ExecutionResult> resultList = new ArrayList<>();
resultList.add(result);
records.put(type, resultList);
}
}
private void print(ConcurrentHashMap<String, List<ExecutionResult>> result) {
if (!run)
return;
String str = "(" + loop + " loops" + ")";
for (String key : result.keySet()) {
System.out.println(key + str);
List<ExecutionResult> re = result.get(key);
for (ExecutionResult r : re) {
System.out.println("\t\t\t" + r.serializer + " " + ": " + r.time + "(avg) μs/op, " + r.bytesSize + " bytes");
}
}
}
class JsonSerializerBenchmark implements BenchmarkSerializer {
private JsonSerializer jsonSerializer = new JsonSerializer();
@Override
public String getName() {
return "Self JsonSerializer";
}
@Override
public <T extends SpecificRecord> void serialize(T obj, OutputStream os) throws IOException {
jsonSerializer.serialize(obj, os);
}
@Override
public <T extends SpecificRecord> T deserialize(Class<T> clazz, InputStream is) throws IOException {
return jsonSerializer.deserialize(clazz, is);
}
@Override
public void clearCache() {
//jsonSerializer.clearCache();
}
}
class JacksonBenchmark implements BenchmarkSerializer {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public String getName() {
return "Jackson DataBind";
}
@Override
public <T extends SpecificRecord> void serialize(T obj, OutputStream os) throws IOException {
objectMapper.writeValue(os, obj);
}
@Override
public <T extends SpecificRecord> T deserialize(Class<T> clazz, InputStream is) throws IOException {
return objectMapper.readValue(is, clazz);
}
@Override
public void clearCache() {
}
}
class BinaryBenchmark implements BenchmarkSerializer {
private BinarySerializer binarySerializer = new BinarySerializer();
@Override
public String getName() {
return "Self Binary Serializer";
}
@Override
public <T extends SpecificRecord> void serialize(T obj, OutputStream os) throws IOException {
binarySerializer.serialize(obj, os);
}
@Override
public <T extends SpecificRecord> T deserialize(Class<T> clazz, InputStream is) throws IOException {
return binarySerializer.deserialize(clazz, is);
}
@Override
public void clearCache() {
binarySerializer.clearCache();
}
}
interface BenchmarkSerializer {
String getName();
<T extends SpecificRecord> void serialize(T obj, OutputStream os) throws IOException;
<T extends SpecificRecord> T deserialize(Class<T> clazz, InputStream is) throws IOException;
void clearCache();
}
}
class ExecutionResult {
public String serializer;
public double time;
public int bytesSize;
public ExecutionResult(String serializer, double time, int bytesSize) {
this.serializer = serializer;
this.time = time;
this.bytesSize = bytesSize;
}
}