优化了清理算法,提升了性能。简单类型测试中,超出 Fury50% 的性能。读取性能两者相当,JfireSE 稍差 1%。
parent
cc1ac66838
commit
6de117bdce
|
@ -1,7 +1,6 @@
|
|||
package com.jfirer.se2;
|
||||
|
||||
import com.jfirer.baseutil.reflect.ReflectUtil;
|
||||
import com.jfirer.fse.InternalByteArray;
|
||||
import io.github.karlatemp.unsafeaccessor.Unsafe;
|
||||
|
||||
import java.lang.invoke.LambdaMetafactory;
|
||||
|
@ -80,24 +79,11 @@ public class ByteArray
|
|||
writerIndex = array.length;
|
||||
}
|
||||
|
||||
public static com.jfirer.fse.ByteArray allocate(int size)
|
||||
public void resetFor(byte[] bytes)
|
||||
{
|
||||
return new InternalByteArray(size);
|
||||
}
|
||||
|
||||
public static com.jfirer.fse.ByteArray allocate()
|
||||
{
|
||||
return new InternalByteArray(1024);
|
||||
}
|
||||
|
||||
public static com.jfirer.fse.ByteArray wrap(byte[] array)
|
||||
{
|
||||
return new InternalByteArray(array);
|
||||
}
|
||||
|
||||
public void setNeedCheck(boolean needCheck)
|
||||
{
|
||||
this.needCheck = needCheck;
|
||||
this.array = bytes;
|
||||
readerIndex = 0;
|
||||
writerIndex = array.length;
|
||||
}
|
||||
|
||||
protected void ensureNewWriterIndex(int newWriterIndex)
|
||||
|
@ -150,11 +136,6 @@ public class ByteArray
|
|||
return result;
|
||||
}
|
||||
|
||||
public int getWriterIndex()
|
||||
{
|
||||
return writerIndex;
|
||||
}
|
||||
|
||||
public void setWriterIndex(int writerIndex)
|
||||
{
|
||||
this.writerIndex = writerIndex;
|
||||
|
@ -985,10 +966,15 @@ public class ByteArray
|
|||
|
||||
public void writeString(String value)
|
||||
{
|
||||
byte[] bytes = (byte[]) UNSAFE.getReference(value, STRING_VALUE_FIELD_OFFSET);
|
||||
byte coder = UNSAFE.getByte(value, STRING_CODER_FIELD_OFFSET);
|
||||
int idx = writerIndex;
|
||||
int numBytes = bytes.length;
|
||||
byte[] bytes = (byte[]) UNSAFE.getReference(value, STRING_VALUE_FIELD_OFFSET);
|
||||
byte coder = UNSAFE.getByte(value, STRING_CODER_FIELD_OFFSET);
|
||||
writeString(bytes, coder);
|
||||
}
|
||||
|
||||
public void writeString(byte[] bytes, byte coder)
|
||||
{
|
||||
int idx = writerIndex;
|
||||
int numBytes = bytes.length;
|
||||
ensureNewWriterIndex(idx + 9 + numBytes);
|
||||
UNSAFE.putByte(array, BYTE_ARRAY_OFFSET + idx, coder);
|
||||
writerIndex = idx + 1;
|
||||
|
|
|
@ -36,10 +36,11 @@ public interface JfireSE
|
|||
|
||||
ClassInfo getOrCreateClassInfo(Class<?> clazz);
|
||||
|
||||
ClassInfo find(byte[] classNameBytes, int classId);
|
||||
ClassInfo find(String className, int classId);
|
||||
|
||||
ClassInfo find(int classId);
|
||||
|
||||
Object readByUnderInstanceIdFlag(ByteArray byteArray, byte flag);
|
||||
|
||||
void addCleanClassInfo(ClassInfo classInfo);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@ import com.jfirer.se2.classinfo.StaticClasInfo;
|
|||
import com.jfirer.se2.serializer.Serializer;
|
||||
import com.jfirer.se2.serializer.SerializerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JfireSEImpl implements JfireSE
|
||||
|
@ -28,7 +29,8 @@ public class JfireSEImpl implements JfireSE
|
|||
private Map<Class<?>, ClassInfo> classInfoMap = new HashMap<>();
|
||||
private SerializerFactory serializerFactory = new SerializerFactory(this);
|
||||
private Map<byte[], ClassInfo> classInfoCacheMap = new HashMap<>();
|
||||
private Map<byte[], Class<?>> classNameBytesCache = new HashMap<>();
|
||||
private Map<String, Class<?>> classNameBytesCache = new HashMap<>();
|
||||
private List<ClassInfo> cleanClassInfos = new ArrayList<>();
|
||||
|
||||
public JfireSEImpl(boolean refTracking, StaticClasInfo[] staticClasInfos)
|
||||
{
|
||||
|
@ -42,6 +44,7 @@ public class JfireSEImpl implements JfireSE
|
|||
for (StaticClasInfo each : staticClasInfos)
|
||||
{
|
||||
each.setSerializer(serializerFactory.getSerializer(each.getClazz()));
|
||||
each.setJfireSE(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +68,7 @@ public class JfireSEImpl implements JfireSE
|
|||
serializedClassInfos = tmp;
|
||||
}
|
||||
DynamicClassInfo dynamicClassInfo = new DynamicClassInfo((short) dynamicClassId, clazz, refTracking);
|
||||
dynamicClassInfo.setJfireSE(this);
|
||||
serializedClassInfos[dynamicClassId] = dynamicClassInfo;
|
||||
dynamicClassId++;
|
||||
Serializer serializer = serializerFactory.getSerializer(clazz);
|
||||
|
@ -75,15 +79,17 @@ public class JfireSEImpl implements JfireSE
|
|||
|
||||
private void resetSerialized()
|
||||
{
|
||||
for (int i = staticClassId; i < dynamicClassId; i++)
|
||||
for (ClassInfo each : cleanClassInfos)
|
||||
{
|
||||
serializedClassInfos[i].reset();
|
||||
each.reset();
|
||||
}
|
||||
cleanClassInfos.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Object instance)
|
||||
{
|
||||
byteArray.clear();
|
||||
if (instance == null)
|
||||
{
|
||||
byteArray.put(JfireSE.NULL);
|
||||
|
@ -94,7 +100,6 @@ public class JfireSEImpl implements JfireSE
|
|||
ClassInfo classInfo = getOrCreateClassInfo(instance.getClass());
|
||||
classInfo.write(byteArray, instance);
|
||||
byte[] array = byteArray.toArray();
|
||||
byteArray.clear();
|
||||
resetSerialized();
|
||||
return array;
|
||||
}
|
||||
|
@ -102,57 +107,62 @@ public class JfireSEImpl implements JfireSE
|
|||
@Override
|
||||
public Object deSerialize(byte[] bytes)
|
||||
{
|
||||
ByteArray stream = new ByteArray(bytes);
|
||||
byte b = stream.get();
|
||||
byteArray.resetFor(bytes);
|
||||
byte b = byteArray.get();
|
||||
if (b == JfireSE.NULL)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
switch (b)
|
||||
Object result = switch (b)
|
||||
{
|
||||
case JfireSE.NAME_ID_CONTENT_TRACK ->
|
||||
{
|
||||
byte[] classNameBytes = stream.readBytesWithSizeEmbedded();
|
||||
int classId = stream.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(classNameBytes, classId);
|
||||
return classInfo.readWithTrack(stream);
|
||||
String name = byteArray.readString();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(name, classId);
|
||||
yield classInfo.readWithTrack(byteArray);
|
||||
}
|
||||
case JfireSE.NAME_ID_CONTENT_UN_TRACK ->
|
||||
{
|
||||
byte[] classNameBytes = stream.readBytesWithSizeEmbedded();
|
||||
int classId = stream.readPositiveVarInt();
|
||||
return find(classNameBytes, classId).readWithoutTrack(stream);
|
||||
String className = byteArray.readString();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
yield find(className, classId).readWithoutTrack(byteArray);
|
||||
}
|
||||
case JfireSE.ID_CONTENT_TRACK ->
|
||||
{
|
||||
int classId = stream.readPositiveVarInt();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(classId);
|
||||
return classInfo.readWithTrack(stream);
|
||||
yield classInfo.readWithTrack(byteArray);
|
||||
}
|
||||
case JfireSE.ID_CONTENT_UN_TRACK ->
|
||||
{
|
||||
int classId = stream.readPositiveVarInt();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(classId);
|
||||
return classInfo.readWithoutTrack(stream);
|
||||
yield classInfo.readWithoutTrack(byteArray);
|
||||
}
|
||||
default -> throw new RuntimeException("未知的序列化类型");
|
||||
}
|
||||
};
|
||||
resetSerialized();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassInfo find(byte[] classNameBytes, int classId)
|
||||
public ClassInfo find(String className, int classId)
|
||||
{
|
||||
Class<?> clazz = classNameBytesCache.computeIfAbsent(classNameBytes, bytes -> {
|
||||
Class<?> aClass = classNameBytesCache.get(className);
|
||||
if (aClass == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Class.forName(new String(classNameBytes, StandardCharsets.UTF_8));
|
||||
aClass = Class.forName(className);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
ClassInfo classInfo = getOrCreateClassInfo(clazz);
|
||||
classNameBytesCache.put(className, aClass);
|
||||
}
|
||||
ClassInfo classInfo = getOrCreateClassInfo(aClass);
|
||||
if (deSerializedClassInfos == null)
|
||||
{
|
||||
deSerializedClassInfos = new ClassInfo[classId + 1];
|
||||
|
@ -187,16 +197,16 @@ public class JfireSEImpl implements JfireSE
|
|||
{
|
||||
case JfireSE.NAME_ID_CONTENT_TRACK ->
|
||||
{
|
||||
byte[] classNameBytes = byteArray.readBytesWithSizeEmbedded();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(classNameBytes, classId);
|
||||
String className = byteArray.readString();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
ClassInfo classInfo = find(className, classId);
|
||||
return refTracking ? classInfo.readWithTrack(byteArray) : classInfo.readWithoutTrack(byteArray);
|
||||
}
|
||||
case JfireSE.NAME_ID_CONTENT_UN_TRACK ->
|
||||
{
|
||||
byte[] classNameBytes = byteArray.readBytesWithSizeEmbedded();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
return find(classNameBytes, classId).readWithoutTrack(byteArray);
|
||||
String className = byteArray.readString();
|
||||
int classId = byteArray.readPositiveVarInt();
|
||||
return find(className, classId).readWithoutTrack(byteArray);
|
||||
}
|
||||
case JfireSE.ID_INSTANCE_ID ->
|
||||
{
|
||||
|
@ -215,4 +225,10 @@ public class JfireSEImpl implements JfireSE
|
|||
default -> throw new RuntimeException("未知的序列化类型");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCleanClassInfo(ClassInfo classInfo)
|
||||
{
|
||||
cleanClassInfos.add(classInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,26 +7,31 @@ import io.github.karlatemp.unsafeaccessor.Unsafe;
|
|||
import lombok.Data;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Data
|
||||
public abstract class ClassInfo implements RefTracking
|
||||
{
|
||||
protected final short classId;
|
||||
protected final byte[] classNameBytes;
|
||||
protected final byte[] classNameStringBytes;
|
||||
protected final byte classNameStringCoder;
|
||||
protected final Class<?> clazz;
|
||||
protected final boolean refTrack;
|
||||
protected Object[] tracking;
|
||||
protected int refTrackingIndex = 0;
|
||||
protected Serializer serializer;
|
||||
protected JfireSE jfireSE;
|
||||
protected boolean firstSerialized = true;
|
||||
protected static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
public ClassInfo(short classId, Class<?> clazz, boolean refTrack)
|
||||
{
|
||||
this.classId = classId;
|
||||
this.clazz = clazz;
|
||||
this.refTrack = refTrack;
|
||||
classNameBytes = clazz.getName().getBytes(StandardCharsets.UTF_8);
|
||||
this.classId = classId;
|
||||
this.clazz = clazz;
|
||||
this.refTrack = refTrack;
|
||||
classNameBytes = clazz.getName().getBytes(StandardCharsets.UTF_8);
|
||||
classNameStringBytes = (byte[]) UNSAFE.getReference(clazz.getName(), ByteArray.STRING_VALUE_FIELD_OFFSET);
|
||||
classNameStringCoder = UNSAFE.getByte(clazz.getName(), ByteArray.STRING_CODER_FIELD_OFFSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,9 +62,13 @@ public abstract class ClassInfo implements RefTracking
|
|||
{
|
||||
if (refTrackingIndex != 0)
|
||||
{
|
||||
Arrays.fill(tracking, 0, refTrackingIndex, null);
|
||||
for (int i = 0; i < refTrackingIndex; i++)
|
||||
{
|
||||
tracking[i] = null;
|
||||
}
|
||||
refTrackingIndex = 0;
|
||||
}
|
||||
firstSerialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +117,13 @@ public abstract class ClassInfo implements RefTracking
|
|||
*/
|
||||
public Object readWithTrack(ByteArray byteArray)
|
||||
{
|
||||
return serializer.read(byteArray, this);
|
||||
Object result = serializer.read(byteArray, this);
|
||||
if (firstSerialized)
|
||||
{
|
||||
firstSerialized = false;
|
||||
jfireSE.addCleanClassInfo(this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,35 +14,30 @@ public class DynamicClassInfo extends ClassInfo
|
|||
/**
|
||||
* 首次输出的情况下需要输出类名
|
||||
*/
|
||||
private boolean firstSerialized = true;
|
||||
|
||||
public DynamicClassInfo(short classId, Class<?> clazz, boolean refTracking)
|
||||
{
|
||||
super(classId, clazz, refTracking);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
super.reset();
|
||||
firstSerialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteArray byteArray, Object instance)
|
||||
{
|
||||
if (refTrack)
|
||||
if (firstSerialized)
|
||||
{
|
||||
if (firstSerialized)
|
||||
if (refTrack)
|
||||
{
|
||||
firstSerialized = false;
|
||||
addTracking(instance);
|
||||
byteArray.put(JfireSE.NAME_ID_CONTENT_TRACK);
|
||||
byteArray.writeBytesWithSizeEmbedded(classNameBytes);
|
||||
byteArray.writePositiveVarInt(classId);
|
||||
serializer.writeBytes(byteArray, instance);
|
||||
}
|
||||
else
|
||||
firstSerialized = false;
|
||||
byteArray.put(JfireSE.NAME_ID_CONTENT_TRACK);
|
||||
byteArray.writeString(classNameStringBytes, classNameStringCoder);
|
||||
byteArray.writePositiveVarInt(classId);
|
||||
serializer.writeBytes(byteArray, instance);
|
||||
jfireSE.addCleanClassInfo(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (refTrack)
|
||||
{
|
||||
int i = addTracking(instance);
|
||||
if (i == -1)
|
||||
|
@ -58,17 +53,6 @@ public class DynamicClassInfo extends ClassInfo
|
|||
byteArray.writePositiveVarInt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (firstSerialized)
|
||||
{
|
||||
firstSerialized = false;
|
||||
byteArray.put(JfireSE.NAME_ID_CONTENT_UN_TRACK);
|
||||
byteArray.writeBytesWithSizeEmbedded(classNameBytes);
|
||||
byteArray.writePositiveVarInt(classId);
|
||||
serializer.writeBytes(byteArray, instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
byteArray.put(JfireSE.ID_CONTENT_UN_TRACK);
|
||||
|
|
|
@ -28,6 +28,11 @@ public class StaticClasInfo extends ClassInfo
|
|||
byteArray.writePositiveVarInt(classId);
|
||||
byteArray.writePositiveVarInt(i);
|
||||
}
|
||||
if (firstSerialized)
|
||||
{
|
||||
firstSerialized = false;
|
||||
jfireSE.addCleanClassInfo(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package org.example;
|
||||
|
||||
import com.jfirer.se2.JfireSE;
|
||||
import org.apache.fury.Fury;
|
||||
import org.apache.fury.config.Language;
|
||||
import org.example.sm.TestDataSm;
|
||||
import org.example.sm2.TestDataSm2;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@Warmup(iterations = 2, time = 3)
|
||||
@Measurement(iterations = 3, time = 3)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@Fork(1)
|
||||
@State(Scope.Benchmark)
|
||||
public class BenchMarkRead
|
||||
{
|
||||
Fury fury = Fury.builder().withLanguage(Language.JAVA)//
|
||||
.requireClassRegistration(false)//
|
||||
.withRefTracking(true).build();
|
||||
JfireSE jfireSE = JfireSE.supportRefTracking(true).build();
|
||||
TestData data = new TestData().setTestDataSm(new TestDataSm()).setTestDataSm2(new TestDataSm2());
|
||||
byte[] serialize = jfireSE.serialize(data);
|
||||
byte[] serialize2 = fury.serialize(data);
|
||||
|
||||
@Benchmark
|
||||
public void testJfireSERead()
|
||||
{
|
||||
jfireSE.deSerialize(serialize);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testFuryRead()
|
||||
{
|
||||
fury.deserialize(serialize2);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException
|
||||
{
|
||||
Options opt = new OptionsBuilder().include(BenchMarkRead.class.getSimpleName()).build();
|
||||
new Runner(opt).run();
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit;
|
|||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@Fork(1)
|
||||
@State(Scope.Benchmark)
|
||||
public class BenchMark
|
||||
public class BenchMarkWrite
|
||||
{
|
||||
Fse fse = new Fse();
|
||||
Fse fse_3 = new Fse().useCompile();
|
||||
|
@ -32,7 +32,6 @@ public class BenchMark
|
|||
.withRefTracking(true).build();
|
||||
JfireSE jfireSE = JfireSE.supportRefTracking(true).build();
|
||||
|
||||
@Benchmark
|
||||
public void testFSENoCompile()
|
||||
{
|
||||
buf.clear();
|
||||
|
@ -45,7 +44,6 @@ public class BenchMark
|
|||
byte[] bytes = fury.serialize(data);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testFSEDirectCompile()
|
||||
{
|
||||
buf.clear();
|
||||
|
@ -60,7 +58,7 @@ public class BenchMark
|
|||
|
||||
public static void main(String[] args) throws RunnerException
|
||||
{
|
||||
Options opt = new OptionsBuilder().include(BenchMark.class.getSimpleName()).build();
|
||||
Options opt = new OptionsBuilder().include(BenchMarkWrite.class.getSimpleName()).build();
|
||||
new Runner(opt).run();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.example;
|
||||
|
||||
import com.jfirer.se2.JfireSE;
|
||||
import org.example.sm.TestDataSm;
|
||||
import org.example.sm2.TestDataSm2;
|
||||
import org.junit.Test;
|
||||
|
||||
public class Profile
|
||||
{
|
||||
JfireSE jfireSE = JfireSE.supportRefTracking(true).build();
|
||||
TestData data = new TestData().setTestDataSm(new TestDataSm()).setTestDataSm2(new TestDataSm2());
|
||||
byte[] serialize = jfireSE.serialize(data);
|
||||
|
||||
@Test
|
||||
public void test()
|
||||
{
|
||||
for (int i = 0; i < 10000000; i++)
|
||||
{
|
||||
jfireSE.deSerialize(serialize);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package org.example.festest;
|
||||
|
||||
import com.jfirer.fse.ByteArray;
|
||||
import com.jfirer.se2.JfireSE;
|
||||
import org.apache.fury.Fury;
|
||||
import org.apache.fury.config.Language;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@Warmup(iterations = 2, time = 1)
|
||||
@Measurement(iterations = 3, time = 3)
|
||||
@Threads(1)
|
||||
@Fork(2)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
public class BenchMark
|
||||
{
|
||||
JfireSE jfireSE = JfireSE.supportRefTracking(true).build();
|
||||
TestData data = new TestData();
|
||||
ByteArray buf = ByteArray.allocate(100);
|
||||
Fury fury = Fury.builder().withLanguage(Language.JAVA)//
|
||||
.requireClassRegistration(false)//
|
||||
.withRefTracking(true).build();
|
||||
|
||||
@Benchmark
|
||||
public void testFury()
|
||||
{
|
||||
byte[] bytes = fury.serialize(data);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testJfireSE()
|
||||
{
|
||||
jfireSE.serialize(data);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException
|
||||
{
|
||||
Options opt = new OptionsBuilder().include(BenchMark.class.getSimpleName()).build();
|
||||
new Runner(opt).run();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue